diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch index 6767785fc3..0f9f5767a2 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch @@ -16,7 +16,18 @@ this.field_184395_f = p_i46552_3_; } -@@ -97,7 +98,7 @@ +@@ -74,6 +75,10 @@ + } + + private void func_191967_a(IBakedModel p_191967_1_, int p_191967_2_, ItemStack p_191967_3_) { ++ if (net.minecraftforge.common.ForgeConfig.CLIENT.allowEmissiveItems.get()) { ++ net.minecraftforge.client.ForgeHooksClient.renderLitItem(this, p_191967_1_, p_191967_2_, p_191967_3_); ++ return; ++ } + Tessellator tessellator = Tessellator.func_178181_a(); + BufferBuilder bufferbuilder = tessellator.func_178180_c(); + bufferbuilder.func_181668_a(7, DefaultVertexFormats.field_176599_b); +@@ -97,7 +102,7 @@ if (p_180454_2_.func_188618_c()) { GlStateManager.func_179131_c(1.0F, 1.0F, 1.0F, 1.0F); GlStateManager.func_179091_B(); @@ -25,7 +36,7 @@ } else { this.func_191961_a(p_180454_2_, p_180454_1_); if (p_180454_1_.func_77962_s()) { -@@ -163,7 +164,7 @@ +@@ -163,7 +168,7 @@ k = k | -16777216; } @@ -34,7 +45,7 @@ } } -@@ -224,11 +225,8 @@ +@@ -224,11 +229,8 @@ GlStateManager.func_179147_l(); GlStateManager.func_187428_a(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); GlStateManager.func_179094_E(); @@ -48,7 +59,7 @@ this.func_180454_a(p_184394_1_, p_184394_2_); GlStateManager.func_187407_a(GlStateManager.CullFace.BACK); -@@ -259,7 +257,7 @@ +@@ -259,7 +261,7 @@ GlStateManager.func_187401_a(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.func_179131_c(1.0F, 1.0F, 1.0F, 1.0F); this.func_180452_a(p_191962_2_, p_191962_3_, p_191962_4_.func_177556_c()); @@ -57,7 +68,7 @@ this.func_180454_a(p_191962_1_, p_191962_4_); GlStateManager.func_179118_c(); GlStateManager.func_179101_C(); -@@ -298,6 +296,7 @@ +@@ -298,6 +300,7 @@ crashreportcategory.func_189529_a("Item Type", () -> { return String.valueOf((Object)p_184391_2_.func_77973_b()); }); @@ -65,7 +76,7 @@ crashreportcategory.func_189529_a("Item Damage", () -> { return String.valueOf(p_184391_2_.func_77952_i()); }); -@@ -329,9 +328,12 @@ +@@ -329,9 +332,12 @@ GlStateManager.func_179147_l(); GlStateManager.func_179145_e(); GlStateManager.func_179126_j(); @@ -79,7 +90,7 @@ GlStateManager.func_179140_f(); GlStateManager.func_179097_i(); GlStateManager.func_179090_x(); -@@ -339,11 +341,9 @@ +@@ -339,11 +345,9 @@ GlStateManager.func_179084_k(); Tessellator tessellator = Tessellator.func_178181_a(); BufferBuilder bufferbuilder = tessellator.func_178180_c(); @@ -94,7 +105,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); GlStateManager.func_179147_l(); -@@ -382,4 +382,9 @@ +@@ -382,4 +386,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 e0c209fa5a..057e7a9b1d 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -29,6 +29,8 @@ import java.io.File; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Random; @@ -47,6 +49,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.async.ThreadNameCachingStrategy; import org.apache.logging.log4j.core.impl.ReusableLogEventFactory; import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; import net.minecraft.block.state.IBlockState; import net.minecraft.client.GameSettings; @@ -62,7 +65,10 @@ import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.ItemRenderer; import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.BlockFaceUV; @@ -79,6 +85,7 @@ import net.minecraft.client.renderer.entity.model.ModelBiped; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage; @@ -119,6 +126,7 @@ import net.minecraftforge.client.event.sound.PlaySoundEvent; import net.minecraftforge.client.model.ModelDynBucket; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.animation.Animation; +import net.minecraftforge.client.model.pipeline.QuadGatheringTransformer; import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.model.IModelPart; @@ -548,6 +556,197 @@ public class ForgeHooksClient } } + private static class LightGatheringTransformer extends QuadGatheringTransformer { + + private static final VertexFormat FORMAT = new VertexFormat().addElement(DefaultVertexFormats.TEX_2F).addElement(DefaultVertexFormats.TEX_2S); + + int blockLight, skyLight; + + { setVertexFormat(FORMAT); } + + boolean hasLighting() + { + return dataLength[1] >= 2; + } + + @Override + protected void processQuad() + { + // Reset light data + blockLight = 0; + skyLight = 0; + // Compute average light for all 4 vertices + for (int i = 0; i < 4; i++) + { + blockLight += (int) ((quadData[1][i][0] * 0xFFFF) / 0x20); + skyLight += (int) ((quadData[1][i][1] * 0xFFFF) / 0x20); + } + // Values must be multiplied by 16, divided by 4 for average => x4 + blockLight *= 4; + skyLight *= 4; + } + + // Dummy overrides + + @Override + public void setQuadTint(int tint) {} + + @Override + public void setQuadOrientation(EnumFacing orientation) {} + + @Override + public void setApplyDiffuseLighting(boolean diffuse) {} + + @Override + public void setTexture(TextureAtlasSprite texture) {} + } + + private static final LightGatheringTransformer lightGatherer = new LightGatheringTransformer(); + + public static void renderLitItem(ItemRenderer ri, IBakedModel model, int color, ItemStack stack) + { + List allquads = new ArrayList<>(); + Random random = new Random(); + long seed = 42L; + + for (EnumFacing enumfacing : EnumFacing.values()) + { + random.setSeed(seed); + allquads.addAll(model.getQuads(null, enumfacing, random)); + } + + random.setSeed(seed); + allquads.addAll(model.getQuads(null, null, random)); + + if (allquads.isEmpty()) return; + + // Current list of consecutive quads with the same lighting + List segment = new ArrayList<>(); + + // Lighting of the current segment + int segmentBlockLight = -1; + int segmentSkyLight = -1; + // Coloring of the current segment + int segmentColorMultiplier = color; + // If the current segment contains lighting data + boolean hasLighting = false; + + // Tint index cache to avoid unnecessary IItemColor lookups + int prevTintIndex = -1; + + for (int i = 0; i < allquads.size(); i++) + { + BakedQuad q = allquads.get(i); + + // Lighting of the current quad + int bl = 0; + int sl = 0; + + // Fail-fast on ITEM, as it cannot have light data + if (q.getFormat() != DefaultVertexFormats.ITEM && q.getFormat().hasUv(1)) + { + q.pipe(lightGatherer); + if (lightGatherer.hasLighting()) + { + bl = lightGatherer.blockLight; + sl = lightGatherer.skyLight; + } + } + + int colorMultiplier = segmentColorMultiplier; + + // If there is no color override, and this quad is tinted, we need to apply IItemColor + if (color == 0xFFFFFFFF && q.hasTintIndex()) + { + int tintIndex = q.getTintIndex(); + + if (prevTintIndex != tintIndex) + { + colorMultiplier = getColorMultiplier(stack, tintIndex); + } + prevTintIndex = tintIndex; + } + else + { + colorMultiplier = color; + prevTintIndex = -1; + } + + boolean lightingDirty = segmentBlockLight != bl || segmentSkyLight != sl; + boolean colorDirty = hasLighting && segmentColorMultiplier != colorMultiplier; + + // If lighting or color data has changed, draw the segment and flush it + if (lightingDirty || colorDirty) + { + if (i > 0) // Make sure this isn't the first quad being processed + { + drawSegment(ri, color, stack, segment, segmentBlockLight, segmentSkyLight, segmentColorMultiplier, lightingDirty && (hasLighting || segment.size() < i), colorDirty); + } + segmentBlockLight = bl; + segmentSkyLight = sl; + segmentColorMultiplier = colorMultiplier; + hasLighting = segmentBlockLight > 0 || segmentSkyLight > 0; + } + + segment.add(q); + } + + drawSegment(ri, color, stack, segment, segmentBlockLight, segmentSkyLight, segmentColorMultiplier, hasLighting || segment.size() < allquads.size(), false); + + // Clean up render state if necessary + if (hasLighting) + { + OpenGlHelper.glMultiTexCoord2f(OpenGlHelper.GL_TEXTURE1, OpenGlHelper.lastBrightnessX, OpenGlHelper.lastBrightnessY); + GL11.glMaterialfv(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, RenderHelper.setColorBuffer(0, 0, 0, 1)); + } + } + + private static void drawSegment(ItemRenderer ri, int baseColor, ItemStack stack, List segment, int bl, int sl, int tintColor, boolean updateLighting, boolean updateColor) + { + BufferBuilder bufferbuilder = Tessellator.getInstance().getBuffer(); + bufferbuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.ITEM); + + float lastBl = OpenGlHelper.lastBrightnessX; + float lastSl = OpenGlHelper.lastBrightnessY; + + if (updateLighting || updateColor) + { + float emissive = Math.max(bl, sl) / 240f; + + float r = (tintColor >>> 16 & 0xff) / 255f; + float g = (tintColor >>> 8 & 0xff) / 255f; + float b = (tintColor & 0xff) / 255f; + + GL11.glMaterialfv(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, RenderHelper.setColorBuffer(emissive * r, emissive * g, emissive * b, 1)); + + if (updateLighting) + { + OpenGlHelper.glMultiTexCoord2f(OpenGlHelper.GL_TEXTURE1, Math.max(bl, lastBl), Math.max(sl, lastSl)); + } + } + + ri.renderQuads(bufferbuilder, segment, baseColor, stack); + Tessellator.getInstance().draw(); + + // Preserve this as it represents the "world" lighting + OpenGlHelper.lastBrightnessX = lastBl; + OpenGlHelper.lastBrightnessY = lastSl; + + segment.clear(); + } + + private static int getColorMultiplier(ItemStack stack, int tintIndex) + { + if (tintIndex == -1 || stack.isEmpty()) return 0xFFFFFFFF; + + int colorMultiplier = Minecraft.getInstance().getItemColors().getColor(stack, tintIndex); + + // Always full opacity + colorMultiplier |= 0xff << 24; // -16777216 + + return colorMultiplier; + } + /** * internal, relies on fixed format of FaceBakery */ diff --git a/src/main/java/net/minecraftforge/common/ForgeConfig.java b/src/main/java/net/minecraftforge/common/ForgeConfig.java index 490ebf9755..c516be7694 100644 --- a/src/main/java/net/minecraftforge/common/ForgeConfig.java +++ b/src/main/java/net/minecraftforge/common/ForgeConfig.java @@ -125,6 +125,8 @@ public class ForgeConfig public final BooleanValue selectiveResourceReloadEnabled; public final BooleanValue showLoadWarnings; + + public final BooleanValue allowEmissiveItems; Client(ForgeConfigSpec.Builder builder) { builder.comment("Client only settings, mostly things related to rendering") @@ -166,6 +168,11 @@ public class ForgeConfig .comment("When enabled, forge will show any warnings that occurred during loading") .translation("forge.configgui.showloadwarnings") .define("showLoadWarnings", true); + + allowEmissiveItems = builder + .comment("Allow item rendering to detect emissive quads and draw them properly. This allows glowing blocks to look the same in item form, but incurs a very slight performance hit.") + .translation("forge.configgui.allowEmissiveItems") + .define("allowEmissiveItems", true); builder.pop(); } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 0c10d21a47..52575b7790 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -90,6 +90,8 @@ public net.minecraft.entity.item.EntityXPOrb field_70530_e # xpValue public net.minecraft.world.gen.structure.StructureVillagePieces$Village # RenderPlayer #public net.minecraft.client.renderer.entity.RenderBiped field_77071_a #modelBipedMain +# RenderItem +public net.minecraft.client.renderer.ItemRenderer func_191970_a(Lnet/minecraft/client/renderer/BufferBuilder;Ljava/util/List;ILnet/minecraft/item/ItemStack;)V # renderQuads # ChunkProviderServer public net.minecraft.world.gen.ChunkProviderServer field_186029_c # chunkGenerator public net.minecraft.world.gen.ChunkProviderServer field_73244_f # loadedChunkHashMap