From ce3d8b40cf37924caf1708cdde6842ae6fdcee31 Mon Sep 17 00:00:00 2001 From: David Quintana Date: Thu, 2 Jul 2020 17:05:20 +0200 Subject: [PATCH] Model system improvements: - Port some things I did in 1.14 which I couldn't do in 1.15 due to breaking changes. - Fix multi-layer block models not working (1.16 RenderType doesn't override toString the same way anymore) - Implement multi-layer item rendering. - Improve CompositeModel submodel data passing. --- .../client/renderer/ItemRenderer.java.patch | 26 ++- .../client/ForgeHooksClient.java | 13 ++ .../client/ForgeRenderTypes.java | 142 ++++++++++++ .../client/extensions/IForgeBakedModel.java | 22 ++ .../extensions/IForgeVertexBuilder.java | 8 +- .../client/model/CompositeModel.java | 168 +++++++++++--- .../client/model/DynamicBucketModel.java | 183 ++++++--------- .../client/model/FluidModel.java | 1 + .../client/model/IModelConfiguration.java | 1 - .../client/model/ItemLayerModel.java | 37 +-- .../model/ItemMultiLayerBakedModel.java | 211 ++++++++++++++++++ .../model/ItemTextureQuadConverter.java | 61 +++-- .../client/model/MultiLayerModel.java | 66 +++++- .../client/model/data/IDynamicBakedModel.java | 2 +- .../client/model/data/ModelDataMap.java | 7 +- .../resources/META-INF/accesstransformer.cfg | 1 + .../models/block/test_layer_block.json | 2 +- 17 files changed, 749 insertions(+), 202 deletions(-) create mode 100644 src/main/java/net/minecraftforge/client/ForgeRenderTypes.java create mode 100644 src/main/java/net/minecraftforge/client/model/ItemMultiLayerBakedModel.java diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch index 2d3242a8b..9547ba984 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch @@ -18,16 +18,30 @@ p_229111_4_.func_227861_a_(-0.5D, -0.5D, -0.5D); if (!p_229111_8_.func_188618_c() && (p_229111_1_.func_77973_b() != Items.field_203184_eO || flag)) { boolean flag1; -@@ -132,7 +132,7 @@ - +@@ -105,7 +105,8 @@ + } else { + flag1 = true; + } +- ++ if (p_229111_8_.isLayered()) { net.minecraftforge.client.ForgeHooksClient.drawItemLayered(this, p_229111_8_, p_229111_1_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_, flag1); } ++ else { + RenderType rendertype = RenderTypeLookup.func_239219_a_(p_229111_1_, flag1); + IVertexBuilder ivertexbuilder; + if (p_229111_1_.func_77973_b() == Items.field_151111_aL && p_229111_1_.func_77962_s()) { +@@ -129,10 +130,10 @@ + } else { + ivertexbuilder = func_229113_a_(p_229111_5_, rendertype, true, p_229111_1_.func_77962_s()); + } +- this.func_229114_a_(p_229111_8_, p_229111_1_, p_229111_6_, p_229111_7_, p_229111_4_, ivertexbuilder); ++ } } else { - ItemStackTileEntityRenderer.field_147719_a.func_239207_a_(p_229111_1_, p_229111_2_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_); + p_229111_1_.func_77973_b().getItemStackTileEntityRenderer().func_239207_a_(p_229111_1_, p_229111_2_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_); } p_229111_4_.func_227865_b_(); -@@ -172,7 +172,7 @@ +@@ -172,7 +173,7 @@ float f = (float)(i >> 16 & 255) / 255.0F; float f1 = (float)(i >> 8 & 255) / 255.0F; float f2 = (float)(i & 255) / 255.0F; @@ -36,7 +50,7 @@ } } -@@ -263,6 +263,7 @@ +@@ -263,6 +264,7 @@ crashreportcategory.func_189529_a("Item Type", () -> { return String.valueOf((Object)p_239387_2_.func_77973_b()); }); @@ -44,7 +58,7 @@ crashreportcategory.func_189529_a("Item Damage", () -> { return String.valueOf(p_239387_2_.func_77952_i()); }); -@@ -294,18 +295,16 @@ +@@ -294,18 +296,16 @@ irendertypebuffer$impl.func_228461_a_(); } @@ -67,7 +81,7 @@ this.func_181565_a(bufferbuilder, p_180453_3_ + 2, p_180453_4_ + 13, 13, 2, 0, 0, 0, 255); this.func_181565_a(bufferbuilder, p_180453_3_ + 2, p_180453_4_ + 13, i, 1, j >> 16 & 255, j >> 8 & 255, j & 255, 255); RenderSystem.enableBlend(); -@@ -343,4 +342,9 @@ +@@ -343,4 +343,9 @@ public void func_195410_a(IResourceManager p_195410_1_) { this.field_175059_m.func_178085_b(); } diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index 5a0354544..874534a20 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -810,4 +810,17 @@ public class ForgeHooksClient MinecraftForge.EVENT_BUS.post(event); return event; } + + public static void drawItemLayered(ItemRenderer renderer, IBakedModel modelIn, ItemStack itemStackIn, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, boolean fabulous) + { + for(com.mojang.datafixers.util.Pair layerModel : modelIn.getLayerModels(itemStackIn, fabulous)) { + modelIn = layerModel.getFirst(); + RenderType rendertype = layerModel.getSecond(); + net.minecraftforge.client.ForgeHooksClient.setRenderLayer(rendertype); // neded for compatibility with MultiLayerModels + + IVertexBuilder ivertexbuilder = ItemRenderer.getBuffer(bufferIn, rendertype, true, itemStackIn.hasEffect()); + renderer.renderModel(modelIn, itemStackIn, combinedLightIn, combinedOverlayIn, matrixStackIn, ivertexbuilder); + } + net.minecraftforge.client.ForgeHooksClient.setRenderLayer(null); + } } diff --git a/src/main/java/net/minecraftforge/client/ForgeRenderTypes.java b/src/main/java/net/minecraftforge/client/ForgeRenderTypes.java new file mode 100644 index 000000000..199a8beb1 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/ForgeRenderTypes.java @@ -0,0 +1,142 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client; + +import net.minecraft.client.renderer.RenderState; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.NonNullLazy; +import net.minecraftforge.common.util.NonNullSupplier; +import org.lwjgl.opengl.GL11; + +public enum ForgeRenderTypes +{ + /** + * A cached copy of {@link ForgeRenderTypes#getUnsortedTranslucent(ResourceLocation)} + * for use in item models and TileEntityRenderers that use the block/item atlas. + */ + ITEM_UNSORTED_TRANSLUCENT(()-> getUnsortedTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE)), + ITEM_UNLIT_TRANSLUCENT(()-> getUnlitTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE)), + ITEM_UNSORTED_UNLIT_TRANSLUCENT(()-> getUnlitTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE, false)); + + /** + * @return A RenderType fit for translucent item/entity rendering, but with depth sorting disabled. + */ + public static RenderType getUnsortedTranslucent(ResourceLocation textureLocation) + { + return Internal.unsortedTranslucent(textureLocation); + } + + /** + * @return A RenderType fit for translucent item/entity rendering, but with diffuse lighting disabled + * so that fullbright quads look correct. + */ + public static RenderType getUnlitTranslucent(ResourceLocation textureLocation) + { + return getUnlitTranslucent(textureLocation, true); + } + + /** + * @return A RenderType fit for translucent item/entity rendering, but with diffuse lighting disabled + * so that fullbright quads look correct. + * @param sortingEnabled If false, depth sorting will not be performed. + */ + public static RenderType getUnlitTranslucent(ResourceLocation textureLocation, boolean sortingEnabled) + { + return Internal.unlitTranslucent(textureLocation, sortingEnabled); + } + + /** + * @return Same as {@link RenderType#getEntityCutout(ResourceLocation)}, but with mipmapping enabled. + */ + public static RenderType getEntityCutoutMipped(ResourceLocation textureLocation) + { + return Internal.entityCutoutMipped(textureLocation); + } + + // ---------------------------------------- + // Implementation details below this line + // ---------------------------------------- + + private final NonNullSupplier renderTypeSupplier; + + ForgeRenderTypes(NonNullSupplier renderTypeSupplier) + { + // Wrap in a Lazy<> to avoid running the supplier more than once. + this.renderTypeSupplier = NonNullLazy.of(renderTypeSupplier); + } + + public RenderType get() + { + return renderTypeSupplier.get(); + } + + private static class Internal extends RenderType + { + private Internal(String name, VertexFormat fmt, int glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) + { + super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); + throw new IllegalStateException("This class must not be instantiated"); + } + + public static RenderType unsortedTranslucent(ResourceLocation textureLocation) + { + final boolean sortingEnabled = false; + State renderState = State.getBuilder() + .texture(new TextureState(textureLocation, false, false)) + .transparency(TRANSLUCENT_TRANSPARENCY) + .diffuseLighting(DIFFUSE_LIGHTING_ENABLED) + .alpha(DEFAULT_ALPHA) + .cull(CULL_DISABLED) + .lightmap(LIGHTMAP_ENABLED) + .overlay(OVERLAY_ENABLED) + .build(true); + return makeType("entity_unsorted_translucent", DefaultVertexFormats.ENTITY, GL11.GL_QUADS, 256, true, sortingEnabled, renderState); + } + + public static RenderType unlitTranslucent(ResourceLocation textureLocation, boolean sortingEnabled) + { + State renderState = State.getBuilder() + .texture(new TextureState(textureLocation, false, false)) + .transparency(TRANSLUCENT_TRANSPARENCY) + .alpha(DEFAULT_ALPHA) + .cull(CULL_DISABLED) + .lightmap(LIGHTMAP_ENABLED) + .overlay(OVERLAY_ENABLED) + .build(true); + return makeType("entity_unlit_translucent", DefaultVertexFormats.ENTITY, GL11.GL_QUADS, 256, true, sortingEnabled, renderState); + } + + public static RenderType entityCutoutMipped(ResourceLocation locationIn) { + RenderType.State rendertype$state = RenderType.State.getBuilder() + .texture(new RenderState.TextureState(locationIn, false, true)) + .transparency(NO_TRANSPARENCY) + .diffuseLighting(DIFFUSE_LIGHTING_ENABLED) + .alpha(DEFAULT_ALPHA) + .lightmap(LIGHTMAP_ENABLED) + .overlay(OVERLAY_ENABLED) + .build(true); + return makeType("entity_cutout_mipped", DefaultVertexFormats.ENTITY, 7, 256, true, false, rendertype$state); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java b/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java index f08d75b73..2f3569ab8 100644 --- a/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java +++ b/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java @@ -19,19 +19,25 @@ package net.minecraftforge.client.extensions; +import java.util.Collections; import java.util.List; import java.util.Random; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.datafixers.util.Pair; import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeLookup; import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.item.ItemStack; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockDisplayReader; @@ -75,4 +81,20 @@ public interface IForgeBakedModel { return getBakedModel().getParticleTexture(); } + + /** + * Override to true, to tell forge to call the getLayerModels method below. + */ + default boolean isLayered() + { + return false; + } + + /** + * If {@see isLayered()} returns true, this is called to get the list of layers to draw. + */ + default List> getLayerModels(ItemStack itemStack, boolean fabulous) + { + return Collections.singletonList(Pair.of(getBakedModel(), RenderTypeLookup.func_239219_a_(itemStack, fabulous))); + } } diff --git a/src/main/java/net/minecraftforge/client/extensions/IForgeVertexBuilder.java b/src/main/java/net/minecraftforge/client/extensions/IForgeVertexBuilder.java index 982c291c9..dc03d0328 100644 --- a/src/main/java/net/minecraftforge/client/extensions/IForgeVertexBuilder.java +++ b/src/main/java/net/minecraftforge/client/extensions/IForgeVertexBuilder.java @@ -38,17 +38,17 @@ public interface IForgeVertexBuilder { default IVertexBuilder getVertexBuilder() { return (IVertexBuilder)this; } - // Copy of func_227889_a_, but enables tinting + // Copy of addQuad, but enables tinting and per-vertex alpha default void addVertexData(MatrixStack.Entry matrixStack, BakedQuad bakedQuad, float red, float green, float blue, int lightmapCoord, int overlayColor, boolean readExistingColor) { - getVertexBuilder().addQuad(matrixStack, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, readExistingColor); + addVertexData(matrixStack, bakedQuad, red, green, blue, 1.0f, lightmapCoord, overlayColor, readExistingColor); } - // Copy of func_227889_a_ with alpha support + // Copy of addQuad with alpha support default void addVertexData(MatrixStack.Entry matrixEntry, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int lightmapCoord, int overlayColor) { addVertexData(matrixEntry, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, alpha, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, false); } - // Copy of func_227889_a_ with alpha support + // Copy of addQuad with alpha support default void addVertexData(MatrixStack.Entry matrixEntry, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int lightmapCoord, int overlayColor, boolean readExistingColor) { addVertexData(matrixEntry, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, alpha, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, readExistingColor); } diff --git a/src/main/java/net/minecraftforge/client/model/CompositeModel.java b/src/main/java/net/minecraftforge/client/model/CompositeModel.java index e25ddc5cb..363a7e0a2 100644 --- a/src/main/java/net/minecraftforge/client/model/CompositeModel.java +++ b/src/main/java/net/minecraftforge/client/model/CompositeModel.java @@ -31,10 +31,9 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.resources.IResourceManager; import net.minecraft.util.Direction; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.client.model.data.EmptyModelData; -import net.minecraftforge.client.model.data.IDynamicBakedModel; -import net.minecraftforge.client.model.data.IModelData; -import net.minecraftforge.client.model.data.ModelProperty; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraftforge.client.model.data.*; import net.minecraftforge.client.model.geometry.IModelGeometryPart; import net.minecraftforge.client.model.geometry.IMultipartModelGeometry; @@ -45,20 +44,20 @@ import java.util.function.Function; public class CompositeModel implements IDynamicBakedModel { - public static final ModelProperty SUBMODEL_DATA = new ModelProperty<>(); - private final ImmutableMap bakedParts; private final boolean isAmbientOcclusion; private final boolean isGui3d; + private final boolean isSideLit; private final TextureAtlasSprite particle; private final ItemOverrideList overrides; private final IModelTransform transforms; - public CompositeModel(boolean isGui3d, boolean isAmbientOcclusion, TextureAtlasSprite particle, ImmutableMap bakedParts, IModelTransform combinedTransform, ItemOverrideList overrides) + public CompositeModel(boolean isGui3d, boolean isSideLit, boolean isAmbientOcclusion, TextureAtlasSprite particle, ImmutableMap bakedParts, IModelTransform combinedTransform, ItemOverrideList overrides) { this.bakedParts = bakedParts; this.isAmbientOcclusion = isAmbientOcclusion; this.isGui3d = isGui3d; + this.isSideLit = isSideLit; this.particle = particle; this.overrides = overrides; this.transforms = combinedTransform; @@ -71,12 +70,23 @@ public class CompositeModel implements IDynamicBakedModel List quads = new ArrayList<>(); for(Map.Entry entry : bakedParts.entrySet()) { - // TODO: Some way to provide submodel data? - quads.addAll(entry.getValue().getQuads(state, side, rand, getSubmodelData(extraData, entry.getKey()))); + quads.addAll(entry.getValue().getQuads(state, side, rand, CompositeModelData.get(extraData, entry.getKey()))); } return quads; } + @Nonnull + @Override + public IModelData getModelData(@Nonnull IBlockDisplayReader world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull IModelData tileData) + { + CompositeModelData composite = new CompositeModelData(); + for(Map.Entry entry : bakedParts.entrySet()) + { + composite.putSubmodelData(entry.getKey(), entry.getValue().getModelData(world, pos, state, ModelDataWrapper.wrap(tileData))); + } + return composite; + } + @Override public boolean isAmbientOcclusion() { @@ -92,8 +102,7 @@ public class CompositeModel implements IDynamicBakedModel @Override public boolean func_230044_c_() { - // TODO: Forge: Auto-generated method stub - return false; + return isSideLit; } @Override @@ -132,29 +141,6 @@ public class CompositeModel implements IDynamicBakedModel return bakedParts.get(name); } - private IModelData getSubmodelData(IModelData extraData, String name) - { - SubmodelModelData data = extraData.getData(SUBMODEL_DATA); - if (data == null) - return EmptyModelData.INSTANCE; - return data.getSubmodelData(name); - } - - public static class SubmodelModelData - { - private final Map parts = new HashMap<>(); - - public IModelData getSubmodelData(String name) - { - return parts.getOrDefault(name, EmptyModelData.INSTANCE); - } - - public void putSubmodelData(String name, IModelData data) - { - parts.put(name, data); - } - } - private static class Submodel implements IModelGeometryPart { private final String name; @@ -228,7 +214,7 @@ public class CompositeModel implements IDynamicBakedModel continue; bakedParts.put(part.getKey(), submodel.bakeModel(bakery, spriteGetter, modelTransform, modelLocation)); } - return new CompositeModel(owner.isShadedInGui(), owner.useSmoothLighting(), particle, bakedParts.build(), owner.getCombinedTransform(), overrides); + return new CompositeModel(owner.isShadedInGui(), owner.useSmoothLighting(), owner.isSideLit(), particle, bakedParts.build(), owner.getCombinedTransform(), overrides); } @Override @@ -273,4 +259,116 @@ public class CompositeModel implements IDynamicBakedModel return new Geometry(parts.build()); } } + + /** + * A model data container which stores data for child components. + */ + public static class CompositeModelData extends ModelDataMap + { + public static final ModelProperty SUBMODEL_DATA = new ModelProperty<>(); + + /** + * Helper to get the CompositeModelData from an unknown IModelData instance. + * @param modelData The undetermined instance to get data from + * @return An optional representing the composite data, if present. + */ + public static Optional get(IModelData modelData) + { + return Optional.ofNullable(modelData.getData(SUBMODEL_DATA)); + } + + /** + * Helper to get child data from an unknown IModelData instance. + * @param modelData The undetermined instance to get data from + * @param name The name of the child part to get data for. + * @return The data for the child, or empty if not available. + */ + public static IModelData get(IModelData modelData, String name) + { + return get(modelData).map(data -> data.getSubmodelData(name)) + .orElse(EmptyModelData.INSTANCE); + } + + // Implementation + + private final Map parts = new HashMap<>(); + + public IModelData getSubmodelData(String name) + { + if (parts.containsKey(name)) + return parts.get(name); + return EmptyModelData.INSTANCE; + } + + public void putSubmodelData(String name, IModelData data) + { + parts.put(name, data); + } + + @Override + public boolean hasProperty(ModelProperty prop) + { + return prop == SUBMODEL_DATA ||super.hasProperty(prop); + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getData(ModelProperty prop) + { + if (prop == SUBMODEL_DATA) + return (T)this; + return super.getData(prop); + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T setData(ModelProperty prop, T data) + { + if (prop == SUBMODEL_DATA) + return (T)this; + return super.setData(prop, data); + } + } + + /** + * Wrapper for an IModelData instance which allows forwarding queries to the parent, + * but stores any new/modified values itself, avoiding modifications to the parent. + */ + private static class ModelDataWrapper extends ModelDataMap + { + private final IModelData parent; + + public static IModelData wrap(IModelData parent) + { + return new ModelDataWrapper(parent); + } + + private ModelDataWrapper(IModelData parent) + { + this.parent = parent; + } + + @Override + public boolean hasProperty(ModelProperty prop) + { + return super.hasProperty(prop) || parent.hasProperty(prop); + } + + @Nullable + @Override + public T getData(ModelProperty prop) + { + return super.hasProperty(prop) ? super.getData(prop) : parent.getData(prop); + } + + @Nullable + @Override + public T setData(ModelProperty prop, T data) + { + // We do not want to delegate setting to the parent + return super.setData(prop, data); + } + } } diff --git a/src/main/java/net/minecraftforge/client/model/DynamicBucketModel.java b/src/main/java/net/minecraftforge/client/model/DynamicBucketModel.java index c9424574c..f5a43d47f 100644 --- a/src/main/java/net/minecraftforge/client/model/DynamicBucketModel.java +++ b/src/main/java/net/minecraftforge/client/model/DynamicBucketModel.java @@ -19,10 +19,7 @@ package net.minecraftforge.client.model; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.google.common.collect.*; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; @@ -31,7 +28,6 @@ import net.minecraft.util.math.vector.Quaternion; import net.minecraft.util.math.vector.TransformationMatrix; import net.minecraft.client.renderer.model.*; import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType; -import net.minecraft.client.renderer.texture.MissingTextureSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.entity.LivingEntity; import net.minecraft.fluid.Fluid; @@ -40,29 +36,24 @@ import net.minecraft.item.ItemStack; import net.minecraft.resources.IResourceManager; import net.minecraft.util.Direction; import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.model.geometry.IModelGeometry; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.resource.IResourceType; import net.minecraftforge.resource.VanillaResourceType; -import net.minecraftforge.versions.forge.ForgeVersion; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.Function; import java.util.function.Predicate; public final class DynamicBucketModel implements IModelGeometry { private static final Logger LOGGER = LogManager.getLogger(); - public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation(ForgeVersion.MOD_ID, "dynbucket"), "inventory"); // minimal Z offset to prevent depth-fighting private static final float NORTH_Z_COVER = 7.496f / 16f; @@ -76,13 +67,21 @@ public final class DynamicBucketModel implements IModelGeometry spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation) { - RenderMaterial particleLocation = owner.resolveTexture("particle"); - if (MissingTextureSprite.getLocation().equals(particleLocation.getTextureLocation())) - { - particleLocation = null; - } - - RenderMaterial baseLocation = owner.resolveTexture("base"); - if (MissingTextureSprite.getLocation().equals(baseLocation.getTextureLocation())) - { - baseLocation = null; - } - - RenderMaterial fluidMaskLocation = owner.resolveTexture("fluid"); - if (MissingTextureSprite.getLocation().equals(fluidMaskLocation.getTextureLocation())) - { - fluidMaskLocation = null; - } - - RenderMaterial coverLocation = owner.resolveTexture("cover"); - if (!MissingTextureSprite.getLocation().equals(coverLocation.getTextureLocation())) - { - // cover (the actual item around the other two) - coverLocation = null; - } + RenderMaterial particleLocation = owner.isTexturePresent("particle") ? owner.resolveTexture("particle") : null; + RenderMaterial baseLocation = owner.isTexturePresent("base") ? owner.resolveTexture("base") : null; + RenderMaterial fluidMaskLocation = owner.isTexturePresent("fluid") ? owner.resolveTexture("fluid") : null; + RenderMaterial coverLocation = owner.isTexturePresent("fluid") ? owner.resolveTexture("cover") : null; IModelTransform transformsFromModel = owner.getCombinedTransform(); - ImmutableMap transformMap = transformsFromModel != null ? - PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(transformsFromModel, modelTransform)) : - PerspectiveMapWrapper.getTransforms(modelTransform); + TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? spriteGetter.apply(ForgeHooksClient.getBlockMaterial(fluid.getAttributes().getStillTexture())) : null; + TextureAtlasSprite coverSprite = (coverLocation != null && (!coverIsMask || baseLocation != null)) ? spriteGetter.apply(coverLocation) : null; + + ImmutableMap transformMap = + PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(transformsFromModel, modelTransform)); TextureAtlasSprite particleSprite = particleLocation != null ? spriteGetter.apply(particleLocation) : null; + if (particleSprite == null) particleSprite = fluidSprite; + if (particleSprite == null && !coverIsMask) particleSprite = coverSprite; + // if the fluid is lighter than air, will manipulate the initial state to be rotated 180deg to turn it upside down if (flipGas && fluid != Fluids.EMPTY && fluid.getAttributes().isLighterThanAir()) { - modelTransform = new ModelTransformComposition(modelTransform, new SimpleModelTransform(new TransformationMatrix(null, new Quaternion(0, 0, 1, 0), null, null))); + modelTransform = new SimpleModelTransform( + modelTransform.getRotation().blockCornerToCenter().composeVanilla( + new TransformationMatrix(null, new Quaternion(0, 0, 1, 0), null, null)).blockCenterToCorner()); } TransformationMatrix transform = modelTransform.getRotation(); - TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? spriteGetter.apply(ForgeHooksClient.getBlockMaterial(fluid.getAttributes().getStillTexture())) : null; - - if (particleSprite == null) particleSprite = fluidSprite; - - ImmutableList.Builder builder = ImmutableList.builder(); + ItemMultiLayerBakedModel.Builder builder = ItemMultiLayerBakedModel.builder(owner, particleSprite, new ContainedFluidOverrideHandler(overrides, bakery, owner, this), transformMap); if (baseLocation != null) { // build base (insidest) - builder.addAll(ItemLayerModel.getQuadsForSprites(ImmutableList.of(baseLocation), transform, spriteGetter)); + builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemLayerModel.getQuadsForSprites(ImmutableList.of(baseLocation), transform, spriteGetter)); } if (fluidMaskLocation != null && fluidSprite != null) @@ -156,36 +138,34 @@ public final class DynamicBucketModel implements IModelGeometry 0), ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, NORTH_Z_FLUID, Direction.NORTH, color, 1, luminosity)); + builder.addQuads(ItemLayerModel.getLayerRenderType(luminosity > 0), ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, SOUTH_Z_FLUID, Direction.SOUTH, color, 1, luminosity)); } } - if (coverLocation != null && (!coverIsMask || baseLocation != null)) + if (coverIsMask) + { + if (coverSprite != null && baseLocation != null) + { + TextureAtlasSprite baseSprite = spriteGetter.apply(baseLocation); + builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, NORTH_Z_COVER, Direction.NORTH, 0xFFFFFFFF, 2)); + builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, SOUTH_Z_COVER, Direction.SOUTH, 0xFFFFFFFF, 2)); + } + } + else { - // cover (the actual item around the other two) - TextureAtlasSprite coverSprite = spriteGetter.apply(coverLocation); if (coverSprite != null) { - if (coverIsMask) - { - TextureAtlasSprite baseSprite = spriteGetter.apply(baseLocation); - builder.addAll(ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, NORTH_Z_COVER, Direction.NORTH, 0xFFFFFFFF, 1)); - builder.addAll(ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, SOUTH_Z_COVER, Direction.SOUTH, 0xFFFFFFFF, 1)); - } - else - { - builder.add(ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, NORTH_Z_COVER, coverSprite, Direction.NORTH, 0xFFFFFFFF, 2)); - builder.add(ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, SOUTH_Z_COVER, coverSprite, Direction.SOUTH, 0xFFFFFFFF, 2)); - if (particleSprite == null) - { - particleSprite = coverSprite; - } - } + builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, NORTH_Z_COVER, coverSprite, Direction.NORTH, 0xFFFFFFFF, 2)); + builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, SOUTH_Z_COVER, coverSprite, Direction.SOUTH, 0xFFFFFFFF, 2)); } } - return new BakedModel(bakery, owner, this, builder.build(), particleSprite, Maps.immutableEnumMap(transformMap), Maps.newHashMap(), transform.isIdentity(), modelTransform, owner.isSideLit()); + builder.setParticle(particleSprite); + + return builder.build(); } @Override @@ -193,10 +173,10 @@ public final class DynamicBucketModel implements IModelGeometry texs = Sets.newHashSet(); - texs.add(owner.resolveTexture("particle")); - texs.add(owner.resolveTexture("base")); - texs.add(owner.resolveTexture("fluid")); - texs.add(owner.resolveTexture("cover")); + if (owner.isTexturePresent("particle")) texs.add(owner.resolveTexture("particle")); + if (owner.isTexturePresent("base")) texs.add(owner.resolveTexture("base")); + if (owner.isTexturePresent("fluid")) texs.add(owner.resolveTexture("fluid")); + if (owner.isTexturePresent("cover")) texs.add(owner.resolveTexture("cover")); return texs; } @@ -251,70 +231,55 @@ public final class DynamicBucketModel implements IModelGeometry cache = Maps.newHashMap(); // contains all the baked models since they'll never change + private final ItemOverrideList nested; private final ModelBakery bakery; - - private ContainedFluidOverrideHandler(ModelBakery bakery) + private final IModelConfiguration owner; + private final DynamicBucketModel parent; + + private ContainedFluidOverrideHandler(ItemOverrideList nested, ModelBakery bakery, IModelConfiguration owner, DynamicBucketModel parent) { + this.nested = nested; this.bakery = bakery; + this.owner = owner; + this.parent = parent; } @Override public IBakedModel func_239290_a_(IBakedModel originalModel, ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity) { + IBakedModel overriden = nested.func_239290_a_(originalModel, stack, world, entity); + if (overriden != originalModel) return overriden; return FluidUtil.getFluidContained(stack) .map(fluidStack -> { - BakedModel model = (BakedModel)originalModel; - Fluid fluid = fluidStack.getFluid(); String name = fluid.getRegistryName().toString(); - if (!model.cache.containsKey(name)) + if (!cache.containsKey(name)) { - DynamicBucketModel parent = model.parent.withFluid(fluid); - IBakedModel bakedModel = parent.bake(model.owner, bakery, ModelLoader.defaultTextureGetter(), model.originalTransform, model.getOverrides(), new ResourceLocation("forge:bucket_override")); - model.cache.put(name, bakedModel); + DynamicBucketModel unbaked = this.parent.withFluid(fluid); + IBakedModel bakedModel = unbaked.bake(owner, bakery, ModelLoader.defaultTextureGetter(), ModelRotation.X0_Y0, this, new ResourceLocation("forge:bucket_override")); + cache.put(name, bakedModel); return bakedModel; } - return model.cache.get(name); + return cache.get(name); }) // not a fluid item apparently .orElse(originalModel); // empty bucket } } - - // the dynamic bucket is based on the empty bucket - private static final class BakedModel extends BakedItemModel - { - private final IModelConfiguration owner; - private final DynamicBucketModel parent; - private final Map cache; // contains all the baked models since they'll never change - private final IModelTransform originalTransform; - private final boolean isSideLit; - - BakedModel(ModelBakery bakery, - IModelConfiguration owner, DynamicBucketModel parent, - ImmutableList quads, - TextureAtlasSprite particle, - ImmutableMap transforms, - Map cache, - boolean untransformed, - IModelTransform originalTransform, boolean isSideLit) - { - super(quads, particle, transforms, new ContainedFluidOverrideHandler(bakery), untransformed, isSideLit); - this.owner = owner; - this.parent = parent; - this.cache = cache; - this.originalTransform = originalTransform; - this.isSideLit = isSideLit; - } - } - } diff --git a/src/main/java/net/minecraftforge/client/model/FluidModel.java b/src/main/java/net/minecraftforge/client/model/FluidModel.java index ac52cd672..d0f5d55cf 100644 --- a/src/main/java/net/minecraftforge/client/model/FluidModel.java +++ b/src/main/java/net/minecraftforge/client/model/FluidModel.java @@ -60,6 +60,7 @@ import com.google.common.collect.ImmutableMap; import com.google.gson.JsonElement; import com.google.gson.JsonParser; +// TODO: Write a model loader and test/fix as needed public final class FluidModel implements IModelGeometry { public static final FluidModel WATER = new FluidModel(Fluids.WATER); diff --git a/src/main/java/net/minecraftforge/client/model/IModelConfiguration.java b/src/main/java/net/minecraftforge/client/model/IModelConfiguration.java index b9025a2e9..3e7483f18 100644 --- a/src/main/java/net/minecraftforge/client/model/IModelConfiguration.java +++ b/src/main/java/net/minecraftforge/client/model/IModelConfiguration.java @@ -76,7 +76,6 @@ public interface IModelConfiguration { * Gets the vanilla camera transforms data. * Do not use for non-vanilla code. For general usage, prefer getCombinedState. */ - @Deprecated ItemCameraTransforms getCameraTransforms(); /** diff --git a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java index 755bac183..00b72189a 100644 --- a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java +++ b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java @@ -19,18 +19,13 @@ package net.minecraftforge.client.model; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import com.google.common.collect.*; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; -import it.unimi.dsi.fastutil.ints.IntSet; -import net.minecraft.util.math.vector.TransformationMatrix; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.model.*; -import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; @@ -38,6 +33,8 @@ import net.minecraft.resources.IResourceManager; import net.minecraft.util.Direction; import net.minecraft.util.JSONUtils; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.TransformationMatrix; +import net.minecraftforge.client.ForgeRenderTypes; import net.minecraftforge.client.model.geometry.IModelGeometry; import net.minecraftforge.client.model.pipeline.BakedQuadBuilder; import net.minecraftforge.client.model.pipeline.IVertexConsumer; @@ -55,7 +52,6 @@ import java.util.function.Function; * - Various fixes in the baking logic. * - Not limited to 4 layers maximum. */ -// TODO: Implement as new model loader public final class ItemLayerModel implements IModelGeometry { public static final ItemLayerModel INSTANCE = new ItemLayerModel(ImmutableList.of()); @@ -93,16 +89,31 @@ public final class ItemLayerModel implements IModelGeometry } @Override - public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation) + public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, + Function spriteGetter, IModelTransform modelTransform, + ItemOverrideList overrides, ResourceLocation modelLocation) { - //TODO: Verify + ImmutableMap transformMap = + PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform)); TransformationMatrix transform = modelTransform.getRotation(); - ImmutableList quads = getQuadsForSprites(textures, transform, spriteGetter, fullbrightLayers); TextureAtlasSprite particle = spriteGetter.apply( owner.isTexturePresent("particle") ? owner.resolveTexture("particle") : textures.get(0) ); - ImmutableMap map = PerspectiveMapWrapper.getTransforms(modelTransform); - return new BakedItemModel(quads, particle, map, overrides, transform.isIdentity(), owner.isSideLit()); + + ItemMultiLayerBakedModel.Builder builder = ItemMultiLayerBakedModel.builder(owner, particle, overrides, transformMap); + for(int i = 0; i < textures.size(); i++) + { + TextureAtlasSprite tas = spriteGetter.apply(textures.get(i)); + RenderType rt = getLayerRenderType(fullbrightLayers.contains(i)); + builder.addQuads(rt, getQuadsForSprite(i, tas, transform, true)); + } + + return builder.build(); + } + + public static RenderType getLayerRenderType(boolean isFullbright) + { + return isFullbright ? ForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() : ForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get(); } public static ImmutableList getQuadsForSprites(List textures, TransformationMatrix transform, Function spriteGetter) diff --git a/src/main/java/net/minecraftforge/client/model/ItemMultiLayerBakedModel.java b/src/main/java/net/minecraftforge/client/model/ItemMultiLayerBakedModel.java new file mode 100644 index 000000000..49abb34d4 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ItemMultiLayerBakedModel.java @@ -0,0 +1,211 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.datafixers.util.Pair; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.client.renderer.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.TransformationMatrix; +import net.minecraftforge.client.model.data.EmptyModelData; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public class ItemMultiLayerBakedModel implements IBakedModel +{ + private final boolean smoothLighting; + private final boolean shadedInGui; + private final boolean sideLit; + private final TextureAtlasSprite particle; + private final ItemOverrideList overrides; + private final ImmutableList> layerModels; + private final ImmutableMap cameraTransforms; + + public ItemMultiLayerBakedModel(boolean smoothLighting, boolean shadedInGui, boolean sideLit, + TextureAtlasSprite particle, ItemOverrideList overrides, + ImmutableMap cameraTransforms, + ImmutableList> layerModels) + { + this.smoothLighting = smoothLighting; + this.shadedInGui = shadedInGui; + this.sideLit = sideLit; + this.particle = particle; + this.overrides = overrides; + this.layerModels = layerModels; + this.cameraTransforms = cameraTransforms; + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) + { + List quads = Lists.newArrayList(); + layerModels.forEach(lm -> quads.addAll(lm.getFirst().getQuads(state, side, rand, EmptyModelData.INSTANCE))); + return quads; + } + + @Override + public boolean isAmbientOcclusion() + { + return smoothLighting; + } + + @Override + public boolean isGui3d() + { + return shadedInGui; + } + + @Override + public boolean func_230044_c_() + { + return sideLit; + } + + @Override + public boolean isBuiltInRenderer() + { + return false; + } + + @Override + public TextureAtlasSprite getParticleTexture() + { + return particle; + } + + @Override + public ItemOverrideList getOverrides() + { + return overrides; + } + + @Override + public boolean doesHandlePerspectives() + { + return true; + } + + @Override + public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack mat) + { + return PerspectiveMapWrapper.handlePerspective(this, cameraTransforms, cameraTransformType, mat); + } + + //@Override + public boolean isLayered() + { + return true; + } + + //@Override + public List> getLayerModels(ItemStack itemStack, boolean fabulous) + { + return layerModels; + } + + public static Builder builder(IModelConfiguration owner, TextureAtlasSprite particle, ItemOverrideList overrides, + ImmutableMap cameraTransforms) + { + return new Builder(owner, particle, overrides, cameraTransforms); + } + + public static class Builder + { + private final ImmutableList.Builder> builder = ImmutableList.builder(); + private final List quads = Lists.newArrayList(); + private final ItemOverrideList overrides; + private final ImmutableMap cameraTransforms; + private final IModelConfiguration owner; + private TextureAtlasSprite particle; + private RenderType lastRt = null; + + private Builder(IModelConfiguration owner, TextureAtlasSprite particle, ItemOverrideList overrides, + ImmutableMap cameraTransforms) + { + this.owner = owner; + this.particle = particle; + this.overrides = overrides; + this.cameraTransforms = cameraTransforms; + } + + private void addLayer(ImmutableList.Builder> builder, List quads, RenderType rt) + { + IBakedModel model = new BakedItemModel(ImmutableList.copyOf(quads), particle, ImmutableMap.of(), ItemOverrideList.EMPTY, true, owner.isSideLit()); + builder.add(Pair.of(model, rt)); + } + + private void flushQuads(RenderType rt) + { + if (rt != lastRt) + { + if (quads.size() > 0) + { + addLayer(builder, quads, lastRt); + quads.clear(); + } + lastRt = rt; + } + } + + public Builder setParticle(TextureAtlasSprite particleSprite) + { + this.particle = particleSprite; + return this; + } + + public Builder addQuads(RenderType rt, BakedQuad... quadsToAdd) + { + flushQuads(rt); + Collections.addAll(quads, quadsToAdd); + return this; + } + + public Builder addQuads(RenderType rt, Collection quadsToAdd) + { + flushQuads(rt); + quads.addAll(quadsToAdd); + return this; + } + + public IBakedModel build() + { + if (quads.size() > 0) + { + addLayer(builder, quads, lastRt); + } + return new ItemMultiLayerBakedModel(owner.useSmoothLighting(), owner.isShadedInGui(), owner.isSideLit(), + particle, overrides, cameraTransforms, builder.build()); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ItemTextureQuadConverter.java b/src/main/java/net/minecraftforge/client/model/ItemTextureQuadConverter.java index 774549870..6a55e2ab2 100644 --- a/src/main/java/net/minecraftforge/client/model/ItemTextureQuadConverter.java +++ b/src/main/java/net/minecraftforge/client/model/ItemTextureQuadConverter.java @@ -21,11 +21,12 @@ package net.minecraftforge.client.model; import com.google.common.collect.Lists; -import net.minecraft.util.math.vector.TransformationMatrix; import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.TransformationMatrix; import net.minecraftforge.client.model.pipeline.BakedQuadBuilder; import net.minecraftforge.client.model.pipeline.IVertexConsumer; import net.minecraftforge.client.model.pipeline.TRSRTransformer; @@ -47,15 +48,18 @@ public final class ItemTextureQuadConverter * The resulting list of quads is the texture represented as a list of horizontal OR vertical quads, * depending on which creates less quads. If the amount of quads is equal, horizontal is preferred. * - * @param format * @param template The input texture to convert * @param sprite The texture whose UVs shall be used * @return The generated quads. */ public static List convertTexture(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint) { - List horizontal = convertTextureHorizontal(transform, template, sprite, z, facing, color, tint); - List vertical = convertTextureVertical(transform, template, sprite, z, facing, color, tint); + return convertTexture(transform, template, sprite, z, facing, color, tint, 0); + } + public static List convertTexture(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity) + { + List horizontal = convertTextureHorizontal(transform, template, sprite, z, facing, color, tint, luminosity); + List vertical = convertTextureVertical(transform, template, sprite, z, facing, color, tint, luminosity); return horizontal.size() <= vertical.size() ? horizontal : vertical; } @@ -65,6 +69,10 @@ public final class ItemTextureQuadConverter * The height of the strips is as big as possible. */ public static List convertTextureHorizontal(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint) + { + return convertTextureHorizontal(transform, template, sprite, z, facing, color, tint, 0); + } + public static List convertTextureHorizontal(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity) { int w = template.getWidth(); int h = template.getHeight(); @@ -115,7 +123,7 @@ public final class ItemTextureQuadConverter (float)y * hScale, (float)x * wScale, (float)endY * hScale, - z, sprite, facing, color, tint)); + z, sprite, facing, color, tint, luminosity)); // update Y if all the rows match. no need to rescan if (endY - y > 1) @@ -136,6 +144,10 @@ public final class ItemTextureQuadConverter * The width of the strips is as big as possible. */ public static List convertTextureVertical(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint) + { + return convertTextureVertical(transform, template, sprite, z, facing, color, tint, 0); + } + public static List convertTextureVertical(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity) { int w = template.getWidth(); int h = template.getHeight(); @@ -186,7 +198,7 @@ public final class ItemTextureQuadConverter (float)start * hScale, (float)endX * wScale, (float)y * hScale, - z, sprite, facing, color, tint)); + z, sprite, facing, color, tint, luminosity)); // update X if all the columns match. no need to rescan if (endX - x > 1) @@ -212,6 +224,10 @@ public final class ItemTextureQuadConverter * Coordinates are [0,16] to match the usual coordinates used in TextureAtlasSprites */ public static BakedQuad genQuad(TransformationMatrix transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, Direction facing, int color, int tint) + { + return genQuad(transform, x1, y1, x2, y2, z, sprite, facing, color, tint, 0); + } + public static BakedQuad genQuad(TransformationMatrix transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, Direction facing, int color, int tint, int luminosity) { float u1 = sprite.getInterpolatedU(x1); float v1 = sprite.getInterpolatedV(y1); @@ -227,17 +243,18 @@ public final class ItemTextureQuadConverter y1 = 1f - y2; y2 = 1f - tmp; - return putQuad(transform, facing, sprite, color, tint, x1, y1, x2, y2, z, u1, v1, u2, v2); + return putQuad(transform, facing, sprite, color, tint, x1, y1, x2, y2, z, u1, v1, u2, v2, luminosity); } private static BakedQuad putQuad(TransformationMatrix transform, Direction side, TextureAtlasSprite sprite, int color, int tint, float x1, float y1, float x2, float y2, float z, - float u1, float v1, float u2, float v2) + float u1, float v1, float u2, float v2, int luminosity) { BakedQuadBuilder builder = new BakedQuadBuilder(sprite); builder.setQuadTint(tint); builder.setQuadOrientation(side); + builder.setApplyDiffuseLighting(luminosity == 0); // only apply the transform if it's not identity boolean hasTransform = !transform.isIdentity(); @@ -245,28 +262,29 @@ public final class ItemTextureQuadConverter if (side == Direction.SOUTH) { - putVertex(consumer, side, x1, y1, z, u1, v2, color); - putVertex(consumer, side, x2, y1, z, u2, v2, color); - putVertex(consumer, side, x2, y2, z, u2, v1, color); - putVertex(consumer, side, x1, y2, z, u1, v1, color); + putVertex(consumer, side, x1, y1, z, u1, v2, color, luminosity); + putVertex(consumer, side, x2, y1, z, u2, v2, color, luminosity); + putVertex(consumer, side, x2, y2, z, u2, v1, color, luminosity); + putVertex(consumer, side, x1, y2, z, u1, v1, color, luminosity); } else { - putVertex(consumer, side, x1, y1, z, u1, v2, color); - putVertex(consumer, side, x1, y2, z, u1, v1, color); - putVertex(consumer, side, x2, y2, z, u2, v1, color); - putVertex(consumer, side, x2, y1, z, u2, v2, color); + putVertex(consumer, side, x1, y1, z, u1, v2, color, luminosity); + putVertex(consumer, side, x1, y2, z, u1, v1, color, luminosity); + putVertex(consumer, side, x2, y2, z, u2, v1, color, luminosity); + putVertex(consumer, side, x2, y1, z, u2, v2, color, luminosity); } return builder.build(); } private static void putVertex(IVertexConsumer consumer, Direction side, - float x, float y, float z, float u, float v, int color) + float x, float y, float z, float u, float v, int color, int luminosity) { VertexFormat format = consumer.getVertexFormat(); for (int e = 0; e < format.getElements().size(); e++) { - switch (format.getElements().get(e).getUsage()) + VertexFormatElement element = format.getElements().get(e); + switch (element.getUsage()) { case POSITION: consumer.put(e, x, y, z, 1f); @@ -285,11 +303,16 @@ public final class ItemTextureQuadConverter consumer.put(e, offX, offY, offZ, 0f); break; case UV: - if (format.getElements().get(e).getIndex() == 0) + if (element.getIndex() == 0) { consumer.put(e, u, v, 0f, 1f); break; } + else if (element.getIndex() == 2) + { + consumer.put(e, (luminosity<<4)/32768.0f, (luminosity<<4)/32768.0f, 0f, 1f); + break; + } // else fallthrough to default default: consumer.put(e); diff --git a/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java index 753f99ed6..964a3f1e1 100644 --- a/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java +++ b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java @@ -24,7 +24,7 @@ import java.util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import com.google.common.collect.Sets; +import com.google.common.collect.*; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.mojang.blaze3d.matrix.MatrixStack; @@ -34,11 +34,14 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.util.math.vector.TransformationMatrix; import net.minecraft.client.renderer.model.*; import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.item.ItemStack; import net.minecraft.resources.IResourceManager; import net.minecraft.util.Direction; import net.minecraft.util.JSONUtils; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.ForgeRenderTypes; import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.model.data.EmptyModelData; @@ -48,9 +51,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.function.Function; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import java.util.stream.Collectors; /** * A model that can be rendered in multiple {@link RenderType}. @@ -92,11 +93,12 @@ public final class MultiLayerModel implements IModelGeometry IUnbakedModel missing = ModelLoader.instance().getMissingModel(); return new MultiLayerBakedModel( - owner.useSmoothLighting(), owner.isShadedInGui(), - owner.isSideLit(), spriteGetter.apply(owner.resolveTexture("particle")), overrides, - buildModels(models, modelTransform, bakery, spriteGetter, modelLocation), + owner.useSmoothLighting(), owner.isShadedInGui(), owner.isSideLit(), + spriteGetter.apply(owner.resolveTexture("particle")), overrides, true, missing.bakeModel(bakery, spriteGetter, modelTransform, modelLocation), - PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform))); + buildModels(models, modelTransform, bakery, spriteGetter, modelLocation), + PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform)) + ); } private static final class MultiLayerBakedModel implements IBakedModel @@ -109,10 +111,13 @@ public final class MultiLayerModel implements IModelGeometry protected final TextureAtlasSprite particle; protected final ItemOverrideList overrides; private final IBakedModel missing; + private final boolean convertRenderTypes; + private final List> itemLayers; public MultiLayerBakedModel( boolean ambientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemOverrideList overrides, - ImmutableMap models, IBakedModel missing, ImmutableMap cameraTransforms) + boolean convertRenderTypes, IBakedModel missing, ImmutableMap models, + ImmutableMap cameraTransforms) { this.isSideLit = isSideLit; this.models = models; @@ -122,6 +127,12 @@ public final class MultiLayerModel implements IModelGeometry this.gui3d = isGui3d; this.particle = particle; this.overrides = overrides; + this.convertRenderTypes = convertRenderTypes; + this.itemLayers = models.entrySet().stream().map(kv -> { + RenderType rt = kv.getKey(); + if (convertRenderTypes) rt = ITEM_RENDER_TYPE_MAPPING.getOrDefault(rt, rt); + return Pair.of(kv.getValue(), rt); + }).collect(Collectors.toList()); } @Override @@ -144,6 +155,9 @@ public final class MultiLayerModel implements IModelGeometry } return builder.build(); } + // support for item layer rendering + if (state == null && convertRenderTypes) + layer = ITEM_RENDER_TYPE_MAPPING.inverse().getOrDefault(layer, layer); // assumes that child model will handle this state properly. FIXME? return models.getOrDefault(layer, missing).getQuads(state, side, rand, extraData); } @@ -201,10 +215,38 @@ public final class MultiLayerModel implements IModelGeometry { return ItemOverrideList.EMPTY; } + + @Override + public boolean isLayered() + { + return true; + } + + @Override + public List> getLayerModels(ItemStack itemStack, boolean fabulous) + { + return itemLayers; + } + + public static BiMap ITEM_RENDER_TYPE_MAPPING = HashBiMap.create(); + static { + ITEM_RENDER_TYPE_MAPPING.put(RenderType.getSolid(), RenderType.getEntitySolid(AtlasTexture.LOCATION_BLOCKS_TEXTURE)); + ITEM_RENDER_TYPE_MAPPING.put(RenderType.getCutout(), RenderType.getEntityCutout(AtlasTexture.LOCATION_BLOCKS_TEXTURE)); + ITEM_RENDER_TYPE_MAPPING.put(RenderType.getCutoutMipped(), ForgeRenderTypes.getEntityCutoutMipped(AtlasTexture.LOCATION_BLOCKS_TEXTURE)); + ITEM_RENDER_TYPE_MAPPING.put(RenderType.getTranslucent(), RenderType.getEntityTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE)); + } } public static final class Loader implements IModelLoader { + public static final Map BLOCK_LAYERS = ImmutableMap.builder() + .put("solid", RenderType.getSolid()) + .put("cutout", RenderType.getCutout()) + .put("cutout_mipped", RenderType.getCutoutMipped()) + .put("translucent", RenderType.getTranslucent()) + .put("tripwire", RenderType.func_241715_r_()) + .build(); + public static final Loader INSTANCE = new Loader(); private Loader() {} @@ -220,12 +262,12 @@ public final class MultiLayerModel implements IModelGeometry { ImmutableMap.Builder builder = ImmutableMap.builder(); JsonObject layersObject = JSONUtils.getJsonObject(modelContents, "layers"); - for(RenderType layer : RenderType.getBlockRenderTypes()) // block layers + for(Map.Entry layer : BLOCK_LAYERS.entrySet()) // block layers { - String layerName = layer.toString(); // mc overrides toString to return the ID for the layer + String layerName = layer.getKey(); // mc overrides toString to return the ID for the layer if(layersObject.has(layerName)) { - builder.put(layer, deserializationContext.deserialize(JSONUtils.getJsonObject(layersObject, layerName), BlockModel.class)); + builder.put(layer.getValue(), deserializationContext.deserialize(JSONUtils.getJsonObject(layersObject, layerName), BlockModel.class)); } } ImmutableMap models = builder.build(); diff --git a/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java b/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java index 18b85675b..f06d7b71f 100644 --- a/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java +++ b/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java @@ -31,7 +31,7 @@ import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.util.Direction; /** - * Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.state.IBlockState, net.minecraft.util.EnumFacing, java.util.Random)}. + * Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.BlockState, net.minecraft.util.Direction, java.util.Random)}. */ public interface IDynamicBakedModel extends IBakedModel { diff --git a/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java b/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java index 67222c8b7..9d23a3eec 100644 --- a/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java +++ b/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java @@ -32,7 +32,12 @@ public class ModelDataMap implements IModelData { this.backingMap = new IdentityHashMap<>(map); } - + + protected ModelDataMap() + { + this.backingMap = new IdentityHashMap<>(); + } + @Override public boolean hasProperty(ModelProperty prop) { diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index e526b0b1a..c892f2954 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -150,6 +150,7 @@ public net.minecraft.client.particle.ParticleManager$IParticleMetaFactory public net.minecraft.client.renderer.GameRenderer func_175069_a(Lnet/minecraft/util/ResourceLocation;)V #loadShader private net.minecraft.client.renderer.ItemModelMesher field_199313_a #force public -> private public net.minecraft.client.renderer.ItemRenderer func_229112_a_(Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;Ljava/util/List;Lnet/minecraft/item/ItemStack;II)V # renderQuads +public net.minecraft.client.renderer.ItemRenderer func_229114_a_(Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/item/ItemStack;IILcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;)V # renderModel public net.minecraft.client.renderer.entity.EntityRendererManager field_78729_o #renderers public net.minecraft.client.renderer.entity.EntityRendererManager func_229087_a_(Lnet/minecraft/entity/EntityType;Lnet/minecraft/client/renderer/entity/EntityRenderer;)V # addRenderer protected net.minecraft.client.renderer.entity.ItemRenderer func_177078_a(Lnet/minecraft/item/ItemStack;)I # getMiniItemCount diff --git a/src/test/resources/assets/forgedebugmultilayermodel/models/block/test_layer_block.json b/src/test/resources/assets/forgedebugmultilayermodel/models/block/test_layer_block.json index fa7501f28..1f23b9b7b 100644 --- a/src/test/resources/assets/forgedebugmultilayermodel/models/block/test_layer_block.json +++ b/src/test/resources/assets/forgedebugmultilayermodel/models/block/test_layer_block.json @@ -5,7 +5,7 @@ "solid": { "parent": "block/cube_all", "textures": { "all": "block/slime_block" }, - "transform": { "scale": 0.5, "translation": [-0.25,-0.25,-0.25] } + "transform": { "origin":"center", "scale": 0.625 } }, "translucent": { "parent": "block/cube_all",