diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.java.patch b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.java.patch index aef2e17f1..8f442af36 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.java.patch @@ -1,15 +1,86 @@ --- a/net/minecraft/client/renderer/BlockModelRenderer.java +++ b/net/minecraft/client/renderer/BlockModelRenderer.java -@@ -45,7 +45,7 @@ +@@ -44,11 +44,17 @@ + this.field_187499_a = p_i46575_1_; } ++ @Deprecated public boolean func_199324_a(IWorldReader p_199324_1_, IBakedModel p_199324_2_, IBlockState p_199324_3_, BlockPos p_199324_4_, BufferBuilder p_199324_5_, boolean p_199324_6_, Random p_199324_7_, long p_199324_8_) { - boolean flag = Minecraft.func_71379_u() && p_199324_3_.func_185906_d() == 0 && p_199324_2_.func_177555_b(); -+ boolean flag = Minecraft.func_71379_u() && p_199324_3_.getLightValue(p_199324_1_, p_199324_4_) == 0 && p_199324_2_.isAmbientOcclusion(p_199324_3_); ++ return renderModel(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_, net.minecraftforge.client.model.data.EmptyModelData.INSTANCE); ++ } ++ public boolean renderModel(IWorldReader p_199324_1_, IBakedModel p_199324_2_, IBlockState p_199324_3_, BlockPos p_199324_4_, BufferBuilder p_199324_5_, boolean p_199324_6_, Random p_199324_7_, long p_199324_8_, net.minecraftforge.client.model.data.IModelData modelData) { ++ boolean flag = Minecraft.func_71379_u() && p_199324_3_.getLightValue(p_199324_1_, p_199324_4_) == 0 && p_199324_2_.isAmbientOcclusion(p_199324_3_); ++ modelData = p_199324_2_.getModelData(p_199324_1_, p_199324_4_, p_199324_3_, modelData); ++ try { - return flag ? this.func_199326_b(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_) : this.func_199325_c(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_); -@@ -120,6 +120,13 @@ +- return flag ? this.func_199326_b(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_) : this.func_199325_c(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_); ++ return flag ? this.renderModelSmooth(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_, modelData) : this.renderModelFlat(p_199324_1_, p_199324_2_, p_199324_3_, p_199324_4_, p_199324_5_, p_199324_6_, p_199324_7_, p_199324_8_, modelData); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.func_85055_a(throwable, "Tesselating block model"); + CrashReportCategory crashreportcategory = crashreport.func_85058_a("Block model being tesselated"); +@@ -58,7 +64,12 @@ + } + } + ++ @Deprecated + public boolean func_199326_b(IWorldReader p_199326_1_, IBakedModel p_199326_2_, IBlockState p_199326_3_, BlockPos p_199326_4_, BufferBuilder p_199326_5_, boolean p_199326_6_, Random p_199326_7_, long p_199326_8_) { ++ return renderModelSmooth(p_199326_1_, p_199326_2_, p_199326_3_, p_199326_4_, p_199326_5_, p_199326_6_, p_199326_7_, p_199326_8_, net.minecraftforge.client.model.data.EmptyModelData.INSTANCE); ++ } ++ ++ public boolean renderModelSmooth(IWorldReader p_199326_1_, IBakedModel p_199326_2_, IBlockState p_199326_3_, BlockPos p_199326_4_, BufferBuilder p_199326_5_, boolean p_199326_6_, Random p_199326_7_, long p_199326_8_, net.minecraftforge.client.model.data.IModelData modelData) { + boolean flag = false; + float[] afloat = new float[EnumFacing.values().length * 2]; + BitSet bitset = new BitSet(3); +@@ -66,7 +77,7 @@ + + for(EnumFacing enumfacing : EnumFacing.values()) { + p_199326_7_.setSeed(p_199326_8_); +- List list = p_199326_2_.func_200117_a(p_199326_3_, enumfacing, p_199326_7_); ++ List list = p_199326_2_.getQuads(p_199326_3_, enumfacing, p_199326_7_, modelData); + if (!list.isEmpty() && (!p_199326_6_ || Block.func_176225_a(p_199326_3_, p_199326_1_, p_199326_4_, enumfacing))) { + this.func_187492_a(p_199326_1_, p_199326_3_, p_199326_4_, p_199326_5_, list, afloat, bitset, blockmodelrenderer$ambientocclusionface); + flag = true; +@@ -74,7 +85,7 @@ + } + + p_199326_7_.setSeed(p_199326_8_); +- List list1 = p_199326_2_.func_200117_a(p_199326_3_, (EnumFacing)null, p_199326_7_); ++ List list1 = p_199326_2_.getQuads(p_199326_3_, (EnumFacing)null, p_199326_7_, modelData); + if (!list1.isEmpty()) { + this.func_187492_a(p_199326_1_, p_199326_3_, p_199326_4_, p_199326_5_, list1, afloat, bitset, blockmodelrenderer$ambientocclusionface); + flag = true; +@@ -83,13 +94,18 @@ + return flag; + } + ++ @Deprecated + public boolean func_199325_c(IWorldReader p_199325_1_, IBakedModel p_199325_2_, IBlockState p_199325_3_, BlockPos p_199325_4_, BufferBuilder p_199325_5_, boolean p_199325_6_, Random p_199325_7_, long p_199325_8_) { ++ return renderModelFlat(p_199325_1_, p_199325_2_, p_199325_3_, p_199325_4_, p_199325_5_, p_199325_6_, p_199325_7_, p_199325_8_, net.minecraftforge.client.model.data.EmptyModelData.INSTANCE); ++ } ++ ++ public boolean renderModelFlat(IWorldReader p_199325_1_, IBakedModel p_199325_2_, IBlockState p_199325_3_, BlockPos p_199325_4_, BufferBuilder p_199325_5_, boolean p_199325_6_, Random p_199325_7_, long p_199325_8_, net.minecraftforge.client.model.data.IModelData modelData) { + boolean flag = false; + BitSet bitset = new BitSet(3); + + for(EnumFacing enumfacing : EnumFacing.values()) { + p_199325_7_.setSeed(p_199325_8_); +- List list = p_199325_2_.func_200117_a(p_199325_3_, enumfacing, p_199325_7_); ++ List list = p_199325_2_.getQuads(p_199325_3_, enumfacing, p_199325_7_, modelData); + if (!list.isEmpty() && (!p_199325_6_ || Block.func_176225_a(p_199325_3_, p_199325_1_, p_199325_4_, enumfacing))) { + int i = p_199325_3_.func_185889_a(p_199325_1_, p_199325_4_.func_177972_a(enumfacing)); + this.func_187496_a(p_199325_1_, p_199325_3_, p_199325_4_, i, false, p_199325_5_, list, bitset); +@@ -98,7 +114,7 @@ + } + + p_199325_7_.setSeed(p_199325_8_); +- List list1 = p_199325_2_.func_200117_a(p_199325_3_, (EnumFacing)null, p_199325_7_); ++ List list1 = p_199325_2_.getQuads(p_199325_3_, (EnumFacing)null, p_199325_7_, modelData); + if (!list1.isEmpty()) { + this.func_187496_a(p_199325_1_, p_199325_3_, p_199325_4_, -1, true, p_199325_5_, list1, bitset); + flag = true; +@@ -120,6 +136,13 @@ p_187492_8_.func_187491_a(p_187492_1_, p_187492_2_, p_187492_3_, bakedquad.func_178210_d(), p_187492_6_, p_187492_7_); p_187492_4_.func_178981_a(bakedquad.func_178209_a()); p_187492_4_.func_178962_a(p_187492_8_.field_178207_c[0], p_187492_8_.field_178207_c[1], p_187492_8_.field_178207_c[2], p_187492_8_.field_178207_c[3]); @@ -23,7 +94,7 @@ if (bakedquad.func_178212_b()) { int k = this.field_187499_a.func_186724_a(p_187492_2_, p_187492_1_, p_187492_3_, bakedquad.func_178211_c()); float f = (float)(k >> 16 & 255) / 255.0F; -@@ -229,10 +236,22 @@ +@@ -229,10 +252,22 @@ float f = (float)(k >> 16 & 255) / 255.0F; float f1 = (float)(k >> 8 & 255) / 255.0F; float f2 = (float)(k & 255) / 255.0F; diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch index 006c55e0b..b69d16d6b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch @@ -9,7 +9,7 @@ this.field_175025_e = new BlockFluidRenderer(); } -@@ -40,7 +40,7 @@ +@@ -40,12 +40,17 @@ if (p_175020_1_.func_185901_i() == EnumBlockRenderType.MODEL) { IBakedModel ibakedmodel = this.field_175028_a.func_178125_b(p_175020_1_); long i = p_175020_1_.func_209533_a(p_175020_2_); @@ -18,16 +18,26 @@ this.field_175027_c.func_199324_a(p_175020_4_, ibakedmodel1, p_175020_1_, p_175020_2_, Tessellator.func_178181_a().func_178180_c(), true, this.field_195476_e, i); } } -@@ -53,6 +53,8 @@ + ++ @Deprecated + public boolean func_195475_a(IBlockState p_195475_1_, BlockPos p_195475_2_, IWorldReader p_195475_3_, BufferBuilder p_195475_4_, Random p_195475_5_) { ++ return renderBlock(p_195475_1_, p_195475_2_, p_195475_3_, p_195475_4_, p_195475_5_, net.minecraftforge.client.model.data.EmptyModelData.INSTANCE); ++ } ++ ++ public boolean renderBlock(IBlockState p_195475_1_, BlockPos p_195475_2_, IWorldReader p_195475_3_, BufferBuilder p_195475_4_, Random p_195475_5_, net.minecraftforge.client.model.data.IModelData modelData) { + try { + EnumBlockRenderType enumblockrendertype = p_195475_1_.func_185901_i(); + if (enumblockrendertype == EnumBlockRenderType.INVISIBLE) { +@@ -53,7 +58,7 @@ } else { switch(enumblockrendertype) { case MODEL: -+ IBakedModel model = this.func_184389_a(p_195475_1_); -+ p_195475_1_ = p_195475_1_.func_177230_c().getExtendedState(p_195475_1_, p_195475_3_, p_195475_2_); - return this.field_175027_c.func_199324_a(p_195475_3_, this.func_184389_a(p_195475_1_), p_195475_1_, p_195475_2_, p_195475_4_, true, p_195475_5_, p_195475_1_.func_209533_a(p_195475_2_)); +- return this.field_175027_c.func_199324_a(p_195475_3_, this.func_184389_a(p_195475_1_), p_195475_1_, p_195475_2_, p_195475_4_, true, p_195475_5_, p_195475_1_.func_209533_a(p_195475_2_)); ++ return this.field_175027_c.renderModel(p_195475_3_, this.func_184389_a(p_195475_1_), p_195475_1_, p_195475_2_, p_195475_4_, true, p_195475_5_, p_195475_1_.func_209533_a(p_195475_2_), modelData); case ENTITYBLOCK_ANIMATED: return false; -@@ -105,4 +107,9 @@ + default: +@@ -105,4 +110,9 @@ public void func_195410_a(IResourceManager p_195410_1_) { this.field_175025_e.func_178268_a(); } diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderTask.java.patch b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderTask.java.patch new file mode 100644 index 000000000..d94eab3f9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderTask.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/client/renderer/chunk/ChunkRenderTask.java ++++ b/net/minecraft/client/renderer/chunk/ChunkRenderTask.java +@@ -19,11 +19,14 @@ + private CompiledChunk field_178547_f; + private ChunkRenderTask.Status field_178548_g = ChunkRenderTask.Status.PENDING; + private boolean field_178554_h; ++ private java.util.Map modelData; + + public ChunkRenderTask(RenderChunk p_i46560_1_, ChunkRenderTask.Type p_i46560_2_, double p_i46560_3_) { + this.field_178553_a = p_i46560_1_; + this.field_178549_d = p_i46560_2_; + this.field_188229_e = p_i46560_3_; ++ ++ modelData = net.minecraftforge.client.model.ModelDataManager.getModelData(net.minecraft.client.Minecraft.func_71410_x().field_71441_e, new net.minecraft.util.math.ChunkPos(p_i46560_1_.func_178568_j())); + } + + public ChunkRenderTask.Status func_178546_a() { +@@ -128,4 +131,8 @@ + REBUILD_CHUNK, + RESORT_TRANSPARENCY; + } ++ ++ public net.minecraftforge.client.model.data.IModelData getModelData(net.minecraft.util.math.BlockPos pos) { ++ return modelData.getOrDefault(pos, net.minecraftforge.client.model.data.EmptyModelData.INSTANCE); ++ } + } diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.java.patch b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.java.patch index b02f00a83..2553faf97 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.java.patch @@ -19,7 +19,7 @@ VisGraph lvt_11_1_ = new VisGraph(); HashSet lvt_12_1_ = Sets.newHashSet(); if (lvt_10_1_ != null) { -@@ -142,22 +143,23 @@ +@@ -142,22 +143,24 @@ lvt_11_1_.func_178606_a(blockpos$mutableblockpos); } @@ -41,13 +41,14 @@ IFluidState ifluidstate = lvt_10_1_.func_204610_c(blockpos$mutableblockpos); - if (!ifluidstate.func_206888_e()) { - BlockRenderLayer blockrenderlayer1 = ifluidstate.func_180664_k(); ++ net.minecraftforge.client.model.data.IModelData modelData = p_178581_4_.getModelData(blockpos$mutableblockpos); + for(BlockRenderLayer blockrenderlayer1 : BlockRenderLayer.values()) { + net.minecraftforge.client.ForgeHooksClient.setRenderLayer(blockrenderlayer1); + if (!ifluidstate.func_206888_e() && ifluidstate.canRenderInLayer(blockrenderlayer1)) { int j = blockrenderlayer1.ordinal(); BufferBuilder bufferbuilder = p_178581_4_.func_178545_d().func_179039_a(j); if (!compiledchunk.func_178492_d(blockrenderlayer1)) { -@@ -168,17 +170,18 @@ +@@ -168,17 +171,18 @@ aboolean[j] |= blockrendererdispatcher.func_205318_a(blockpos$mutableblockpos, lvt_10_1_, bufferbuilder, ifluidstate); } @@ -64,7 +65,8 @@ this.func_178573_a(bufferbuilder1, blockpos); } - aboolean[k] |= blockrendererdispatcher.func_195475_a(iblockstate, blockpos$mutableblockpos, lvt_10_1_, bufferbuilder1, random); +- aboolean[k] |= blockrendererdispatcher.func_195475_a(iblockstate, blockpos$mutableblockpos, lvt_10_1_, bufferbuilder1, random); ++ aboolean[k] |= blockrendererdispatcher.renderBlock(iblockstate, blockpos$mutableblockpos, lvt_10_1_, bufferbuilder1, random, modelData); } + } + net.minecraftforge.client.ForgeHooksClient.setRenderLayer(null); diff --git a/patches/minecraft/net/minecraft/client/renderer/model/IBakedModel.java.patch b/patches/minecraft/net/minecraft/client/renderer/model/IBakedModel.java.patch index 1bffc59bc..cce5f56a5 100644 --- a/patches/minecraft/net/minecraft/client/renderer/model/IBakedModel.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/model/IBakedModel.java.patch @@ -1,15 +1,17 @@ --- a/net/minecraft/client/renderer/model/IBakedModel.java +++ b/net/minecraft/client/renderer/model/IBakedModel.java -@@ -10,7 +10,7 @@ +@@ -10,7 +10,9 @@ import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) -public interface IBakedModel { +public interface IBakedModel extends net.minecraftforge.client.extensions.IForgeBakedModel { ++ ++ @Deprecated List func_200117_a(@Nullable IBlockState p_200117_1_, @Nullable EnumFacing p_200117_2_, Random p_200117_3_); boolean func_177555_b(); -@@ -21,7 +21,8 @@ +@@ -21,7 +23,8 @@ TextureAtlasSprite func_177554_e(); diff --git a/patches/minecraft/net/minecraft/world/chunk/ChunkSection.java.patch b/patches/minecraft/net/minecraft/world/chunk/ChunkSection.java.patch deleted file mode 100644 index 873667a33..000000000 --- a/patches/minecraft/net/minecraft/world/chunk/ChunkSection.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/chunk/ChunkSection.java -+++ b/net/minecraft/world/chunk/ChunkSection.java -@@ -35,6 +35,8 @@ - } - - public void func_177484_a(int p_177484_1_, int p_177484_2_, int p_177484_3_, IBlockState p_177484_4_) { -+ if (p_177484_4_ instanceof net.minecraftforge.common.property.IExtendedBlockState) -+ p_177484_4_ = ((net.minecraftforge.common.property.IExtendedBlockState)p_177484_4_).getClean(); - IBlockState iblockstate = this.func_177485_a(p_177484_1_, p_177484_2_, p_177484_3_); - IFluidState ifluidstate = this.func_206914_b(p_177484_1_, p_177484_2_, p_177484_3_); - IFluidState ifluidstate1 = p_177484_4_.func_204520_s(); diff --git a/src/main/java/net/minecraftforge/client/MinecraftForgeClient.java b/src/main/java/net/minecraftforge/client/MinecraftForgeClient.java index ad39ed633..bb16ac1b2 100644 --- a/src/main/java/net/minecraftforge/client/MinecraftForgeClient.java +++ b/src/main/java/net/minecraftforge/client/MinecraftForgeClient.java @@ -20,14 +20,28 @@ package net.minecraftforge.client; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.BitSet; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import javax.annotation.Nonnull; +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.chunk.RenderChunkCache; import net.minecraft.client.renderer.texture.NativeImage; @@ -36,13 +50,10 @@ import net.minecraft.resources.IResourceManager; import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.World; - -import org.apache.commons.lang3.tuple.Pair; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.data.IModelData; public class MinecraftForgeClient { diff --git a/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java b/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java index d2f32914e..61c78bcb2 100644 --- a/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java +++ b/src/main/java/net/minecraftforge/client/extensions/IForgeBakedModel.java @@ -19,17 +19,34 @@ package net.minecraftforge.client.extensions; +import java.util.List; +import java.util.Random; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorldReader; +import net.minecraftforge.client.model.data.IModelData; public interface IForgeBakedModel -{ +{ default IBakedModel getBakedModel() { return (IBakedModel) this; } + @Nonnull + default List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand, @Nonnull IModelData extraData) + { + return getBakedModel().getQuads(state, side, rand); + } + default boolean isAmbientOcclusion(IBlockState state) { return getBakedModel().isAmbientOcclusion(); } /* @@ -40,4 +57,9 @@ public interface IForgeBakedModel { return net.minecraftforge.client.ForgeHooksClient.handlePerspective(getBakedModel(), cameraTransformType); } + + default @Nonnull IModelData getModelData(@Nonnull IWorldReader world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull IModelData tileData) + { + return tileData; + } } diff --git a/src/main/java/net/minecraftforge/client/model/ModelDataManager.java b/src/main/java/net/minecraftforge/client/model/ModelDataManager.java new file mode 100644 index 000000000..4efd727ad --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ModelDataManager.java @@ -0,0 +1,101 @@ +package net.minecraftforge.client.model; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import net.minecraft.client.Minecraft; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraftforge.client.model.data.IModelData; + +public class ModelDataManager +{ + private static WeakReference currentWorld = new WeakReference<>(null); + + private static final Map> needModelDataRefresh = new HashMap<>(); + + private static final LoadingCache> modelDataCache = CacheBuilder.newBuilder() + .maximumSize(1000) + .concurrencyLevel(5) + .expireAfterAccess(30, TimeUnit.SECONDS) + .build(new CacheLoader>(){ + + @Override + public Map load(@Nonnull ChunkPos key) + { + return new ConcurrentHashMap<>(); + } + }); + + private static void cleanCaches(World world) + { + if (world != currentWorld.get()) + { + currentWorld = new WeakReference<>(world); + needModelDataRefresh.clear(); + modelDataCache.invalidateAll(); + } + } + + public static void requestModelDataRefresh(TileEntity te) + { + World world = te.getWorld(); + Preconditions.checkNotNull(world, "Tile entity world must not be null"); + Preconditions.checkArgument(world == Minecraft.getInstance().world, "Cannot request a model data refresh for a world other than the current client world"); + synchronized (needModelDataRefresh) + { + cleanCaches(world); + needModelDataRefresh.computeIfAbsent(new ChunkPos(te.getPos()), $ -> Collections.synchronizedSet(new HashSet<>())) + .add(te.getPos()); + } + } + + private static void refreshModelData(World world, ChunkPos chunk) + { + Set needUpdate; + synchronized (needModelDataRefresh) + { + cleanCaches(world); + needUpdate = needModelDataRefresh.remove(chunk); + } + if (needUpdate != null) + { + Map data = modelDataCache.getUnchecked(chunk); + for (BlockPos pos : needUpdate) + { + TileEntity toUpdate = world.getTileEntity(pos); + if (toUpdate != null) + { + data.put(pos, toUpdate.getModelData()); + } + } + } + } + + public static @Nullable IModelData getModelData(World world, BlockPos pos) + { + return getModelData(world, new ChunkPos(pos)).get(pos); + } + + public static Map getModelData(World world, ChunkPos pos) + { + refreshModelData(world, pos); + return modelDataCache.getUnchecked(pos); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ModelFluid.java b/src/main/java/net/minecraftforge/client/model/ModelFluid.java index 11310e5a3..f3d4a7e03 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelFluid.java +++ b/src/main/java/net/minecraftforge/client/model/ModelFluid.java @@ -43,13 +43,13 @@ import net.minecraft.resources.IResourceManager; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.pipeline.IVertexConsumer; import net.minecraftforge.client.model.pipeline.TRSRTransformer; import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.versions.forge.ForgeVersion; import net.minecraftforge.common.model.IModelState; import net.minecraftforge.common.model.TRSRTransformation; -import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.fluids.Fluid; import org.apache.commons.lang3.tuple.Pair; @@ -103,7 +103,7 @@ public final class ModelFluid implements IUnbakedModel bakedTextureGetter.apply(fluid.getFlowing()), Optional.ofNullable(fluid.getOverlay()).map(bakedTextureGetter), fluid.isLighterThanAir(), - Optional.empty() + null ); } @@ -157,7 +157,7 @@ public final class ModelFluid implements IUnbakedModel } }); - public CachingBakedFluid(Optional transformation, ImmutableMap transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional overlay, boolean gas, Optional stateOption) + public CachingBakedFluid(Optional transformation, ImmutableMap transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional overlay, boolean gas, Optional stateOption) { super(transformation, transforms, format, color, still, flowing, overlay, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption), getOverlay(stateOption)); } @@ -171,12 +171,12 @@ public final class ModelFluid implements IUnbakedModel * while also providing good use of the available value range. * (For fluids with default quanta, this evenly divides the per-block intervals of 1/9 by 96) */ - private static int[] getCorners(Optional stateOption) + private static int[] getCorners(Optional stateOption) { int[] cornerRound = {0, 0, 0, 0}; if (stateOption.isPresent()) { - IExtendedBlockState state = stateOption.get(); + IModelData state = stateOption.get(); for (int i = 0; i < 4; i++) { Float level = null; // TODO fluids state.getValue(BlockFluidBase.LEVEL_CORNERS[i]); @@ -193,7 +193,7 @@ public final class ModelFluid implements IUnbakedModel * The value is currently stored as the angle rounded to the nearest degree. * A value of -1000 is used to signify no flow. */ - private static int getFlow(Optional stateOption) + private static int getFlow(Optional stateOption) { Float flow = -1000f; if (stateOption.isPresent()) @@ -213,12 +213,12 @@ public final class ModelFluid implements IUnbakedModel * instead of the normal "flowing" texture (if applicable for that fluid). * The sides are stored here by their regular horizontal index. */ - private static boolean[] getOverlay(Optional stateOption) + private static boolean[] getOverlay(Optional stateOption) { boolean[] overlaySides = new boolean[4]; if (stateOption.isPresent()) { - IExtendedBlockState state = stateOption.get(); + IModelData state = stateOption.get(); for (int i = 0; i < 4; i++) { Boolean overlay = null; // TODO fluids state.getValue(BlockFluidBase.SIDE_OVERLAYS[i]); @@ -229,11 +229,11 @@ public final class ModelFluid implements IUnbakedModel } @Override - public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand) + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData modelData) { - if (side != null && state instanceof IExtendedBlockState) + if (side != null) { - Optional exState = Optional.of((IExtendedBlockState)state); + Optional exState = Optional.of(modelData); int[] cornerRound = getCorners(exState); int flowRound = getFlow(exState); diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index bbbb6e0df..31b31b68b 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -77,12 +77,12 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.IRegistry; import net.minecraftforge.client.model.animation.AnimationItemOverrideList; import net.minecraftforge.client.model.animation.ModelBlockAnimation; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.model.IModelState; import net.minecraftforge.common.model.Models; import net.minecraftforge.common.model.TRSRTransformation; import net.minecraftforge.common.model.animation.IClip; -import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fml.client.ClientModLoader; @@ -510,20 +510,12 @@ public final class ModelLoader extends ModelBakery private final ItemOverrideList overrides = new AnimationItemOverrideList(VanillaModelWrapper.this, modelState, format, bakedTextureGetter, super.getOverrides()); @Override - public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand) + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData modelData) { - if(state instanceof IExtendedBlockState) + IModelState newState = modelData.getData(Properties.AnimationProperty); + if(newState != null) { - IExtendedBlockState exState = (IExtendedBlockState)state; - if(exState.getUnlistedNames().contains(Properties.AnimationProperty)) - { - IModelState newState = exState.getValue(Properties.AnimationProperty); - IExtendedBlockState newExState = (IExtendedBlockState) exState.withProperty(Properties.AnimationProperty, null); - if(newState != null) - { - return VanillaModelWrapper.this.bake(modelGetter, bakedTextureGetter, new ModelStateComposition(modelState, newState), uvlock, format).getQuads(newExState, side, rand); - } - } + return VanillaModelWrapper.this.bake(modelGetter, bakedTextureGetter, new ModelStateComposition(modelState, newState), uvlock, format).getQuads(state, side, rand, modelData); } return super.getQuads(state, side, rand); } diff --git a/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java b/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java index 674ca60a3..d910dbbfa 100644 --- a/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java +++ b/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java @@ -19,26 +19,26 @@ package net.minecraftforge.client.model.animation; +import java.util.Random; + import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BlockRendererDispatcher; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.model.IBakedModel; -import net.minecraft.client.renderer.tileentity.TileEntityRenderer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IWorldReader; import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.client.model.ModelDataManager; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.common.animation.Event; import net.minecraftforge.common.animation.IEventHandler; import net.minecraftforge.common.model.animation.CapabilityAnimation; import net.minecraftforge.common.model.animation.IAnimationStateMachine; -import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; import net.minecraftforge.common.util.LazyOptional; -import java.util.Random; - /** * Generic {@link TileGameRenderer} that works with the Forge model system and animations. */ @@ -58,30 +58,23 @@ public class TileEntityRendererAnimation extends TileEntit BlockPos pos = te.getPos(); IWorldReader world = MinecraftForgeClient.getRegionRenderCache(te.getWorld(), pos); IBlockState state = world.getBlockState(pos); - if(state.getBlock().getStateContainer().getProperties().contains(Properties.StaticProperty)) + IBakedModel model = blockRenderer.getBlockModelShapes().getModel(state); + IModelData data = model.getModelData(world, pos, state, ModelDataManager.getModelData(te.getWorld(), pos)); + if (data.hasProperty(Properties.AnimationProperty)) { - state = state.with(Properties.StaticProperty, false); - } - if(state instanceof IExtendedBlockState) - { - IExtendedBlockState exState = (IExtendedBlockState)state; - if(exState.getUnlistedNames().contains(Properties.AnimationProperty)) - { - float time = Animation.getWorldTime(getWorld(), partialTick); - cap - .map(asm -> asm.apply(time)) - .ifPresent(pair -> { - handleEvents(te, time, pair.getRight()); + float time = Animation.getWorldTime(getWorld(), partialTick); + cap + .map(asm -> asm.apply(time)) + .ifPresent(pair -> { + handleEvents(te, time, pair.getRight()); - // TODO: caching? - IBakedModel model = blockRenderer.getBlockModelShapes().getModel(exState.getClean()); - IExtendedBlockState animState = (IExtendedBlockState) exState.withProperty(Properties.AnimationProperty, pair.getLeft()); + // TODO: caching? + data.setData(Properties.AnimationProperty, pair.getLeft()); - renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ()); + renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ()); - blockRenderer.getBlockModelRenderer().renderModel(world, model, animState, pos, renderer, false, new Random(), 42); - }); - } + blockRenderer.getBlockModelRenderer().renderModel(world, model, state, pos, renderer, false, new Random(), 42, data); + }); } } diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java index 54c9f12f1..997e82fb7 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -27,13 +27,33 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import javax.annotation.Nullable; import javax.vecmath.Matrix4f; import javax.vecmath.Vector3f; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.common.base.Objects; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.IBakedModel; @@ -59,6 +79,8 @@ import net.minecraftforge.client.model.b3d.B3DModel.Mesh; import net.minecraftforge.client.model.b3d.B3DModel.Node; import net.minecraftforge.client.model.b3d.B3DModel.Texture; import net.minecraftforge.client.model.b3d.B3DModel.Vertex; +import net.minecraftforge.client.model.data.IDynamicBakedModel; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.model.IModelPart; import net.minecraftforge.common.model.IModelState; @@ -66,30 +88,8 @@ import net.minecraftforge.common.model.Models; import net.minecraftforge.common.model.TRSRTransformation; import net.minecraftforge.common.model.animation.IClip; import net.minecraftforge.common.model.animation.IJoint; -import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.function.Function; -import com.google.common.base.Objects; -import java.util.Optional; -import java.util.Random; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; - /* * Loader for Blitz3D models. * To enable for your mod call instance.addDomain(modId). @@ -599,7 +599,7 @@ public enum B3DLoader implements ICustomModelLoader } } - private static final class BakedWrapper implements IBakedModel + private static final class BakedWrapper implements IDynamicBakedModel { private final Node node; private final IModelState state; @@ -647,34 +647,27 @@ public enum B3DLoader implements ICustomModelLoader } @Override - public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand) + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData data) { if(side != null) return ImmutableList.of(); IModelState modelState = this.state; - if(state instanceof IExtendedBlockState) + IModelState newState = data.getData(Properties.AnimationProperty); + if(newState != null) { - IExtendedBlockState exState = (IExtendedBlockState)state; - if(exState.getUnlistedNames().contains(Properties.AnimationProperty)) + // FIXME: should animation state handle the parent state, or should it remain here? + IModelState parent = this.state; + if(parent instanceof B3DState) { - // FIXME: should animation state handle the parent state, or should it remain here? - IModelState parent = this.state; - if(parent instanceof B3DState) - { - B3DState ps = (B3DState)parent; - parent = ps.getParent(); - } - IModelState newState = exState.getValue(Properties.AnimationProperty); - if(newState != null) - { - if (parent == null) - { - modelState = newState; - } - else - { - modelState = new ModelStateComposition(parent, newState); - } - } + B3DState ps = (B3DState)parent; + parent = ps.getParent(); + } + if (parent == null) + { + modelState = newState; + } + else + { + modelState = new ModelStateComposition(parent, newState); } } if(quads == null) diff --git a/src/main/java/net/minecraftforge/client/model/data/EmptyModelData.java b/src/main/java/net/minecraftforge/client/model/data/EmptyModelData.java new file mode 100644 index 000000000..16223e1f3 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/data/EmptyModelData.java @@ -0,0 +1,15 @@ +package net.minecraftforge.client.model.data; + +public enum EmptyModelData implements IModelData +{ + INSTANCE; + + @Override + public boolean hasProperty(ModelProperty prop) { return false; } + + @Override + public T getData(ModelProperty prop) { return null; } + + @Override + public T setData(ModelProperty prop, T data) { return null; } +} diff --git a/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java b/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java new file mode 100644 index 000000000..1cb69c270 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/data/IDynamicBakedModel.java @@ -0,0 +1,29 @@ +package net.minecraftforge.client.model.data; + +import java.util.List; +import java.util.Random; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.util.EnumFacing; + +/** + * Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.state.IBlockState, net.minecraft.util.EnumFacing, java.util.Random)}. + */ +public interface IDynamicBakedModel extends IBakedModel +{ + @Override + default @Nonnull List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand) + { + return getQuads(state, side, rand, EmptyModelData.INSTANCE); + } + + // Force this to be overriden otherwise this introduces a default cycle between the two overloads. + @Override + @Nonnull + List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand, @Nonnull IModelData extraData); +} diff --git a/src/main/java/net/minecraftforge/client/model/data/IModelData.java b/src/main/java/net/minecraftforge/client/model/data/IModelData.java new file mode 100644 index 000000000..578c7755a --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/data/IModelData.java @@ -0,0 +1,25 @@ +package net.minecraftforge.client.model.data; + +import javax.annotation.Nullable; + +public interface IModelData +{ + /** + * Check if this data has a property, even if the value is {@code null}. Can be + * used by code that intends to fill in data for a render pipeline, such as the + * forge animation system. + *

+ * IMPORTANT: {@link #getData(ModelProperty)} can return {@code null} + * even if this method returns {@code true}. + * + * @param prop The property to check for inclusion in this model data + * @return {@code true} if this data has the given property, even if no value is present + */ + boolean hasProperty(ModelProperty prop); + + @Nullable + T getData(ModelProperty prop); + + @Nullable + T setData(ModelProperty prop, T data); +} diff --git a/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java b/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java new file mode 100644 index 000000000..a9082c885 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java @@ -0,0 +1,58 @@ +package net.minecraftforge.client.model.data; + +import java.util.IdentityHashMap; +import java.util.Map; + +import com.google.common.base.Preconditions; + +public class ModelDataMap implements IModelData +{ + private final Map, Object> backingMap; + + private ModelDataMap(Map, Object> map) + { + this.backingMap = new IdentityHashMap<>(map); + } + + @Override + public boolean hasProperty(ModelProperty prop) + { + return backingMap.containsKey(prop); + } + + @SuppressWarnings("unchecked") + @Override + public T getData(ModelProperty prop) + { + return (T) backingMap.get(prop); + } + + @SuppressWarnings("unchecked") + @Override + public T setData(ModelProperty prop, T data) + { + Preconditions.checkArgument(prop.test(data), "Value is invalid for this property"); + return (T) backingMap.put(prop, data); + } + + public static class Builder + { + private final Map, Object> defaults = new IdentityHashMap<>(); + + public Builder withProperty(ModelProperty prop) + { + return withInitial(prop, null); + } + + public Builder withInitial(ModelProperty prop, T data) + { + this.defaults.put(prop, data); + return this; + } + + public ModelDataMap build() + { + return new ModelDataMap(defaults); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java b/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java new file mode 100644 index 000000000..d931fbe85 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java @@ -0,0 +1,23 @@ +package net.minecraftforge.client.model.data; + +import java.util.function.Predicate; + +import com.google.common.base.Predicates; + +public class ModelProperty implements Predicate { + + private final Predicate pred; + + public ModelProperty() { + this(Predicates.alwaysTrue()); + } + + public ModelProperty(Predicate pred) { + this.pred = pred; + } + + @Override + public boolean test(T t) { + return pred.test(t); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java index 5fced635d..6b6043947 100644 --- a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java +++ b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java @@ -54,13 +54,13 @@ import net.minecraft.resources.IResourceManager; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.model.*; +import net.minecraftforge.client.model.data.IDynamicBakedModel; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.model.IModelPart; import net.minecraftforge.common.model.IModelState; import net.minecraftforge.common.model.Models; import net.minecraftforge.common.model.TRSRTransformation; -import net.minecraftforge.common.property.IExtendedBlockState; -import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.common.property.Properties; import org.apache.commons.lang3.tuple.Pair; @@ -1255,36 +1255,7 @@ public class OBJModel implements IUnbakedModel } } - @Deprecated - public enum OBJProperty implements IUnlistedProperty - { - INSTANCE; - @Override - public String getName() - { - return "OBJProperty"; - } - - @Override - public boolean isValid(OBJState value) - { - return value instanceof OBJState; - } - - @Override - public Class getType() - { - return OBJState.class; - } - - @Override - public String valueToString(OBJState value) - { - return value.toString(); - } - } - - public class OBJBakedModel implements IBakedModel + public class OBJBakedModel implements IDynamicBakedModel { private final OBJModel model; private IModelState state; @@ -1308,26 +1279,18 @@ public class OBJModel implements IUnbakedModel // FIXME: merge with getQuads @Override - public List getQuads(IBlockState blockState, EnumFacing side, Random rand) + public List getQuads(IBlockState blockState, EnumFacing side, Random rand, IModelData modelData) { if (side != null) return ImmutableList.of(); if (quads == null) { quads = buildQuads(this.state); } - if (blockState instanceof IExtendedBlockState) + IModelState newState = modelData.getData(Properties.AnimationProperty); + if (newState != null) { - IExtendedBlockState exState = (IExtendedBlockState) blockState; - if (exState.getUnlistedNames().contains(Properties.AnimationProperty)) - { - - IModelState newState = exState.getValue(Properties.AnimationProperty); - if (newState != null) - { - newState = new ModelStateComposition(this.state, newState); - return buildQuads(newState); - } - } + newState = new ModelStateComposition(this.state, newState); + return buildQuads(newState); } return quads; } diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java b/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java index 9ae7615ee..0d29c3caf 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java @@ -19,6 +19,8 @@ package net.minecraftforge.common.extensions; +import javax.annotation.Nonnull; + import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; @@ -29,6 +31,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.ModelDataManager; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.common.capabilities.ICapabilitySerializable; public interface IForgeTileEntity extends ICapabilitySerializable @@ -86,7 +91,10 @@ public interface IForgeTileEntity extends ICapabilitySerializable> extends StateContainer -{ - private final ImmutableSet> unlistedProperties; - - public > ExtendedStateContainer(O blockIn, StateContainer.IFactory stateFactory, net.minecraft.state.IProperty[] properties, IUnlistedProperty[] unlistedProperties) - { - super(blockIn, getProxyFactory(unlistedProperties, stateFactory), buildListedMap(properties));// TODO Unlisted properties?, buildUnlistedMap(unlistedProperties)); - ImmutableSet.Builder> builder = ImmutableSet.builder(); - for(IUnlistedProperty property : unlistedProperties) - { - builder.add(property); - } - this.unlistedProperties = builder.build(); - } - - private static , A extends AbstractStateHolder> - StateContainer.IFactory> getProxyFactory(IUnlistedProperty[] unlistedProperties, StateContainer.IFactory proxy) - { - return (o, props) -> { - if (unlistedProperties == null || unlistedProperties.length == 0) return proxy.create(o, props); - return new ExtendedStateHolder(o, props, buildUnlistedMap(unlistedProperties), null); - }; - } - - public Collection> getUnlistedProperties() - { - return unlistedProperties; - } - - private static Map> buildListedMap(IProperty[] properties) - { - return Arrays.stream(properties).collect(Collectors.toMap(IProperty::getName, Function.identity())); - } - - private static ImmutableMap, Optional> buildUnlistedMap(IUnlistedProperty[] unlistedProperties) - { - ImmutableMap.Builder, Optional> builder = ImmutableMap.builder(); - for(IUnlistedProperty p : unlistedProperties) - { - builder.put(p, Optional.empty()); - } - return builder.build(); - } - - protected static class ExtendedStateHolder> extends AbstractStateHolder implements IExtendedState - { - private final ImmutableMap, Optional> unlistedProperties; - private S cleanState; - - protected ExtendedStateHolder(O block, ImmutableMap, Comparable> properties, ImmutableMap, Optional> unlistedProperties, S clean) - { - super(block, properties); - this.unlistedProperties = unlistedProperties; - this.cleanState = clean == null ? (S) this : clean; - } - - @Override - @Nonnull - public , V extends T> S with(@Nonnull IProperty property, @Nonnull V value) - { - S clean = super.with(property, value); - if (clean == this.cleanState) { - return (S) this; - } - - if (this == this.cleanState) - { // no dynamic properties present, looking up in the normal table - return clean; - } - - return (S) new ExtendedStateHolder(object, ((IStateHolder)clean).getValues(), unlistedProperties, this.cleanState); - } - - @Override - public S withProperty(IUnlistedProperty property, @Nullable V value) - { - Optional oldValue = unlistedProperties.get(property); - if (oldValue == null) - { - throw new IllegalArgumentException("Cannot set unlisted property " + property + " as it does not exist in " + this); - } - if (Objects.equals(oldValue.orElse(null), value)) - { - return (S) this; - } - if (!property.isValid(value)) - { - throw new IllegalArgumentException("Cannot set unlisted property " + property + " to " + value + " on object " + object + ", it is not an allowed value"); - } - boolean clean = true; - ImmutableMap.Builder, Optional> builder = ImmutableMap.builder(); - for (Map.Entry, Optional> entry : unlistedProperties.entrySet()) - { - IUnlistedProperty key = entry.getKey(); - Optional newValue = key.equals(property) ? Optional.ofNullable(value) : entry.getValue(); - if (newValue.isPresent()) clean = false; - builder.put(key, newValue); - } - if (clean) - { // no dynamic properties, lookup normal state - return (S) cleanState; - } - return (S) new ExtendedStateHolder(object, getValues(), builder.build(), this.cleanState); - } - - @Override - public Collection> getUnlistedNames() - { - return unlistedProperties.keySet(); - } - - @Override - @Nullable - public V getValue(IUnlistedProperty property) - { - Optional value = unlistedProperties.get(property); - if (value == null) - { - throw new IllegalArgumentException("Cannot get unlisted property " + property + " as it does not exist in " + this); - } - return property.getType().cast(value.orElse(null)); - } - - public ImmutableMap, Optional> getUnlistedProperties() - { - return unlistedProperties; - } - - @Override - public S getClean() - { - return cleanState; - } - } -} diff --git a/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java b/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java deleted file mode 100644 index 7b20ca42d..000000000 --- a/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2019. - * - * 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.common.property; - -import net.minecraft.block.state.IBlockState; - -public interface IExtendedBlockState extends IExtendedState, IBlockState -{ - -} diff --git a/src/main/java/net/minecraftforge/common/property/IExtendedState.java b/src/main/java/net/minecraftforge/common/property/IExtendedState.java deleted file mode 100644 index 77992f1d4..000000000 --- a/src/main/java/net/minecraftforge/common/property/IExtendedState.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2019. - * - * 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.common.property; - -import java.util.Collection; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.state.IStateHolder; - -import java.util.Optional; -import com.google.common.collect.ImmutableMap; - -public interface IExtendedState extends IStateHolder -{ - Collection> getUnlistedNames(); - - V getValue(IUnlistedProperty property); - - C withProperty(IUnlistedProperty property, V value); - - ImmutableMap, Optional> getUnlistedProperties(); - - C getClean(); -} diff --git a/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java b/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java deleted file mode 100644 index a3c9f8366..000000000 --- a/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2019. - * - * 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.common.property; - -public interface IUnlistedProperty -{ - String getName(); - - boolean isValid(V value); - - Class getType(); - - String valueToString(V value); -} diff --git a/src/main/java/net/minecraftforge/common/property/Properties.java b/src/main/java/net/minecraftforge/common/property/Properties.java index 625e6dbb7..025747d39 100644 --- a/src/main/java/net/minecraftforge/common/property/Properties.java +++ b/src/main/java/net/minecraftforge/common/property/Properties.java @@ -20,7 +20,7 @@ package net.minecraftforge.common.property; import net.minecraft.state.BooleanProperty; -import net.minecraft.state.IProperty; +import net.minecraftforge.client.model.data.ModelProperty; import net.minecraftforge.common.model.IModelState; public class Properties @@ -33,54 +33,5 @@ public class Properties /** * Property holding the IModelState used for animating the model in the TESR. */ - public static final IUnlistedProperty AnimationProperty = new IUnlistedProperty() - { - @Override - public String getName() { return "forge_animation"; } - @Override - public boolean isValid(IModelState state) { return true; } - @Override - public Class getType() { return IModelState.class; } - @Override - public String valueToString(IModelState state) { return state.toString(); } - }; - - public static > IUnlistedProperty toUnlisted(IProperty property) - { - return new PropertyAdapter(property); - } - - public static class PropertyAdapter> implements IUnlistedProperty - { - private final IProperty parent; - - public PropertyAdapter(IProperty parent) - { - this.parent = parent; - } - - @Override - public String getName() - { - return parent.getName(); - } - - @Override - public boolean isValid(V value) - { - return parent.getAllowedValues().contains(value); - } - - @Override - public Class getType() - { - return parent.getValueClass(); - } - - @Override - public String valueToString(V value) - { - return parent.getName(value); - } - } + public static final ModelProperty AnimationProperty = new ModelProperty(); } diff --git a/src/main/java/net/minecraftforge/common/property/PropertyFloat.java b/src/main/java/net/minecraftforge/common/property/PropertyFloat.java deleted file mode 100644 index fb5a28dcd..000000000 --- a/src/main/java/net/minecraftforge/common/property/PropertyFloat.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2019. - * - * 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.common.property; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Range; - -public class PropertyFloat implements IUnlistedProperty -{ - private final String name; - private final Predicate validator; - - public PropertyFloat(String name) - { - this(name, Predicates.alwaysTrue()); - } - - public PropertyFloat(String name, float min, float max) - { - this(name, Range.closed(min, max)); - } - - public PropertyFloat(String name, Predicate validator) - { - this.name = name; - this.validator = validator; - } - - @Override - public String getName() - { - return name; - } - - @Override - public boolean isValid(Float value) - { - return validator.apply(value); - } - - @Override - public Class getType() - { - return Float.class; - } - - @Override - public String valueToString(Float value) - { - return value.toString(); - } -} diff --git a/src/main/resources/assets/forge/blockstates/modeltest.json b/src/main/resources/assets/forge/blockstates/modeltest.json new file mode 100644 index 000000000..19e9105c9 --- /dev/null +++ b/src/main/resources/assets/forge/blockstates/modeltest.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "forge:modeltest" } + } +} diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 5bb3af307..35720a2ff 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -4,6 +4,11 @@ net/minecraft/block/BlockRailPowered.(Lnet/minecraft/block/Block$Propertie net/minecraft/block/BlockRedstoneWire.canConnectTo(Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;)Z=|p_176343_0_,world,pos,p_176343_1_ net/minecraft/client/multiplayer/WorldClient.removeEntity(Lnet/minecraft/entity/Entity;Z)V=|p_72900_1_,keepData + +net/minecraft/client/renderer/BlockRendererDispatcher.renderBlock(Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/IWorldReader;Lnet/minecraft/client/renderer/BufferBuilder;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Z=|p_195475_1_,p_195475_2_,p_195475_3_,p_195475_4_,p_195475_5_,modelData +net/minecraft/client/renderer/BlockModelRenderer.renderModel(Lnet/minecraft/world/IWorldReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/renderer/BufferBuilder;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z=|p_199324_1_,p_199324_2_,p_199324_3_,p_199324_4_,p_199324_5_,p_199324_6_,p_199324_7_,p_199324_8_,modelData +net/minecraft/client/renderer/BlockModelRenderer.renderModelSmooth(Lnet/minecraft/world/IWorldReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/renderer/BufferBuilder;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z=|p_199326_1_,p_199326_2_,p_199326_3_,p_199326_4_,p_199326_5_,p_199326_6_,p_199326_7_,p_199326_8_,modelData +net/minecraft/client/renderer/BlockModelRenderer.renderModelFlat(Lnet/minecraft/world/IWorldReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/renderer/BufferBuilder;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z=|p_199325_1_,p_199325_2_,p_199325_3_,p_199325_4_,p_199325_5_,p_199325_6_,p_199325_7_,p_199325_8_,modelData net/minecraft/client/renderer/model/BakedQuad.([IILnet/minecraft/util/EnumFacing;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;ZLnet/minecraft/client/renderer/vertex/VertexFormat;)V=|p_i46574_1_,p_i46574_2_,p_i46574_3_,p_i46574_4_,applyDiffuseLighting,format net/minecraft/client/renderer/model/FaceBakery.makeBakedQuad(Lnet/minecraft/client/renderer/Vector3f;Lnet/minecraft/client/renderer/Vector3f;Lnet/minecraft/client/renderer/model/BlockPartFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/util/EnumFacing;Lnet/minecraftforge/common/model/ITransformation;Lnet/minecraft/client/renderer/model/BlockPartRotation;ZZ)Lnet/minecraft/client/renderer/model/BakedQuad;=|p_199332_1_,p_199332_2_,p_199332_3_,p_199332_4_,p_199332_5_,p_199332_6_,p_199332_7_,p_199332_8_,p_199332_9_ net/minecraft/client/renderer/model/FaceBakery.makeQuadVertexData(Lnet/minecraft/client/renderer/model/BlockFaceUV;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/util/EnumFacing;[FLnet/minecraftforge/common/model/ITransformation;Lnet/minecraft/client/renderer/model/BlockPartRotation;Z)[I=|p_188012_1_,p_188012_2_,p_188012_3_,p_188012_4_,p_188012_5_,p_188012_6_,p_188012_7_ diff --git a/src/test/java/net/minecraftforge/debug/client/model/ModelDataTest.java b/src/test/java/net/minecraftforge/debug/client/model/ModelDataTest.java new file mode 100644 index 000000000..07743e8b3 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/client/model/ModelDataTest.java @@ -0,0 +1,184 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2019. + * + * 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.debug.client.model; + +import java.util.List; +import java.util.Random; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ItemOverrideList; +import net.minecraft.client.renderer.model.ModelResourceLocation; +import net.minecraft.client.renderer.texture.MissingTextureSprite; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ITickable; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.IWorldReader; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.model.ModelDataManager; +import net.minecraftforge.client.model.data.IDynamicBakedModel; +import net.minecraftforge.client.model.data.IModelData; +import net.minecraftforge.client.model.data.ModelDataMap; +import net.minecraftforge.client.model.data.ModelProperty; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +@Mod(ModelDataTest.MODID) +public class ModelDataTest +{ + public static final String MODID = "forgedebugmodeldata"; + public static final String VERSION = "1.0"; + + private static final ModelProperty MAGIC_PROP = new ModelProperty(); + + public ModelDataTest() + { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + modEventBus.addGenericListener(Block.class, this::registerBlocks); + modEventBus.addGenericListener(TileEntityType.class, this::registerTileEntities); + MinecraftForge.EVENT_BUS.addListener(this::modelBake); + } + + public void modelBake(ModelBakeEvent event) + { + final IBakedModel stone = event.getModelRegistry().get(new ModelResourceLocation("minecraft:stone")); + final IBakedModel dirt = event.getModelRegistry().get(new ModelResourceLocation("minecraft:dirt")); + final IBakedModel old = event.getModelRegistry().get(new ModelResourceLocation("forge:modeltest")); + event.getModelRegistry().put(new ModelResourceLocation("forge:modeltest"), new IDynamicBakedModel() + { + @Override + public boolean isGui3d() + { + return false; + } + + @Override + public boolean isBuiltInRenderer() + { + return false; + } + + @Override + public boolean isAmbientOcclusion() + { + return true; + } + + @Override + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData modelData) + { + return modelData.getData(MAGIC_PROP) ? stone.getQuads(state, side, rand, modelData) : dirt.getQuads(state, side, rand, modelData); + } + + @Override + public TextureAtlasSprite getParticleTexture() + { + return MissingTextureSprite.getSprite(); + } + + @Override + public ItemOverrideList getOverrides() + { + return null; + } + + @Override + @Nonnull + public IModelData getModelData(@Nonnull IWorldReader world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull IModelData tileData) + { + if (world.getBlockState(pos.down()).getBlock() == Blocks.AIR) + { + tileData.setData(MAGIC_PROP, !tileData.getData(MAGIC_PROP)); + } + return tileData; + } + }); + } + + private static final TileEntityType TILE_TYPE = TileEntityType.Builder.create(Tile::new).build(null); + + private static class Tile extends TileEntity implements ITickable + { + + public Tile() + { + super(TILE_TYPE); + } + + private int counter; + + @Override + public void tick() + { + if (world.isRemote && counter++ == 100) + { + ModelDataManager.requestModelDataRefresh(this); + world.markBlockRangeForRenderUpdate(getPos(), getPos()); + } + } + + @Override + public IModelData getModelData() + { + return new ModelDataMap.Builder().withInitial(MAGIC_PROP, counter < 100).build(); + } + } + + public void registerBlocks(RegistryEvent.Register event) + { + event.getRegistry().register(new BlockContainer(Block.Properties.create(Material.ROCK)) + { + + @Override + @Nullable + public TileEntity createNewTileEntity(IBlockReader worldIn) + { + return new Tile(); + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) + { + return EnumBlockRenderType.MODEL; + } + }.setRegistryName("forge:modeltest")); + } + + public void registerTileEntities(RegistryEvent.Register> event) + { + event.getRegistry().register(TILE_TYPE.setRegistryName("forge:modeltest")); + } +}