From dacce3e7bdd645a75a2b699125d8ed105f557a5e Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 16 Oct 2016 23:51:09 +0200 Subject: [PATCH] Fixes #2470: Implement rendering of the crafting output for encoded patterns when shift is being held. --- .../ItemEncodedPatternBakedModel.java | 233 ++++++++++++++++++ .../crafting/ItemEncodedPatternModel.java | 67 +++++ .../crafting/ItemEncodedPatternRendering.java | 27 ++ .../appeng/core/api/definitions/ApiItems.java | 6 +- 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 src/main/java/appeng/client/render/crafting/ItemEncodedPatternBakedModel.java create mode 100644 src/main/java/appeng/client/render/crafting/ItemEncodedPatternModel.java create mode 100644 src/main/java/appeng/client/render/crafting/ItemEncodedPatternRendering.java diff --git a/src/main/java/appeng/client/render/crafting/ItemEncodedPatternBakedModel.java b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternBakedModel.java new file mode 100644 index 00000000..5309d686 --- /dev/null +++ b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternBakedModel.java @@ -0,0 +1,233 @@ +package appeng.client.render.crafting; + + +import java.util.List; +import javax.annotation.Nullable; +import javax.vecmath.Matrix4f; + +import com.google.common.collect.ImmutableMap; + +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.input.Keyboard; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; +import net.minecraftforge.client.model.IPerspectiveAwareModel; +import net.minecraftforge.common.model.TRSRTransformation; + +import appeng.items.misc.ItemEncodedPattern; + + +/** + * This special model handles switching between rendering the crafting output of an encoded pattern (when shift is being held), and + * showing the encoded pattern itself. Matters are further complicated by only wanting to show the crafting output when the pattern is being + * rendered in the GUI, and not anywhere else. + */ +class ItemEncodedPatternBakedModel implements IPerspectiveAwareModel +{ + private final IBakedModel baseModel; + + private final ImmutableMap transforms; + + private final CustomOverrideList overrides; + + ItemEncodedPatternBakedModel( IBakedModel baseModel, ImmutableMap transforms ) + { + this.baseModel = baseModel; + this.transforms = transforms; + this.overrides = new CustomOverrideList(); + } + + @Override + public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) + { + return baseModel.getQuads( state, side, rand ); + } + + @Override + public boolean isAmbientOcclusion() + { + return baseModel.isAmbientOcclusion(); + } + + @Override + public boolean isGui3d() + { + return baseModel.isGui3d(); + } + + @Override + public boolean isBuiltInRenderer() + { + return baseModel.isBuiltInRenderer(); + } + + @Override + public TextureAtlasSprite getParticleTexture() + { + return baseModel.getParticleTexture(); + } + + @Override + @Deprecated + public ItemCameraTransforms getItemCameraTransforms() + { + return baseModel.getItemCameraTransforms(); + } + + @Override + public ItemOverrideList getOverrides() + { + return overrides; + } + + @Override + public Pair handlePerspective( ItemCameraTransforms.TransformType cameraTransformType ) + { + if( baseModel instanceof IPerspectiveAwareModel ) + { + return ( (IPerspectiveAwareModel) baseModel ).handlePerspective( cameraTransformType ); + } + + return IPerspectiveAwareModel.MapWrapper.handlePerspective( this, transforms, cameraTransformType ); + } + + /** + * Since the ItemOverrideList handling comes before handling the perspective awareness (which is the first place where we + * know how we are being rendered) we need to remember the model of the crafting output, and make the decision on which to render later on. + * Sadly, Forge is pretty inconsistent when it will call the handlePerspective method, so some methods are called even on this interim-model. + * Usually those methods only matter for rendering on the ground and other cases, where we wouldn't render the crafting output model anyway, + * so in those cases we delegate to the model of the encoded pattern. + */ + private class ShiftHoldingModelWrapper implements IPerspectiveAwareModel + { + + private final IBakedModel outputModel; + + private ShiftHoldingModelWrapper( IBakedModel outputModel ) + { + this.outputModel = outputModel; + } + + @Override + public Pair handlePerspective( ItemCameraTransforms.TransformType cameraTransformType ) + { + final IBakedModel selectedModel; + + // No need to re-check for shift being held since this model is only handed out in that case + if( cameraTransformType == ItemCameraTransforms.TransformType.GUI ) + { + selectedModel = outputModel; + } + else + { + selectedModel = baseModel; + } + + // Now retroactively handle the isGui3d call, for which we always return false below + if( selectedModel.isGui3d() != baseModel.isGui3d() ) + { + GlStateManager.enableLighting(); + } + + if( selectedModel instanceof IPerspectiveAwareModel ) + { + return ( (IPerspectiveAwareModel) selectedModel ).handlePerspective( cameraTransformType ); + } + + return IPerspectiveAwareModel.MapWrapper.handlePerspective( this, transforms, cameraTransformType ); + } + + @Override + public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) + { + // This may be called for items on the ground, in which case we will always fall back to the pattern + return baseModel.getQuads( state, side, rand ); + } + + @Override + public boolean isAmbientOcclusion() + { + return baseModel.isAmbientOcclusion(); + } + + @Override + public boolean isGui3d() + { + // NOTE: Sadly, Forge will let Minecraft call this method before handling the perspective awareness + return baseModel.isGui3d(); + } + + @Override + public boolean isBuiltInRenderer() + { + // This may be called for items on the ground, in which case we will always fall back to the pattern + return baseModel.isBuiltInRenderer(); + } + + @Override + public TextureAtlasSprite getParticleTexture() + { + // This may be called for items on the ground, in which case we will always fall back to the pattern + return baseModel.getParticleTexture(); + } + + @Override + public ItemCameraTransforms getItemCameraTransforms() + { + // This may be called for items on the ground, in which case we will always fall back to the pattern + return baseModel.getItemCameraTransforms(); + } + + @Override + public ItemOverrideList getOverrides() + { + // This may be called for items on the ground, in which case we will always fall back to the pattern + return baseModel.getOverrides(); + } + } + + + /** + * Item Override Lists are the only point during item rendering where we can access the item stack that is being rendered. + * So this is the point where we actually check if shift is being held, and if so, determine the crafting output model. + */ + private class CustomOverrideList extends ItemOverrideList + { + + CustomOverrideList() + { + super( baseModel.getOverrides().getOverrides() ); + } + + @Override + public IBakedModel handleItemState( IBakedModel originalModel, ItemStack stack, World world, EntityLivingBase entity ) + { + boolean shiftHeld = Keyboard.isKeyDown( Keyboard.KEY_LSHIFT ) || Keyboard.isKeyDown( Keyboard.KEY_RSHIFT ); + if( shiftHeld ) + { + ItemEncodedPattern iep = (ItemEncodedPattern) stack.getItem(); + ItemStack output = iep.getOutput( stack ); + if( output != null ) + { + IBakedModel realModel = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getItemModel( output ); + // Give the item model a chance to handle the overrides as well + realModel = realModel.getOverrides().handleItemState( realModel, output, world, entity ); + return new ShiftHoldingModelWrapper( realModel ); + } + } + + return baseModel.getOverrides().handleItemState( originalModel, stack, world, entity ); + } + } +} diff --git a/src/main/java/appeng/client/render/crafting/ItemEncodedPatternModel.java b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternModel.java new file mode 100644 index 00000000..f316c7d7 --- /dev/null +++ b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternModel.java @@ -0,0 +1,67 @@ +package appeng.client.render.crafting; + + +import java.util.Collection; +import java.util.Collections; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; + +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.IPerspectiveAwareModel; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.common.model.IModelState; +import net.minecraftforge.common.model.TRSRTransformation; + +import appeng.core.AppEng; + + +/** + * Simple model for the encoded pattern built-in baked model. + */ +class ItemEncodedPatternModel implements IModel +{ + + private static final ResourceLocation BASE_MODEL = new ResourceLocation( AppEng.MOD_ID, "item/encoded_pattern" ); + + @Override + public Collection getDependencies() + { + return Collections.singletonList( BASE_MODEL ); + } + + @Override + public Collection getTextures() + { + return Collections.emptyList(); + } + + @Override + public IBakedModel bake( IModelState state, VertexFormat format, Function bakedTextureGetter ) + { + IBakedModel baseModel; + try + { + baseModel = ModelLoaderRegistry.getModel( BASE_MODEL ).bake( state, format, bakedTextureGetter ); + } + catch( Exception e ) + { + throw new RuntimeException( e ); + } + + ImmutableMap transforms = IPerspectiveAwareModel.MapWrapper.getTransforms(state); + + return new ItemEncodedPatternBakedModel( baseModel, transforms ); + } + + @Override + public IModelState getDefaultState() + { + return TRSRTransformation.identity(); + } +} diff --git a/src/main/java/appeng/client/render/crafting/ItemEncodedPatternRendering.java b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternRendering.java new file mode 100644 index 00000000..b1aeb515 --- /dev/null +++ b/src/main/java/appeng/client/render/crafting/ItemEncodedPatternRendering.java @@ -0,0 +1,27 @@ +package appeng.client.render.crafting; + + +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import appeng.bootstrap.IItemRendering; +import appeng.bootstrap.ItemRenderingCustomizer; +import appeng.core.AppEng; + + +public class ItemEncodedPatternRendering extends ItemRenderingCustomizer +{ + + private static final ResourceLocation MODEL = new ResourceLocation( AppEng.MOD_ID, "builtin/encoded_pattern" ); + + @Override + @SideOnly( Side.CLIENT ) + public void customize( IItemRendering rendering ) + { + rendering.builtInModel( "models/item/builtin/encoded_pattern", new ItemEncodedPatternModel() ); + rendering.model( new ModelResourceLocation( MODEL, "inventory" ) ).variants( MODEL ); + } + +} diff --git a/src/main/java/appeng/core/api/definitions/ApiItems.java b/src/main/java/appeng/core/api/definitions/ApiItems.java index 39c9b499..5e778883 100644 --- a/src/main/java/appeng/core/api/definitions/ApiItems.java +++ b/src/main/java/appeng/core/api/definitions/ApiItems.java @@ -23,6 +23,7 @@ import appeng.api.definitions.IItemDefinition; import appeng.api.definitions.IItems; import appeng.api.util.AEColoredItemDefinition; import appeng.bootstrap.FeatureFactory; +import appeng.client.render.crafting.ItemEncodedPatternRendering; import appeng.core.CreativeTabFacade; import appeng.core.features.AEFeature; import appeng.debug.ToolDebugCard; @@ -190,7 +191,10 @@ public final class ApiItems implements IItems .build(); // rv1 - this.encodedPattern = registry.item( "encoded_pattern", ItemEncodedPattern::new ).features( AEFeature.Patterns ).build(); + this.encodedPattern = registry.item( "encoded_pattern", ItemEncodedPattern::new ) + .features( AEFeature.Patterns ) + .rendering( new ItemEncodedPatternRendering() ) + .build(); this.paintBall = registry.item( "paint_ball", ItemPaintBall::new ) .features( AEFeature.PaintBalls )