Implement rendering for item models with emissive quads (#5047)

This commit is contained in:
tterrag 2019-04-12 10:45:32 -04:00
parent 1280dea111
commit 67da3182c1
4 changed files with 227 additions and 8 deletions

View file

@ -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();
}

View file

@ -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<BakedQuad> 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<BakedQuad> 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<BakedQuad> 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
*/

View file

@ -126,6 +126,8 @@ public class ForgeConfig
public final BooleanValue showLoadWarnings;
public final BooleanValue allowEmissiveItems;
Client(ForgeConfigSpec.Builder builder) {
builder.comment("Client only settings, mostly things related to rendering")
.push("client");
@ -167,6 +169,11 @@ public class ForgeConfig
.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();
}
}

View file

@ -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