From dd365d72202c3a5f1a4172dcc4393a075bcaf585 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Mon, 29 Jun 2015 01:19:10 +0300 Subject: [PATCH] Model pipeline system. Should replace all ad-hoc quad generation methods in forge, and make IBakedModel -> WorldRenderer data transfer faster. Added IVertexConsumer + helper classes; lighting that works correctly for non-axis-aligned faces using the new infrastructure. Changed smooth lighting algorithm, now it should work correctly for everything. New block lighter can be disabled in the forge config options. --- .../BlockRendererDispatcher.java.patch | 9 + .../client/renderer/WorldRenderer.java.patch | 17 + .../renderer/block/model/BakedQuad.java.patch | 13 + .../block/model/FaceBakery.java.patch | 12 +- .../renderer/entity/RenderItem.java.patch | 33 +- .../client/ForgeHooksClient.java | 15 +- .../client/model/Attributes.java | 8 + .../client/model/BlockStateLoader.java | 3 - .../client/model/ForgeBlockStateV1.java | 3 +- .../client/model/IModelState.java | 2 - .../client/model/ISmartVariant.java | 1 - .../client/model/ItemLayerModel.java | 91 +++-- .../client/model/ModelFluid.java | 97 ++---- .../client/model/ModelLoader.java | 12 +- .../client/model/ModelLoaderRegistry.java | 3 - .../client/model/MultiModel.java | 6 +- .../client/model/b3d/B3DLoader.java | 81 ++--- .../client/model/pipeline/BlockInfo.java | 200 +++++++++++ .../pipeline/ForgeBlockModelRenderer.java | 107 ++++++ .../model/pipeline/IVertexConsumer.java | 23 ++ .../model/pipeline/IVertexProducer.java | 10 + .../client/model/pipeline/LightUtil.java | 311 ++++++++++++++++++ .../pipeline/QuadGatheringTransformer.java | 43 +++ .../model/pipeline/TransformerConsumer.java | 27 ++ .../model/pipeline/UnpackedBakedQuad.java | 154 +++++++++ .../model/pipeline/VertexLighterFlat.java | 226 +++++++++++++ .../model/pipeline/VertexLighterSmoothAo.java | 160 +++++++++ .../model/pipeline/WorldRendererConsumer.java | 49 +++ .../common/ForgeModContainer.java | 6 + 29 files changed, 1518 insertions(+), 204 deletions(-) create mode 100644 patches/minecraft/net/minecraft/client/renderer/WorldRenderer.java.patch create mode 100644 patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java.patch create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/BlockInfo.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/IVertexConsumer.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/IVertexProducer.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/LightUtil.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/QuadGatheringTransformer.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/TransformerConsumer.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/UnpackedBakedQuad.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterFlat.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterSmoothAo.java create mode 100644 src/main/java/net/minecraftforge/client/model/pipeline/WorldRendererConsumer.java diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch index 2eef166b2..e52101bb6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch @@ -1,5 +1,14 @@ --- ../src-base/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java +++ ../src-work/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java +@@ -24,7 +24,7 @@ + { + private BlockModelShapes field_175028_a; + private final GameSettings field_175026_b; +- private final BlockModelRenderer field_175027_c = new BlockModelRenderer(); ++ private final BlockModelRenderer field_175027_c = new net.minecraftforge.client.model.pipeline.ForgeBlockModelRenderer(); + private final ChestRenderer field_175024_d = new ChestRenderer(); + private final BlockFluidRenderer field_175025_e = new BlockFluidRenderer(); + private static final String __OBFID = "CL_00002520"; @@ -49,6 +49,24 @@ { p_175020_1_ = block.func_176221_a(p_175020_1_, p_175020_4_, p_175020_2_); diff --git a/patches/minecraft/net/minecraft/client/renderer/WorldRenderer.java.patch b/patches/minecraft/net/minecraft/client/renderer/WorldRenderer.java.patch new file mode 100644 index 000000000..6d9a3666c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/WorldRenderer.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/WorldRenderer.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/WorldRenderer.java +@@ -520,6 +520,14 @@ + } + } + ++ public void checkAndGrow() ++ { ++ if (this.field_179008_i >= this.field_179009_s - this.field_179011_q.func_177338_f()) ++ { ++ this.func_178983_e(2097152); ++ } ++ } ++ + @SideOnly(Side.CLIENT) + public class State + { diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java.patch b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java.patch new file mode 100644 index 000000000..656fce0df --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java.patch @@ -0,0 +1,13 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java +@@ -5,8 +5,9 @@ + import net.minecraftforge.fml.relauncher.SideOnly; + + @SideOnly(Side.CLIENT) +-public class BakedQuad ++public class BakedQuad implements net.minecraftforge.client.model.pipeline.IVertexProducer + { ++ @Override public void pipe(net.minecraftforge.client.model.pipeline.IVertexConsumer consumer) { net.minecraftforge.client.model.pipeline.LightUtil.putBakedQuad(consumer, this); } + protected final int[] field_178215_a; + protected final int field_178213_b; + protected final EnumFacing field_178214_c; diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java.patch b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java.patch index 94750e3cc..a015f515f 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java.patch @@ -14,7 +14,13 @@ EnumFacing enumfacing1 = func_178410_a(aint); if (p_178414_8_) -@@ -40,11 +45,16 @@ +@@ -35,16 +40,22 @@ + this.func_178408_a(aint, enumfacing1); + } + ++ net.minecraftforge.client.ForgeHooksClient.fillNormal(aint, enumfacing1); + return new BakedQuad(aint, p_178414_3_.field_178245_c, enumfacing1); + } private int[] func_178405_a(BlockPartFace p_178405_1_, TextureAtlasSprite p_178405_2_, EnumFacing p_178405_3_, float[] p_178405_4_, ModelRotation p_178405_5_, BlockPartRotation p_178405_6_, boolean p_178405_7_, boolean p_178405_8_) { @@ -32,7 +38,7 @@ } return aint; -@@ -90,12 +100,17 @@ +@@ -90,12 +101,17 @@ private void func_178402_a(int[] p_178402_1_, int p_178402_2_, EnumFacing p_178402_3_, BlockPartFace p_178402_4_, float[] p_178402_5_, TextureAtlasSprite p_178402_6_, ModelRotation p_178402_7_, BlockPartRotation p_178402_8_, boolean p_178402_9_, boolean p_178402_10_) { @@ -52,7 +58,7 @@ this.func_178404_a(p_178402_1_, k, p_178402_2_, vector3d, j, p_178402_6_, p_178402_4_.field_178243_e); } -@@ -156,14 +171,19 @@ +@@ -156,14 +172,19 @@ public int func_178415_a(Vector3d p_178415_1_, EnumFacing p_178415_2_, int p_178415_3_, ModelRotation p_178415_4_, boolean p_178415_5_) { diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch index 1614e2a3d..220d22619 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch @@ -9,17 +9,16 @@ this.func_175041_b(); } -@@ -212,6 +212,9 @@ - private void func_175033_a(WorldRenderer p_175033_1_, BakedQuad p_175033_2_, int p_175033_3_) - { - p_175033_1_.func_178981_a(p_175033_2_.func_178209_a()); -+ if(p_175033_2_ instanceof net.minecraftforge.client.model.IColoredBakedQuad) -+ net.minecraftforge.client.ForgeHooksClient.putQuadColor(p_175033_1_, p_175033_2_, p_175033_3_); -+ else - p_175033_1_.func_178968_d(p_175033_3_); - this.func_175038_a(p_175033_1_, p_175033_2_); - } -@@ -302,6 +305,10 @@ +@@ -222,7 +222,7 @@ + BakedQuad bakedquad; + int j; + +- for (Iterator iterator = p_175032_2_.iterator(); iterator.hasNext(); this.func_175033_a(p_175032_1_, bakedquad, j)) ++ for (Iterator iterator = p_175032_2_.iterator(); iterator.hasNext(); net.minecraftforge.client.model.pipeline.LightUtil.renderQuadColor(p_175032_1_, bakedquad, j)) + { + bakedquad = (BakedQuad)iterator.next(); + j = p_175032_3_; +@@ -302,6 +302,10 @@ modelresourcelocation = new ModelResourceLocation("bow_pulling_0", "inventory"); } } @@ -30,7 +29,7 @@ if (modelresourcelocation != null) { -@@ -314,6 +321,11 @@ +@@ -314,6 +318,11 @@ protected void func_175034_a(ItemTransformVec3f p_175034_1_) { @@ -42,7 +41,7 @@ if (p_175034_1_ != ItemTransformVec3f.field_178366_a) { GlStateManager.func_179109_b(p_175034_1_.field_178365_c.x + field_175055_b, p_175034_1_.field_178365_c.y + field_175056_c, p_175034_1_.field_178365_c.z + field_175053_d); -@@ -335,23 +347,7 @@ +@@ -335,23 +344,7 @@ GlStateManager.func_179120_a(770, 771, 1, 0); GlStateManager.func_179094_E(); @@ -67,7 +66,7 @@ this.func_180454_a(p_175040_1_, p_175040_2_); GlStateManager.func_179121_F(); -@@ -374,7 +370,7 @@ +@@ -374,7 +367,7 @@ GlStateManager.func_179112_b(770, 771); GlStateManager.func_179131_c(1.0F, 1.0F, 1.0F, 1.0F); this.func_180452_a(p_175042_2_, p_175042_3_, ibakedmodel.func_177556_c()); @@ -76,7 +75,7 @@ this.func_180454_a(p_175042_1_, ibakedmodel); GlStateManager.func_179118_c(); GlStateManager.func_179101_C(); -@@ -485,10 +481,11 @@ +@@ -485,10 +478,11 @@ GlStateManager.func_179126_j(); } @@ -91,7 +90,7 @@ GlStateManager.func_179140_f(); GlStateManager.func_179097_i(); GlStateManager.func_179090_x(); -@@ -501,7 +498,7 @@ +@@ -501,7 +495,7 @@ this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 13, 2, 0); this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 12, 1, i1); this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, j1, 1, l); @@ -100,7 +99,7 @@ GlStateManager.func_179141_d(); GlStateManager.func_179098_w(); GlStateManager.func_179145_e(); -@@ -1072,12 +1069,26 @@ +@@ -1072,12 +1066,26 @@ this.func_175029_a(Blocks.field_150420_aW, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "brown_mushroom_block"); this.func_175029_a(Blocks.field_150419_aX, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "red_mushroom_block"); this.func_175031_a(Blocks.field_150380_bt, "dragon_egg"); diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index af85aad8d..64b008d8a 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -52,6 +52,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumWorldBlockLayer; import net.minecraft.util.IRegistry; import net.minecraft.util.MovingObjectPosition; @@ -84,6 +85,7 @@ import org.lwjgl.opengl.GL11; //import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*; //import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*; + import com.google.common.collect.Maps; public class ForgeHooksClient @@ -682,7 +684,7 @@ public class ForgeHooksClient int ncg = Math.min(0xFF, (int)(cg * vcg / 0xFF)); int ncb = Math.min(0xFF, (int)(cb * vcb / 0xFF)); int nca = Math.min(0xFF, (int)(ca * vca / 0xFF)); - renderer.putColorRGBA(renderer.getColorIndex(i + 1), ncr, ncg, ncb, nca); + renderer.putColorRGBA(renderer.getColorIndex(4 - i), ncr, ncg, ncb, nca); } } @@ -710,4 +712,15 @@ public class ForgeHooksClient { tileItemMap.put(Pair.of(item, metadata), TileClass); } + + public static void fillNormal(int[] faceData, EnumFacing facing) + { + int x = ((byte)(facing.getFrontOffsetX() * 127)) & 0xFF; + int y = ((byte)(facing.getFrontOffsetY() * 127)) & 0xFF; + int z = ((byte)(facing.getFrontOffsetZ() * 127)) & 0xFF; + for(int i = 0; i < 4; i++) + { + faceData[i * 7 + 6] = x | (y << 0x08) | (z << 0x10); + } + } } diff --git a/src/main/java/net/minecraftforge/client/model/Attributes.java b/src/main/java/net/minecraftforge/client/model/Attributes.java index 056b98115..67f5b8949 100644 --- a/src/main/java/net/minecraftforge/client/model/Attributes.java +++ b/src/main/java/net/minecraftforge/client/model/Attributes.java @@ -74,6 +74,10 @@ public class Attributes return true; } + /** + * @deprecated use UnpackedBakedQuad.Builder + */ + @Deprecated public static void put(ByteBuffer buf, VertexFormatElement e, boolean denormalize, Number fill, Number... ns) { if(e.getElementCount() > ns.length && fill == null) throw new IllegalArgumentException("not enough elements"); @@ -109,6 +113,10 @@ public class Attributes } } + /** + * @deprecated use IVertexConsumer + */ + @Deprecated public static BakedQuad transform(TRSRTransformation transform, BakedQuad quad, VertexFormat format) { for (VertexFormatElement e : (List)format.getElements()) diff --git a/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java b/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java index a69a2c377..186f9e1d7 100644 --- a/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java +++ b/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java @@ -6,11 +6,8 @@ import java.io.InputStreamReader; import java.io.Reader; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import javax.vecmath.Matrix4f; - import net.minecraft.client.renderer.block.model.ModelBlockDefinition; import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.util.ResourceLocation; diff --git a/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java b/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java index 630c7ef32..59598c289 100644 --- a/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java +++ b/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java @@ -12,7 +12,6 @@ import java.util.Map; import java.util.Map.Entry; import javax.vecmath.AxisAngle4d; -import javax.vecmath.AxisAngle4f; import javax.vecmath.Matrix4f; import javax.vecmath.Quat4f; import javax.vecmath.Vector3f; @@ -21,8 +20,8 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformT import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.util.JsonUtils; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.client.model.BlockStateLoader.SubModel; import net.minecraftforge.client.model.BlockStateLoader.Marker; +import net.minecraftforge.client.model.BlockStateLoader.SubModel; import net.minecraftforge.fml.common.FMLLog; import com.google.common.base.Optional; diff --git a/src/main/java/net/minecraftforge/client/model/IModelState.java b/src/main/java/net/minecraftforge/client/model/IModelState.java index 927d135c3..34795854b 100644 --- a/src/main/java/net/minecraftforge/client/model/IModelState.java +++ b/src/main/java/net/minecraftforge/client/model/IModelState.java @@ -1,7 +1,5 @@ package net.minecraftforge.client.model; -import javax.vecmath.Matrix4f; - import com.google.common.base.Function; /* diff --git a/src/main/java/net/minecraftforge/client/model/ISmartVariant.java b/src/main/java/net/minecraftforge/client/model/ISmartVariant.java index 58d1711f0..8a03b4a3f 100644 --- a/src/main/java/net/minecraftforge/client/model/ISmartVariant.java +++ b/src/main/java/net/minecraftforge/client/model/ISmartVariant.java @@ -1,6 +1,5 @@ package net.minecraftforge.client.model; -import net.minecraft.client.renderer.block.model.ModelBlock; public interface ISmartVariant { diff --git a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java index c2eaf5350..6a875f430 100644 --- a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java +++ b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java @@ -1,12 +1,12 @@ package net.minecraftforge.client.model; -import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import javax.vecmath.Matrix4f; +import javax.vecmath.Vector4f; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; @@ -14,19 +14,17 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformT import net.minecraft.client.renderer.block.model.ModelBlock; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import org.apache.commons.lang3.tuple.Pair; -import org.lwjgl.BufferUtils; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; public class ItemLayerModel implements IRetexturableModel { @@ -90,13 +88,7 @@ public class ItemLayerModel implements IRetexturableModel { for(int i = 0; i < textures.size(); i++) { TextureAtlasSprite sprite = bakedTextureGetter.apply(textures.get(i)); - builder.addAll(Iterables.transform(getQuadsForSprite(i, sprite, format), new Function() - { - public BakedQuad apply(BakedQuad input) - { - return Attributes.transform(transform, input, format); - } - })); + builder.addAll(getQuadsForSprite(i, sprite, format, transform)); } TextureAtlasSprite particle = bakedTextureGetter.apply(textures.isEmpty() ? new ResourceLocation("missingno") : textures.get(0)); if(state instanceof IPerspectiveState) @@ -146,21 +138,18 @@ public class ItemLayerModel implements IRetexturableModel { { TRSRTransformation tr = transforms.get(cameraTransformType); Matrix4f mat = null; - if(tr != null && tr != TRSRTransformation.identity()) mat = tr.blockCornerToCenter(tr).getMatrix(); + if(tr != null && tr != TRSRTransformation.identity()) mat = TRSRTransformation.blockCornerToCenter(tr).getMatrix(); return Pair.of((IBakedModel)this, mat); } } - public ImmutableList getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format) + public ImmutableList getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format, TRSRTransformation transform) { ImmutableList.Builder builder = ImmutableList.builder(); int uMax = sprite.getIconWidth(); int vMax = sprite.getIconHeight(); - ByteBuffer buf = BufferUtils.createByteBuffer(4 * format.getNextOffset()); - int[] data; - for(int f = 0; f < sprite.getFrameCount(); f++) { int[] pixels = sprite.getFrameTextureData(f)[0]; @@ -175,26 +164,26 @@ public class ItemLayerModel implements IRetexturableModel { boolean t = isTransparent(pixels, uMax, vMax, u, v); if(ptu && !t) // left - transparent, right - opaque { - builder.add(buildSideQuad(buf, format, EnumFacing.WEST, tint, sprite, u, v)); + builder.add(buildSideQuad(format, transform, EnumFacing.WEST, tint, sprite, u, v)); } if(!ptu && t) // left - opaque, right - transparent { - builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, u, v)); + builder.add(buildSideQuad(format, transform, EnumFacing.EAST, tint, sprite, u, v)); } if(ptv[u] && !t) // up - transparent, down - opaque { - builder.add(buildSideQuad(buf, format, EnumFacing.UP, tint, sprite, u, v)); + builder.add(buildSideQuad(format, transform, EnumFacing.UP, tint, sprite, u, v)); } if(!ptv[u] && t) // up - opaque, down - transparent { - builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, v)); + builder.add(buildSideQuad(format, transform, EnumFacing.DOWN, tint, sprite, u, v)); } ptu = t; ptv[u] = t; } if(!ptu) // last - opaque { - builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, uMax, v)); + builder.add(buildSideQuad(format, transform, EnumFacing.EAST, tint, sprite, uMax, v)); } } // last line @@ -202,19 +191,19 @@ public class ItemLayerModel implements IRetexturableModel { { if(!ptv[u]) { - builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, vMax)); + builder.add(buildSideQuad(format, transform, EnumFacing.DOWN, tint, sprite, u, vMax)); } } } // front - builder.add(buildQuad(buf, format, EnumFacing.SOUTH, tint, + builder.add(buildQuad(format, transform, EnumFacing.SOUTH, tint, 0, 0, 7.5f / 16f, sprite.getMinU(), sprite.getMaxV(), 0, 1, 7.5f / 16f, sprite.getMinU(), sprite.getMinV(), 1, 1, 7.5f / 16f, sprite.getMaxU(), sprite.getMinV(), 1, 0, 7.5f / 16f, sprite.getMaxU(), sprite.getMaxV() )); // back - builder.add(buildQuad(buf, format, EnumFacing.NORTH, tint, + builder.add(buildQuad(format, transform, EnumFacing.NORTH, tint, 0, 0, 8.5f / 16f, sprite.getMinU(), sprite.getMaxV(), 1, 0, 8.5f / 16f, sprite.getMaxU(), sprite.getMaxV(), 1, 1, 8.5f / 16f, sprite.getMaxU(), sprite.getMinV(), @@ -228,7 +217,7 @@ public class ItemLayerModel implements IRetexturableModel { return (pixels[u + (vMax - 1 - v) * uMax] >> 24 & 0xFF) == 0; } - private static BakedQuad buildSideQuad(ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v) + private static BakedQuad buildSideQuad(VertexFormat format, TRSRTransformation transform, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v) { float x0 = (float)u / sprite.getIconWidth(); float y0 = (float)v / sprite.getIconHeight(); @@ -256,7 +245,7 @@ public class ItemLayerModel implements IRetexturableModel { float v0 = 16f * (1f - y0 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); float v1 = 16f * (1f - y1 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); return buildQuad( - buf, format, side.getOpposite(), tint, // getOpposite is related either to the swapping of V direction, or something else + format, transform, side.getOpposite(), tint, // getOpposite is related either to the swapping of V direction, or something else x0, y0, z1, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0), x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), x1, y1, z2, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), @@ -265,48 +254,50 @@ public class ItemLayerModel implements IRetexturableModel { } private static final BakedQuad buildQuad( - ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, + VertexFormat format, TRSRTransformation transform, EnumFacing side, int tint, float x0, float y0, float z0, float u0, float v0, float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3) { - buf.clear(); - putVertex(buf, format, side, x0, y0, z0, u0, v0); - putVertex(buf, format, side, x1, y1, z1, u1, v1); - putVertex(buf, format, side, x2, y2, z2, u2, v2); - putVertex(buf, format, side, x3, y3, z3, u3, v3); - buf.flip(); - int[] data = new int[4 * format.getNextOffset() / 4]; - buf.asIntBuffer().get(data); - return new BakedQuad(data, tint, side); + UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format); + builder.setQuadTint(tint); + builder.setQuadOrientation(side); + putVertex(builder, format, transform, side, x0, y0, z0, u0, v0); + putVertex(builder, format, transform, side, x1, y1, z1, u1, v1); + putVertex(builder, format, transform, side, x2, y2, z2, u2, v2); + putVertex(builder, format, transform, side, x3, y3, z3, u3, v3); + return builder.build(); } - private static void put(ByteBuffer buf, VertexFormatElement e, Float... fs) + private static void putVertex(UnpackedBakedQuad.Builder builder, VertexFormat format, TRSRTransformation transform, EnumFacing side, float x, float y, float z, float u, float v) { - Attributes.put(buf, e, true, 0f, fs); - } - - private static void putVertex(ByteBuffer buf, VertexFormat format, EnumFacing side, float x, float y, float z, float u, float v) - { - for(VertexFormatElement e : (List)format.getElements()) + Vector4f vec = new Vector4f(); + for(int e = 0; e < format.getElementCount(); e++) { - switch(e.getUsage()) + switch(format.getElement(e).getUsage()) { case POSITION: - put(buf, e, x, y, z, 1f); + vec.x = x; + vec.y = y; + vec.z = z; + vec.w = 1; + transform.getMatrix().transform(vec); + builder.put(e, vec.x, vec.y, vec.z, vec.w); break; case COLOR: - put(buf, e, 1f, 1f, 1f, 1f); + builder.put(e, 1f, 1f, 1f, 1f); break; - case UV: - put(buf, e, u, v, 0f, 1f); + case UV: if(format.getElement(e).getIndex() == 0) + { + builder.put(e, u, v, 0f, 1f); break; + } case NORMAL: - put(buf, e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f); + builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f); break; default: - put(buf, e); + builder.put(e); break; } } diff --git a/src/main/java/net/minecraftforge/client/model/ModelFluid.java b/src/main/java/net/minecraftforge/client/model/ModelFluid.java index b367379f2..21b29f9fd 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelFluid.java +++ b/src/main/java/net/minecraftforge/client/model/ModelFluid.java @@ -1,6 +1,5 @@ package net.minecraftforge.client.model; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; @@ -11,21 +10,20 @@ import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.util.EnumFacing; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.pipeline.LightUtil; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.fluids.BlockFluidBase; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fml.common.FMLLog; -import org.lwjgl.BufferUtils; - import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -115,9 +113,6 @@ public class ModelFluid implements IModelCustomData this.gas = gas; this.state = stateOption; - ByteBuffer buf = BufferUtils.createByteBuffer(4 * format.getNextOffset()); - int[] data; - faceQuads = Maps.newEnumMap(EnumFacing.class); for(EnumFacing side : EnumFacing.values()) { @@ -157,36 +152,34 @@ public class ModelFluid implements IModelCustomData float s = MathHelper.sin(flow) * scale; EnumFacing side = gas ? EnumFacing.DOWN : EnumFacing.UP; - buf.clear(); + UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format); + builder.setQuadOrientation(side); + builder.setQuadColored(); for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1)) { putVertex( - buf, side, + builder, side, x[i], y[i], z[i], topSprite.getInterpolatedU(8 + c * (x[i] * 2 - 1) + s * (z[i] * 2 - 1)), topSprite.getInterpolatedV(8 + c * (x[(i + 1) % 4] * 2 - 1) + s * (z[(i + 1) % 4] * 2 - 1))); } - buf.flip(); - data = new int[4 * format.getNextOffset() / 4]; - buf.asIntBuffer().get(data); - faceQuads.put(side, ImmutableList.of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, side))); + faceQuads.put(side, ImmutableList.of(builder.build())); // bottom side = side.getOpposite(); - buf.clear(); + builder = new UnpackedBakedQuad.Builder(format); + builder.setQuadOrientation(side); + builder.setQuadColored(); for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1)) { putVertex( - buf, side, + builder, side, z[i], gas ? 1 : 0, x[i], still.getInterpolatedU(z[i] * 16), still.getInterpolatedV(x[i] * 16)); } - buf.flip(); - data = new int[4 * format.getNextOffset() / 4]; - buf.asIntBuffer().get(data); - faceQuads.put(side, ImmutableList.of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, side))); + faceQuads.put(side, ImmutableList.of(builder.build())); // sides @@ -197,22 +190,21 @@ public class ModelFluid implements IModelCustomData for(int k = 0; k < 2; k++) { - buf.clear(); + builder = new UnpackedBakedQuad.Builder(format); + builder.setQuadOrientation(side); + builder.setQuadColored(); for(int j = 0; j < 4; j++) { int l = (k * 3) + (1 - 2 * k) * j; float yl = z[l] * y[(i + x[l]) % 4]; if(gas && z[l] == 0) yl = 1; putVertex( - buf, side, + builder, side, x[(i + x[l]) % 4], yl, z[(i + x[l]) % 4], flowing.getInterpolatedU(x[l] * 8), flowing.getInterpolatedV((gas ? yl : 1 - yl) * 8)); } - buf.flip(); - data = new int[4 * format.getNextOffset() / 4]; - buf.asIntBuffer().get(data); - q[k] = new IColoredBakedQuad.ColoredBakedQuad(data, -1, side); + q[k] = builder.build(); } faceQuads.put(side, ImmutableList.of(q[0], q[1])); } @@ -221,70 +213,49 @@ public class ModelFluid implements IModelCustomData { // 1 quad for inventory - buf.clear(); + UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format); + builder.setQuadOrientation(EnumFacing.UP); + builder.setQuadColored(); for(int i = 0; i < 4; i++) { putVertex( - buf, EnumFacing.UP, + builder, EnumFacing.UP, z[i], x[i], 0, still.getInterpolatedU(x[i] * 16), still.getInterpolatedV(z[i] * 16)); } - buf.flip(); - data = new int[4 * format.getNextOffset() / 4]; - buf.asIntBuffer().get(data); - faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, EnumFacing.UP))); + faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(builder.build())); } } - private void put(ByteBuffer buf, VertexFormatElement e, Float... fs) + private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, float x, float y, float z, float u, float v) { - Attributes.put(buf, e, true, 0f, fs); - } - - private float diffuse(EnumFacing side) - { - switch(side) - { - case DOWN: - return .5f; - case UP: - return 1f; - case NORTH: - case SOUTH: - return .8f; - default: - return .6f; - } - } - - private void putVertex(ByteBuffer buf, EnumFacing side, float x, float y, float z, float u, float v) - { - for(VertexFormatElement e : (List)format.getElements()) + for(int e = 0; e < format.getElementCount(); e++) { // TODO transformation - switch(e.getUsage()) + switch(format.getElement(e).getUsage()) { case POSITION: - put(buf, e, x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1f); + builder.put(e, x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1f); break; case COLOR: - // temporarily add diffuse lighting - float d = diffuse(side); - put(buf, e, + float d = LightUtil.diffuseLight(side); + builder.put(e, d * ((color >> 16) & 0xFF) / 255f, d * ((color >> 8) & 0xFF) / 255f, d * (color & 0xFF) / 255f, ((color >> 24) & 0xFF) / 255f); break; - case UV: - put(buf, e, u, v, 0f, 1f); + case UV: if(format.getElement(e).getIndex() == 0) + { + builder.put(e, u, v, 0f, 1f); break; + } case NORMAL: - put(buf, e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetX(), (float)side.getFrontOffsetX(), 0f); + builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f); break; default: - put(buf, e); + builder.put(e); break; } } diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index 7efdcff3a..eac24211a 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -5,7 +5,6 @@ import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -35,6 +34,7 @@ import net.minecraft.client.renderer.block.statemap.IStateMapper; import net.minecraft.client.renderer.texture.IIconCreator; 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.resources.IResourceManager; import net.minecraft.client.resources.model.BuiltInModel; @@ -47,18 +47,13 @@ import net.minecraft.item.Item; import net.minecraft.util.EnumFacing; import net.minecraft.util.IRegistry; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.client.event.TextureStitchEvent; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLLog; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.registry.GameData; import net.minecraftforge.fml.common.registry.RegistryDelegate; import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.Level; import com.google.common.base.Function; -import com.google.common.base.Functions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -123,7 +118,7 @@ public class ModelLoader extends ModelBakery return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString()); } }; - IFlexibleBakedModel missingBaked = missingModel.bake(missingModel.getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter); + IFlexibleBakedModel missingBaked = missingModel.bake(missingModel.getDefaultState(), DefaultVertexFormats.ITEM, textureGetter); for (Entry e : stateModels.entrySet()) { if(e.getValue() == getMissingModel()) @@ -132,7 +127,7 @@ public class ModelLoader extends ModelBakery } else { - bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter)); + bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), DefaultVertexFormats.ITEM, textureGetter)); } } return bakedRegistry; @@ -328,6 +323,7 @@ public class ModelLoader extends ModelBakery return new ItemLayerModel(model).bake(perState, format, bakedTextureGetter); } if(isCustomRenderer(model)) return new IFlexibleBakedModel.Wrapper(new BuiltInModel(transforms), format); + // TODO perspective awareness for this return bakeNormal(model, state.apply(this), format, bakedTextureGetter, state instanceof UVLock); } diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java index 608e304ca..cf7f86e1c 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -1,6 +1,5 @@ package net.minecraftforge.client.model; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; @@ -18,8 +17,6 @@ import net.minecraftforge.fml.common.FMLLog; import org.apache.logging.log4j.Level; -import com.google.common.base.Throwables; - /* * Central hub for custom model loaders. */ diff --git a/src/main/java/net/minecraftforge/client/model/MultiModel.java b/src/main/java/net/minecraftforge/client/model/MultiModel.java index 207eb0124..221398e62 100644 --- a/src/main/java/net/minecraftforge/client/model/MultiModel.java +++ b/src/main/java/net/minecraftforge/client/model/MultiModel.java @@ -8,15 +8,15 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import org.apache.commons.lang3.tuple.Pair; - import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; + +import org.apache.commons.lang3.tuple.Pair; + import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; 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 d66996dbd..5335c67f3 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -2,7 +2,6 @@ package net.minecraftforge.client.model.b3d; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -13,6 +12,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import javax.vecmath.Matrix4f; +import javax.vecmath.Vector3f; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -20,15 +20,12 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.client.model.Attributes; -import net.minecraftforge.client.model.IColoredBakedQuad.ColoredBakedQuad; import net.minecraftforge.client.model.ICustomModelLoader; import net.minecraftforge.client.model.IFlexibleBakedModel; import net.minecraftforge.client.model.IModel; @@ -44,7 +41,6 @@ import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.TRSRTransformation; import net.minecraftforge.client.model.b3d.B3DModel.Animation; -import net.minecraftforge.client.model.b3d.B3DModel.Bone; import net.minecraftforge.client.model.b3d.B3DModel.Face; import net.minecraftforge.client.model.b3d.B3DModel.IKind; import net.minecraftforge.client.model.b3d.B3DModel.Key; @@ -52,13 +48,14 @@ 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.pipeline.LightUtil; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.fml.common.FMLLog; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; -import org.lwjgl.BufferUtils; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -68,7 +65,6 @@ 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.Multimap; /* * Loader for Blitz3D models. @@ -481,7 +477,6 @@ public class B3DLoader implements ICustomModelLoader private final VertexFormat format; private final ImmutableMap textures; - private final ByteBuffer buf; private ImmutableList quads; private static final int BYTES_IN_INT = Integer.SIZE / Byte.SIZE; @@ -493,8 +488,6 @@ public class B3DLoader implements ICustomModelLoader this.state = state; this.format = format; this.textures = textures; - - buf = BufferUtils.createByteBuffer(VERTICES_IN_QUAD * format.getNextOffset()); } public List getFaceQuads(EnumFacing side) @@ -517,7 +510,7 @@ public class B3DLoader implements ICustomModelLoader builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads()); } } - Multimap>> weightMap = mesh.getKind().getWeightMap(); + mesh.getKind().getWeightMap(); Collection faces = mesh.getKind().getFaces(); faces = mesh.getKind().bake(new Function, Matrix4f>() { @@ -529,85 +522,77 @@ public class B3DLoader implements ICustomModelLoader }); for(Face f : faces) { - buf.clear(); + UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format); + quadBuilder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z)); + quadBuilder.setQuadColored(); List textures = null; if(f.getBrush() != null) textures = f.getBrush().getTextures(); TextureAtlasSprite sprite; if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno"); else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.instance; else sprite = this.textures.get(textures.get(0).getPath()); - putVertexData(f.getV1(), sprite); - putVertexData(f.getV2(), sprite); - putVertexData(f.getV3(), sprite); - putVertexData(f.getV3(), sprite); - buf.flip(); - int[] data = new int[VERTICES_IN_QUAD * format.getNextOffset() / BYTES_IN_INT]; - buf.asIntBuffer().get(data); - builder.add(new ColoredBakedQuad(data, -1, EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z))); + putVertexData(quadBuilder, f.getV1(), f.getNormal(), sprite); + putVertexData(quadBuilder, f.getV2(), f.getNormal(), sprite); + putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite); + putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite); + builder.add(quadBuilder.build()); } quads = builder.build(); } return quads; } - private void put(VertexFormatElement e, Float... fs) - { - Attributes.put(buf, e, true, 0f, fs); - } - - @SuppressWarnings("unchecked") - private final void putVertexData(Vertex v, TextureAtlasSprite sprite) + private final void putVertexData(UnpackedBakedQuad.Builder builder, Vertex v, Vector3f faceNormal, TextureAtlasSprite sprite) { // TODO handle everything not handled (texture transformations, bones, transformations, normals, e.t.c) - int oldPos = buf.position(); - Number[] ns = new Number[16]; - for(int i = 0; i < ns.length; i++) ns[i] = 0f; - for(VertexFormatElement e : (List)format.getElements()) + for(int e = 0; e < format.getElementCount(); e++) { - switch(e.getUsage()) + switch(format.getElement(e).getUsage()) { case POSITION: - put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1f); + builder.put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1); break; case COLOR: + float d = LightUtil.diffuseLight(faceNormal.x, faceNormal.y, faceNormal.z); if(v.getColor() != null) { - put(e, v.getColor().x, v.getColor().y, v.getColor().z, v.getColor().w); + builder.put(e, d * v.getColor().x, d * v.getColor().y, d * v.getColor().z, v.getColor().w); } else { - put(e, 1f, 1f, 1f, 1f); + builder.put(e, d, d, d, 1); } break; case UV: // TODO handle more brushes - if(e.getIndex() < v.getTexCoords().length) + if(format.getElement(e).getIndex() < v.getTexCoords().length) { - put(e, + builder.put(e, sprite.getInterpolatedU(v.getTexCoords()[0].x * 16), sprite.getInterpolatedV(v.getTexCoords()[0].y * 16), - 0f, - 1f + 0, + 1 ); } else { - put(e, 0f, 0f, 0f, 1f); + builder.put(e, 0, 0, 0, 1); } break; case NORMAL: - // TODO - put(e, 0f, 1f, 0f, 1f); - break; - case GENERIC: - // TODO - put(e, 0f, 0f, 0f, 0f); + if(v.getNormal() != null) + { + builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 1); + } + else + { + builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 1); + } break; default: - break; + builder.put(e); } } - buf.position(oldPos + format.getNextOffset()); } public boolean isAmbientOcclusion() diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/BlockInfo.java b/src/main/java/net/minecraftforge/client/model/pipeline/BlockInfo.java new file mode 100644 index 000000000..620510f71 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/BlockInfo.java @@ -0,0 +1,200 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.block.Block; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.MathHelper; +import net.minecraft.world.IBlockAccess; + +public class BlockInfo +{ + private IBlockAccess world; + private Block block; + private BlockPos blockPos; + + private final boolean[][][] translucent = new boolean[3][3][3]; + private final int[][][] s = new int[3][3][3]; + private final int[][][] b = new int[3][3][3]; + private final float[][][][] skyLight = new float[3][2][2][2]; + private final float[][][][] blockLight = new float[3][2][2][2]; + private final float[][][] ao = new float[3][3][3]; + + private float shx = 0, shy = 0, shz = 0; + + private int cachedTint = -1; + private int cachedMultiplier = -1; + + public int getColorMultiplier(int tint) + { + if(cachedTint == tint) return cachedMultiplier; + return block.colorMultiplier(world, blockPos, tint); + } + + public void updateShift() + { + shx = shy = shz = 0; + long rand = 0; + // FIXME + switch(block.getOffsetType()) + { + case XYZ: + rand = MathHelper.getPositionRandom(blockPos); + shy = ((float)((rand >> 20) & 0xF) / 0xF - 1) * .2f; + case XZ: + shx = ((float)((rand >> 16) & 0xF) / 0xF - .5f) * .5f; + shz = ((float)((rand >> 24) & 0xF) / 0xF - .5f) * .5f; + default: + } + } + + public void setWorld(IBlockAccess world) + { + this.world = world; + cachedTint = -1; + cachedMultiplier = -1; + } + + public void setBlock(Block block) + { + this.block = block; + cachedTint = -1; + cachedMultiplier = -1; + } + + public void setBlockPos(BlockPos blockPos) + { + this.blockPos = blockPos; + cachedTint = -1; + cachedMultiplier = -1; + } + + private float combine(int c, int s1, int s2, int s3) + { + if(c == 0) c = Math.max(0, Math.max(s1, s2) - 1); + if(s1 == 0) s1 = Math.max(0, c - 1); + if(s2 == 0) s2 = Math.max(0, c - 1); + if(s3 == 0) s3 = Math.max(0, Math.max(s1, s2) - 1); + return (float)(c + s1 + s2 + s3) * 0x20 / (4 * 0xFFFF); + } + + public void updateLightMatrix() + { + boolean full = false; + for(int x = 0; x <= 2; x++) + { + for(int y = 0; y <= 2; y++) + { + for(int z = 0; z <= 2; z++) + { + BlockPos pos = blockPos.add(x - 1, y - 1, z - 1); + Block block = world.getBlockState(pos).getBlock(); + translucent[x][y][z] = block.isTranslucent(); + //translucent[x][y][z] = world.getBlockState(pos).getBlock().getLightOpacity(world, pos) == 0; + int brightness = this.block.getMixedBrightnessForBlock(world, pos); + s[x][y][z] = (brightness >> 0x14) & 0xF; + b[x][y][z] = (brightness >> 0x04) & 0xF; + ao[x][y][z] = block.getAmbientOcclusionLightValue(); + if(x == 1 && y == 1 && z == 1) + { + full = block.isFullCube(); + } + } + } + } + if(!full) + { + for(EnumFacing side : EnumFacing.values()) + { + int x = side.getFrontOffsetX() + 1; + int y = side.getFrontOffsetY() + 1; + int z = side.getFrontOffsetZ() + 1; + s[x][y][z] = Math.max(s[1][1][1] - 1, s[x][y][z]); + b[x][y][z] = Math.max(b[1][1][1] - 1, b[x][y][z]); + } + } + for(int x = 0; x < 2; x++) + { + for(int y = 0; y < 2; y++) + { + for(int z = 0; z < 2; z++) + { + int x1 = x * 2; + int y1 = y * 2; + int z1 = z * 2; + + boolean tx = translucent[x1][1][z1] || translucent[x1][y1][1]; + skyLight[0][x][y][z] = combine(s[x1][1][1], s[x1][1][z1], s[x1][y1][1], tx ? s[x1][y1][z1] : s[x1][1][1]); + blockLight[0][x][y][z] = combine(b[x1][1][1], b[x1][1][z1], b[x1][y1][1], tx ? b[x1][y1][z1] : b[x1][1][1]); + + boolean ty = translucent[x1][y1][1] || translucent[1][y1][z1]; + skyLight[1][x][y][z] = combine(s[1][y1][1], s[x1][y1][1], s[1][y1][z1], ty ? s[x1][y1][z1] : s[1][y1][1]); + blockLight[1][x][y][z] = combine(b[1][y1][1], b[x1][y1][1], b[1][y1][z1], ty ? b[x1][y1][z1] : b[1][y1][1]); + + boolean tz = translucent[1][y1][z1] || translucent[1][y1][z1]; + skyLight[2][x][y][z] = combine(s[1][1][z1], s[1][y1][z1], s[x1][1][z1], tz ? s[x1][y1][z1] : s[1][1][z1]); + blockLight[2][x][y][z] = combine(b[1][1][z1], b[1][y1][z1], b[x1][1][z1], tz ? b[x1][y1][z1] : b[1][1][z1]); + } + } + } + } + + public IBlockAccess getWorld() + { + return world; + } + + public Block getBlock() + { + return block; + } + + public BlockPos getBlockPos() + { + return blockPos; + } + + public boolean[][][] getTranslucent() + { + return translucent; + } + + public float[][][][] getSkyLight() + { + return skyLight; + } + + public float[][][][] getBlockLight() + { + return blockLight; + } + + public float[][][] getAo() + { + return ao; + } + + public float getShx() + { + return shx; + } + + public float getShy() + { + return shy; + } + + public float getShz() + { + return shz; + } + + public int getCachedTint() + { + return cachedTint; + } + + public int getCachedMultiplier() + { + return cachedMultiplier; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer.java b/src/main/java/net/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer.java new file mode 100644 index 000000000..7c8e63893 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer.java @@ -0,0 +1,107 @@ +package net.minecraftforge.client.model.pipeline; + +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.BlockModelRenderer; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.resources.model.IBakedModel; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.common.ForgeModContainer; + +public class ForgeBlockModelRenderer extends BlockModelRenderer +{ + private final ThreadLocal lighterFlat = new ThreadLocal() + { + @Override + protected VertexLighterFlat initialValue() + { + return new VertexLighterFlat(); + } + }; + private final ThreadLocal lighterSmooth = new ThreadLocal() + { + @Override + protected VertexLighterSmoothAo initialValue() + { + return new VertexLighterSmoothAo(); + } + }; + + private final ThreadLocal lastRendererFlat = new ThreadLocal(); + private final ThreadLocal lastRendererSmooth = new ThreadLocal(); + + @Override + public boolean renderModelStandard(IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides) + { + if(ForgeModContainer.forgeLightPipelineEnabled) + { + if(wr != lastRendererFlat.get()) + { + lastRendererFlat.set(wr); + lighterFlat.get().setParent(new WorldRendererConsumer(wr)); + } + return render(lighterFlat.get(), world, model, block, pos, wr, checkSides); + } + else + { + return super.renderModelStandard(world, model, block, pos, wr, checkSides); + } + } + + @Override + public boolean renderModelAmbientOcclusion(IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides) + { + if(ForgeModContainer.forgeLightPipelineEnabled) + { + if(wr != lastRendererSmooth.get()) + { + lastRendererSmooth.set(wr); + lighterSmooth.get().setParent(new WorldRendererConsumer(wr)); + } + return render(lighterSmooth.get(), world, model, block, pos, wr, checkSides); + } + else + { + return super.renderModelAmbientOcclusion(world, model, block, pos, wr, checkSides); + } + } + + public static boolean render(VertexLighterFlat lighter, IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides) + { + lighter.setWorld(world); + lighter.setBlock(block); + lighter.setBlockPos(pos); + boolean empty = true; + List quads = model.getGeneralQuads(); + if(!quads.isEmpty()) + { + lighter.updateBlockInfo(); + empty = false; + for(BakedQuad quad : quads) + { + quad.pipe(lighter); + } + } + for(EnumFacing side : EnumFacing.values()) + { + quads = model.getFaceQuads(side); + if(!quads.isEmpty()) + { + if(!checkSides || block.shouldSideBeRendered(world, pos.offset(side), side)) + { + if(empty) lighter.updateBlockInfo(); + empty = false; + for(BakedQuad quad : quads) + { + quad.pipe(lighter); + } + } + } + } + return !empty; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/IVertexConsumer.java b/src/main/java/net/minecraftforge/client/model/pipeline/IVertexConsumer.java new file mode 100644 index 000000000..92cf355ca --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/IVertexConsumer.java @@ -0,0 +1,23 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; + +/** + * Assumes that the data length is not less than e.getElementCount(). + * Also assumes that element index passed will increment from 0 to format.getElementCount() - 1. + * Normal, Color and UV are assumed to be in 0-1 range. + */ +public interface IVertexConsumer +{ + /** + * @return the format that should be used for passed data. + */ + VertexFormat getVertexFormat(); + + void setQuadTint(int tint); + void setQuadOrientation(EnumFacing orientation); + void setQuadColored(); + + void put(int element, float... data); +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/IVertexProducer.java b/src/main/java/net/minecraftforge/client/model/pipeline/IVertexProducer.java new file mode 100644 index 000000000..54233748c --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/IVertexProducer.java @@ -0,0 +1,10 @@ +package net.minecraftforge.client.model.pipeline; + + +public interface IVertexProducer +{ + /** + * @param consumer consumer to receive the vertex data this producer can provide + */ + void pipe(IVertexConsumer consumer); +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/LightUtil.java b/src/main/java/net/minecraftforge/client/model/pipeline/LightUtil.java new file mode 100644 index 000000000..dd7baffdb --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/LightUtil.java @@ -0,0 +1,311 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.block.model.BakedQuad; +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; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.client.model.IColoredBakedQuad; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +public class LightUtil +{ + public static float diffuseLight(float x, float y, float z) + { + float s2 = (float)Math.pow(2, .5); + float y1 = y + 3 - 2 * s2; + return (x * x * 0.6f + (y1 * y1 * (3 + 2 * s2)) / 8 + z * z * 0.8f); + } + + public static float diffuseLight(EnumFacing side) + { + switch(side) + { + case DOWN: + return .5f; + case UP: + return 1f; + case NORTH: + case SOUTH: + return .8f; + default: + return .6f; + } + } + + public static EnumFacing toSide(float x, float y, float z) + { + if(Math.abs(x) > Math.abs(y)) + { + if(Math.abs(x) > Math.abs(z)) + { + if(x < 0) return EnumFacing.WEST; + return EnumFacing.EAST; + } + else + { + if(z < 0) return EnumFacing.NORTH; + return EnumFacing.SOUTH; + } + } + else + { + if(Math.abs(y) > Math.abs(z)) + { + if(y < 0) return EnumFacing.DOWN; + return EnumFacing.UP; + } + else + { + if(z < 0) return EnumFacing.NORTH; + return EnumFacing.SOUTH; + } + } + } + + private static final LoadingCache formatMaps = CacheBuilder.newBuilder() + .maximumSize(10) + .build(new CacheLoader() + { + public int[] load(VertexFormat format) + { + return mapFormats(format, DefaultVertexFormats.ITEM); + } + }); + + public static void putBakedQuad(IVertexConsumer consumer, BakedQuad quad) + { + consumer.setQuadOrientation(quad.getFace()); + if(quad.hasTintIndex()) + { + consumer.setQuadTint(quad.getTintIndex()); + } + if(quad instanceof IColoredBakedQuad) + { + consumer.setQuadColored(); + } + //int[] eMap = mapFormats(consumer.getVertexFormat(), DefaultVertexFormats.ITEM); + float[] data = new float[4]; + int[] eMap = formatMaps.getUnchecked(consumer.getVertexFormat()); + for(int v = 0; v < 4; v++) + { + for(int e = 0; e < consumer.getVertexFormat().getElementCount(); e++) + { + if(eMap[e] != DefaultVertexFormats.ITEM.getElementCount()) + { + unpack(quad.getVertexData(), data, DefaultVertexFormats.ITEM, v, eMap[e]); + consumer.put(e, data); + } + else + { + consumer.put(e); + } + } + } + } + + public static int[] mapFormats(VertexFormat from, VertexFormat to) + { + int[] eMap = new int[from.getElementCount()]; + + for(int e = 0; e < from.getElementCount(); e++) + { + VertexFormatElement expected = from.getElement(e); + int e2; + for(e2 = 0; e2 < to.getElementCount(); e2++) + { + VertexFormatElement current = to.getElement(e2); + if(expected.getUsage() == current.getUsage() && expected.getIndex() == current.getIndex()) + { + break; + } + } + eMap[e] = e2; + } + return eMap; + } + + public static void unpack(int[] from, float[] to, VertexFormat formatFrom, int v, int e) + { + VertexFormatElement element = formatFrom.getElement(e); + for(int i = 0; i < 4; i++) + { + if(i < element.getElementCount()) + { + int pos = v * formatFrom.getNextOffset() + element.getOffset() + element.getType().getSize() * i; + int index = pos >> 2; + int offset = pos & 3; + int bits = from[index]; + bits = bits >>> (offset * 8); + if((pos + element.getType().getSize() - 1) / 4 != index) + { + bits |= from[index + 1] << ((4 - offset) * 8); + } + int mask = (256 << (8 * (element.getType().getSize() - 1))) - 1; + bits &= mask; + switch(element.getType()) + { + case FLOAT: + to[i] = Float.intBitsToFloat(bits); + break; + case UBYTE: + case USHORT: + to[i] = (float)bits / mask; + break; + case UINT: + to[i] = (float)((double)(bits & 0xFFFFFFFFL) / 0xFFFFFFFFL); + break; + case BYTE: + to[i] = ((float)(byte)bits) / mask * 2; + break; + case SHORT: + to[i] = ((float)(short)bits) / mask * 2; + break; + case INT: + to[i] = ((float)(bits & 0xFFFFFFFFL)) / 0xFFFFFFFFL * 2; + break; + } + } + else + { + to[i] = 0; + } + } + } + + public static void pack(float[] from, int[] to, VertexFormat formatTo, int v, int e) + { + VertexFormatElement element = formatTo.getElement(e); + for(int i = 0; i < 4; i++) + { + if(i < element.getElementCount()) + { + int pos = v * formatTo.getNextOffset() + element.getOffset() + element.getType().getSize() * i; + int index = pos >> 2; + int offset = pos & 3; + int bits = 0; + int mask = (256 << (8 * (element.getType().getSize() - 1))) - 1; + switch(element.getType()) + { + case FLOAT: + bits = Float.floatToRawIntBits(from[i]); + break; + case UBYTE: + case USHORT: + case UINT: + bits = (int)(from[i] * mask); + break; + case BYTE: + case SHORT: + case INT: + bits = (int)(from[i] * mask / 2); + break; + } + to[index] &= ~(mask << (offset * 8)); + to[index] |= (((bits & mask) << (offset * 8))); + // TODO handle overflow into to[index + 1] + } + } + } + + private static IVertexConsumer tessellator = null; + public static IVertexConsumer getTessellator() + { + if(tessellator == null) + { + Tessellator tes = Tessellator.getInstance(); + WorldRenderer wr = tes.getWorldRenderer(); + tessellator = new WorldRendererConsumer(wr); + } + return tessellator; + } + + private static ItemConsumer itemConsumer = null; + public static ItemConsumer getItemConsumer() + { + if(itemConsumer == null) + { + itemConsumer = new ItemConsumer(getTessellator()); + } + return itemConsumer; + } + + public static void renderQuadColor(WorldRenderer wr, BakedQuad quad, int auxColor) + { + ItemConsumer cons; + if(wr == Tessellator.getInstance().getWorldRenderer()) + { + cons = getItemConsumer(); + } + else + { + cons = new ItemConsumer(new WorldRendererConsumer(wr)); + } + float r = (float)(auxColor & 0xFF) / 0xFF; + float g = (float)((auxColor >>> 8) & 0xFF) / 0xFF; + float b = (float)((auxColor >>> 16) & 0xFF) / 0xFF; + float a = (float)((auxColor >>> 24) & 0xFF) / 0xFF; + cons.setAuxColor(r, g, b, a); + quad.pipe(cons); + } + + public static class ItemConsumer extends VertexTransformer + { + private boolean colored = false; + private int vertices = 0; + + private float[] auxColor = new float[]{1, 1, 1, 1}; + private float[] buf = new float[4]; + + public ItemConsumer(IVertexConsumer parent) + { + super(parent); + } + + public void setAuxColor(float... auxColor) + { + System.arraycopy(auxColor, 0, this.auxColor, 0, this.auxColor.length); + } + + @Override + public void setQuadColored() + { + colored = true; + } + + public void put(int element, float... data) + { + if(getVertexFormat().getElement(element).getUsage() == EnumUsage.COLOR) + { + System.arraycopy(auxColor, 0, buf, 0, buf.length); + if(colored) + { + for(int i = 0; i < 4; i++) + { + buf[i] *= data[i]; + } + } + super.put(element, buf); + } + else + { + super.put(element, data); + } + if(element == getVertexFormat().getElementCount() - 1) + { + vertices++; + if(vertices == 4) + { + vertices = 0; + colored = false; + } + } + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/QuadGatheringTransformer.java b/src/main/java/net/minecraftforge/client/model/pipeline/QuadGatheringTransformer.java new file mode 100644 index 000000000..0edc74313 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/QuadGatheringTransformer.java @@ -0,0 +1,43 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.client.renderer.vertex.VertexFormat; + +public abstract class QuadGatheringTransformer implements IVertexConsumer +{ + protected IVertexConsumer parent; + protected VertexFormat format; + protected int vertices = 0; + + protected float[][][] quadData = null; + + public void setParent(IVertexConsumer parent) + { + this.parent = parent; + } + + public void setVertexFormat(VertexFormat format) + { + this.format = format; + quadData = new float[format.getElementCount()][4][4]; + } + + @Override + public VertexFormat getVertexFormat() + { + return format; + } + + @Override + public void put(int element, float... data) + { + System.arraycopy(data, 0, quadData[element][vertices], 0, data.length); + if(element == getVertexFormat().getElementCount() - 1) vertices++; + if(vertices == 4) + { + vertices = 0; + processQuad(); + } + } + + protected abstract void processQuad(); +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/TransformerConsumer.java b/src/main/java/net/minecraftforge/client/model/pipeline/TransformerConsumer.java new file mode 100644 index 000000000..dbaf31d88 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/TransformerConsumer.java @@ -0,0 +1,27 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.client.renderer.vertex.VertexFormat; + +public abstract class TransformerConsumer implements IVertexConsumer { + private IVertexConsumer parent; + + protected TransformerConsumer(IVertexConsumer parent) + { + this.parent = parent; + } + + @Override + public VertexFormat getVertexFormat() + { + return parent.getVertexFormat(); + } + + @Override + public void put(int element, float... data) + { + float[] newData = transform(element, data); + parent.put(element, newData); + } + + protected abstract float[] transform(int element, float... data); +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/UnpackedBakedQuad.java b/src/main/java/net/minecraftforge/client/model/pipeline/UnpackedBakedQuad.java new file mode 100644 index 000000000..430207d08 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/UnpackedBakedQuad.java @@ -0,0 +1,154 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.client.model.IColoredBakedQuad; + +// advantages: non-fixed-length vertex format, no overhead of packing and unpacking attributes to transform the model +// disadvantages: (possibly) larger memory footprint, overhead on packing the attributes at the final rendering stage +public class UnpackedBakedQuad extends BakedQuad +{ + protected final float[][][] unpackedData; + protected final VertexFormat format; + protected boolean packed = false; + + public UnpackedBakedQuad(float[][][] unpackedData, int tint, EnumFacing orientation, VertexFormat format) + { + super(new int[format.getNextOffset() /* / 4 * 4 */], tint, orientation); + this.unpackedData = unpackedData; + this.format = format; + } + + @Override + public int[] getVertexData() + { + if(!packed) + { + packed = true; + for(int v = 0; v < 4; v++) + { + for(int e = 0; e < format.getElementCount(); e++) + { + LightUtil.pack(unpackedData[v][e], vertexData, format, v, e); + } + } + } + return vertexData; + } + + @Override + public void pipe(IVertexConsumer consumer) + { + int[] eMap = LightUtil.mapFormats(consumer.getVertexFormat(), format); + + if(hasTintIndex()) + { + consumer.setQuadTint(getTintIndex()); + } + consumer.setQuadOrientation(getFace()); + if(this instanceof IColoredBakedQuad) + { + consumer.setQuadColored(); + } + for(int v = 0; v < 4; v++) + { + for(int e = 0; e < consumer.getVertexFormat().getElementCount(); e++) + { + if(eMap[e] != format.getElementCount()) + { + consumer.put(e, unpackedData[v][eMap[e]]); + } + else + { + consumer.put(e); + } + } + } + } + + public static class Colored extends UnpackedBakedQuad implements IColoredBakedQuad + { + public Colored(float[][][] unpackedData, int tint, EnumFacing orientation, VertexFormat format) + { + super(unpackedData, tint, orientation, format); + } + } + + public static class Builder implements IVertexConsumer + { + private final VertexFormat format; + private final float[][][] unpackedData; + private int tint = -1; + private EnumFacing orientation; + private boolean isColored = false; + + private int vertices = 0; + private int elements = 0; + private boolean full = false; + + public Builder(VertexFormat format) + { + this.format = format; + unpackedData = new float[4][format.getElementCount()][4]; + } + + public VertexFormat getVertexFormat() + { + return format; + } + + public void setQuadTint(int tint) + { + this.tint = tint; + } + + public void setQuadOrientation(EnumFacing orientation) + { + this.orientation = orientation; + } + + public void setQuadColored() + { + this.isColored = true; + } + + public void put(int element, float... data) + { + for(int i = 0; i < 4; i++) + { + if(i < data.length) + { + unpackedData[vertices][element][i] = data[i]; + } + else + { + unpackedData[vertices][element][i] = 0; + } + } + elements++; + if(elements == format.getElementCount()) + { + vertices++; + elements = 0; + } + if(vertices == 4) + { + full = true; + } + } + + public UnpackedBakedQuad build() + { + if(!full) + { + throw new IllegalStateException("not enough data"); + } + if(isColored) + { + return new Colored(unpackedData, tint, orientation, format); + } + return new UnpackedBakedQuad(unpackedData, tint, orientation, format); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterFlat.java b/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterFlat.java new file mode 100644 index 000000000..692ba6e92 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterFlat.java @@ -0,0 +1,226 @@ +package net.minecraftforge.client.model.pipeline; + +import javax.vecmath.Vector3f; + +import com.google.common.base.Objects; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.IBlockAccess; + +public class VertexLighterFlat extends QuadGatheringTransformer +{ + protected final BlockInfo blockInfo = new BlockInfo(); + private int tint = -1; + + protected int posIndex = -1; + protected int normalIndex = -1; + protected int colorIndex = -1; + protected int lightmapIndex = -1; + + @Override + public void setParent(IVertexConsumer parent) + { + super.setParent(parent); + if(Objects.equal(getVertexFormat(), parent.getVertexFormat())) return; + setVertexFormat(getVertexFormat(parent)); + for(int i = 0; i < getVertexFormat().getElementCount(); i++) + { + switch(getVertexFormat().getElement(i).getUsage()) + { + case POSITION: + posIndex = i; + break; + case NORMAL: + normalIndex = i; + break; + case COLOR: + colorIndex = i; + break; + case UV: + if(getVertexFormat().getElement(i).getIndex() == 1) + { + lightmapIndex = i; + } + break; + default: + } + } + if(posIndex == -1) + { + throw new IllegalArgumentException("vertex lighter needs format with position"); + } + if(lightmapIndex == -1) + { + throw new IllegalArgumentException("vertex lighter needs format with lightmap"); + } + if(colorIndex == -1) + { + throw new IllegalArgumentException("vertex lighter needs format with color"); + } + } + + private static VertexFormat getVertexFormat(IVertexConsumer parent) + { + VertexFormat format = parent.getVertexFormat(); + if(format.hasNormal()) return format; + format = new VertexFormat(format); + format.setElement(new VertexFormatElement(0, VertexFormatElement.EnumType.FLOAT, VertexFormatElement.EnumUsage.NORMAL, 4)); + return format; + } + + @Override + protected void processQuad() + { + float[][] position = quadData[posIndex]; + float[][] normal = null; + float[][] lightmap = quadData[lightmapIndex]; + float[][] color = quadData[colorIndex]; + + if(normalIndex != -1 && ( + quadData[normalIndex][0][0] != -1 || + quadData[normalIndex][0][1] != -1 || + quadData[normalIndex][0][2] != -1)) + { + normal = quadData[normalIndex]; + } + else + { + normal = new float[4][4]; + Vector3f v1 = new Vector3f(position[3]); + Vector3f t = new Vector3f(position[1]); + Vector3f v2 = new Vector3f(position[2]); + v1.sub(t); + t.set(position[0]); + v2.sub(t); + v1.cross(v2, v1); + v1.normalize(); + for(int v = 0; v < 4; v++) + { + normal[v][0] = v1.x; + normal[v][1] = v1.y; + normal[v][2] = v1.z; + normal[v][3] = 0; + } + } + + int multiplier = -1; + if(tint != -1) + { + multiplier = blockInfo.getColorMultiplier(tint); + } + + for(int v = 0; v < 4; v++) + { + position[v][0] += blockInfo.getShx(); + position[v][1] += blockInfo.getShy(); + position[v][2] += blockInfo.getShz(); + + float x = position[v][0] - .5f; + float y = position[v][1] - .5f; + float z = position[v][2] - .5f; + + //if(blockInfo.getBlock().isFullCube()) + { + x += normal[v][0] * .5f; + y += normal[v][1] * .5f; + z += normal[v][2] * .5f; + } + + updateLightmap(normal[v], lightmap[v], x, y, z); + updateColor(normal[v], color[v], x, y, z, tint, multiplier); + + // no need for remapping cause all we could've done is add 1 element to the end + for(int e = 0; e < parent.getVertexFormat().getElementCount(); e++) + { + switch(parent.getVertexFormat().getElement(e).getUsage()) + { + case POSITION: + float[] pos = new float[4]; + System.arraycopy(position[v], 0, pos, 0, position[v].length); + pos[0] += (blockInfo.getBlockPos().getX() & 0xF); + pos[1] += (blockInfo.getBlockPos().getY() & 0xF); + pos[2] += (blockInfo.getBlockPos().getZ() & 0xF); + parent.put(e, pos); + break; + case NORMAL: if(normalIndex != -1) + { + parent.put(e, normal[v]); + break; + } + case COLOR: + parent.put(e, color[v]); + break; + case UV: if(getVertexFormat().getElement(e).getIndex() == 1) + { + parent.put(e, lightmap[v]); + break; + } + default: + parent.put(e, quadData[e][v]); + } + } + } + tint = -1; + } + + protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z) + { + float e1 = .5f - 1e4f; + float e2 = 1 - 1e4f; + BlockPos pos = blockInfo.getBlockPos(); + + if(y < -e1 && normal[1] < -e2) pos = pos.down(); + if(y > e1 && normal[1] > e2) pos = pos.up(); + if(z < -e1 && normal[2] < -e2) pos = pos.north(); + if(z > e1 && normal[2] > e2) pos = pos.south(); + if(x < -e1 && normal[0] < -e2) pos = pos.west(); + if(x > e1 && normal[0] > e2) pos = pos.east(); + + int brightness = blockInfo.getBlock().getMixedBrightnessForBlock(blockInfo.getWorld(), pos); + + lightmap[0] = ((float)((brightness >> 0x04) & 0xF) * 0x20) / 0xFFFF; + lightmap[1] = ((float)((brightness >> 0x14) & 0xF) * 0x20) / 0xFFFF; + } + + protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier) + { + if(tint != -1) + { + color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF; + color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF; + color[2] *= (float)(multiplier & 0xFF) / 0xFF; + } + } + + public void setQuadTint(int tint) + { + this.tint = tint; + } + public void setQuadOrientation(EnumFacing orientation) {} + public void setQuadCulled() {} + public void setQuadColored() {} + + public void setWorld(IBlockAccess world) + { + blockInfo.setWorld(world); + } + + public void setBlock(Block block) + { + blockInfo.setBlock(block); + } + + public void setBlockPos(BlockPos blockPos) + { + blockInfo.setBlockPos(blockPos); + } + + public void updateBlockInfo() + { + blockInfo.updateShift(); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterSmoothAo.java b/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterSmoothAo.java new file mode 100644 index 000000000..118d00838 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/VertexLighterSmoothAo.java @@ -0,0 +1,160 @@ +package net.minecraftforge.client.model.pipeline; + +import net.minecraft.util.MathHelper; + + +public class VertexLighterSmoothAo extends VertexLighterFlat +{ + @Override + protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z) + { + lightmap[0] = calcLightmap(blockInfo.getBlockLight(), x, y, z); + lightmap[1] = calcLightmap(blockInfo.getSkyLight(), x, y, z); + } + + @Override + protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier) + { + if(tint != -1) + { + color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF; + color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF; + color[2] *= (float)(multiplier & 0xFF) / 0xFF; + } + float a = getAo(x, y, z); + color[0] *= a; + color[1] *= a; + color[2] *= a; + } + + protected float calcLightmap(float[][][][] light, float x, float y, float z) + { + x *= 2; + y *= 2; + z *= 2; + float l2 = x * x + y * y + z * z; + if(l2 > 6 - 2e-2f) + { + float s = (float)Math.sqrt((6 - 2e-2f) / l2); + x *= s; + y *= s; + z *= s; + } + float ax = Math.abs(x); + float ay = Math.abs(y); + float az = Math.abs(z); + float e1 = 1 + 1e-4f; + if(ax > 2 - 1e-4f && ay <= e1 && az <= e1) + { + x = MathHelper.clamp_float(x, -2 + 1e-4f, 2 - 1e-4f); + } + else if(ay > 2 - 1e-4f && az <= e1 && ax <= e1) + { + y = MathHelper.clamp_float(y, -2 + 1e-4f, 2 - 1e-4f); + } + else if(az > 2 - 1e-4f && ax <= e1 && ay <= e1) + { + z = MathHelper.clamp_float(z, -2 + 1e-4f, 2 - 1e-4f); + } + ax = Math.abs(x); + ay = Math.abs(y); + az = Math.abs(z); + if(ax <= e1 && ay + az > 3f - 1e-4f) + { + float s = (3f - 1e-4f) / (ay + az); + y *= s; + z *= s; + } + else if(ay <= e1 && az + ax > 3f - 1e-4f) + { + float s = (3f - 1e-4f) / (az + ax); + z *= s; + x *= s; + } + else if(az <= e1 && ax + ay > 3f - 1e-4f) + { + float s = (3f - 1e-4f) / (ax + ay); + x *= s; + y *= s; + } + else if(ax + ay + az > 4 - 1e-4f) + { + float s = (4 - 1e-4f) / (ax + ay + az); + x *= s; + y *= s; + z *= s; + } + + float l = 0; + float s = 0; + + for(int ix = 0; ix <= 1; ix++) + { + for(int iy = 0; iy <= 1; iy++) + { + for(int iz = 0; iz <= 1; iz++) + { + float vx = x * (1 - ix * 2); + float vy = y * (1 - iy * 2); + float vz = z * (1 - iz * 2); + + float s3 = vx + vy + vz + 4; + float sx = vy + vz + 3; + float sy = vz + vx + 3; + float sz = vx + vy + 3; + + float bx = (2 * vx + vy + vz + 6) / (s3 * sy * sz * (vx + 2)); + s += bx; + l += bx * light[0][ix][iy][iz]; + + float by = (2 * vy + vz + vx + 6) / (s3 * sz * sx * (vy + 2)); + s += by; + l += by * light[1][ix][iy][iz]; + + float bz = (2 * vz + vx + vy + 6) / (s3 * sx * sy * (vz + 2)); + s += bz; + l += bz * light[2][ix][iy][iz]; + } + } + } + + l /= s; + + if(l > 15f * 0x20 / 0xFFFF) l = 15f * 0x20 / 0xFFFF; + if(l < 0) l = 0; + + return l; + } + + protected float getAo(float x, float y, float z) + { + int sx = x < 0 ? 1 : 2; + int sy = y < 0 ? 1 : 2; + int sz = z < 0 ? 1 : 2; + + if(x < 0) x++; + if(y < 0) y++; + if(z < 0) z++; + + float a = 0; + float[][][] ao = blockInfo.getAo(); + a += ao[sx - 1][sy - 1][sz - 1] * (1 - x) * (1 - y) * (1 - z); + a += ao[sx - 1][sy - 1][sz - 0] * (1 - x) * (1 - y) * (0 + z); + a += ao[sx - 1][sy - 0][sz - 1] * (1 - x) * (0 + y) * (1 - z); + a += ao[sx - 1][sy - 0][sz - 0] * (1 - x) * (0 + y) * (0 + z); + a += ao[sx - 0][sy - 1][sz - 1] * (0 + x) * (1 - y) * (1 - z); + a += ao[sx - 0][sy - 1][sz - 0] * (0 + x) * (1 - y) * (0 + z); + a += ao[sx - 0][sy - 0][sz - 1] * (0 + x) * (0 + y) * (1 - z); + a += ao[sx - 0][sy - 0][sz - 0] * (0 + x) * (0 + y) * (0 + z); + + a = MathHelper.clamp_float(a, 0, 1); + return a; + } + + @Override + public void updateBlockInfo() + { + super.updateBlockInfo(); + blockInfo.updateLightMatrix(); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/pipeline/WorldRendererConsumer.java b/src/main/java/net/minecraftforge/client/model/pipeline/WorldRendererConsumer.java new file mode 100644 index 000000000..fdee4c833 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/pipeline/WorldRendererConsumer.java @@ -0,0 +1,49 @@ +package net.minecraftforge.client.model.pipeline; + +import java.util.Arrays; + +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; + +/** + * Assumes VertexFormatElement is present in the WorlRenderer's vertex format. + */ +public class WorldRendererConsumer implements IVertexConsumer +{ + private final WorldRenderer renderer; + private final int[] quadData; + private int v = 0; + + public WorldRendererConsumer(WorldRenderer renderer) + { + super(); + this.renderer = renderer; + quadData = new int[renderer.getVertexFormat().getNextOffset()/* / 4 * 4 */]; + } + + public VertexFormat getVertexFormat() + { + return renderer.getVertexFormat(); + } + + public void put(int e, float... data) + { + LightUtil.pack(data, quadData, getVertexFormat(), v, e); + if(e == getVertexFormat().getElementCount() - 1) + { + v++; + if(v == 4) + { + renderer.addVertexData(quadData); + renderer.checkAndGrow(); + Arrays.fill(quadData, 0); + v = 0; + } + } + } + + public void setQuadTint(int tint) {} + public void setQuadOrientation(EnumFacing orientation) {} + public void setQuadColored() {} +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index fe56d52e9..710529534 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -70,6 +70,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC public static boolean disableVersionCheck = false; public static int defaultSpawnFuzz = 20; public static boolean defaultHasSpawnFuzz = true; + public static boolean forgeLightPipelineEnabled = true; private static Configuration config; @@ -222,6 +223,11 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC defaultHasSpawnFuzz = prop.getBoolean(Boolean.TRUE); propOrder.add(prop.getName()); + prop = config.get(Configuration.CATEGORY_GENERAL, "forgeLightPipelineEnabled", Boolean.TRUE, + "Enable the forge block rendering pipeline - fixes the lighting of custom models."); + forgeLightPipelineEnabled = prop.getBoolean(Boolean.TRUE); + propOrder.add(prop.getName()); + config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder); if (config.hasChanged())