diff --git a/patches/minecraft/net/minecraft/client/renderer/WorldVertexBufferUploader.java.patch b/patches/minecraft/net/minecraft/client/renderer/WorldVertexBufferUploader.java.patch new file mode 100644 index 0000000000..bf398cbd5a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/WorldVertexBufferUploader.java.patch @@ -0,0 +1,70 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/WorldVertexBufferUploader.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/WorldVertexBufferUploader.java +@@ -29,35 +29,9 @@ + + while (iterator.hasNext()) + { ++ // moved to VertexFormatElement.preDraw + vertexformatelement = (VertexFormatElement)iterator.next(); +- enumusage = vertexformatelement.func_177375_c(); +- k = vertexformatelement.func_177367_b().func_177397_c(); +- int l = vertexformatelement.func_177369_e(); +- +- switch (WorldVertexBufferUploader.SwitchEnumUsage.field_178958_a[enumusage.ordinal()]) +- { +- case 1: +- bytebuffer.position(vertexformatelement.func_177373_a()); +- GL11.glVertexPointer(vertexformatelement.func_177370_d(), k, j, bytebuffer); +- GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); +- break; +- case 2: +- bytebuffer.position(vertexformatelement.func_177373_a()); +- OpenGlHelper.func_77472_b(OpenGlHelper.field_77478_a + l); +- GL11.glTexCoordPointer(vertexformatelement.func_177370_d(), k, j, bytebuffer); +- GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); +- OpenGlHelper.func_77472_b(OpenGlHelper.field_77478_a); +- break; +- case 3: +- bytebuffer.position(vertexformatelement.func_177373_a()); +- GL11.glColorPointer(vertexformatelement.func_177370_d(), k, j, bytebuffer); +- GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); +- break; +- case 4: +- bytebuffer.position(vertexformatelement.func_177373_a()); +- GL11.glNormalPointer(k, j, bytebuffer); +- GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY); +- } ++ vertexformatelement.func_177375_c().preDraw(vertexformatelement, j, bytebuffer); + } + + GL11.glDrawArrays(p_178177_1_.func_178979_i(), 0, p_178177_1_.func_178989_h()); +@@ -65,27 +39,9 @@ + + while (iterator.hasNext()) + { ++ // moved to VertexFormatElement.postDraw + vertexformatelement = (VertexFormatElement)iterator.next(); +- enumusage = vertexformatelement.func_177375_c(); +- k = vertexformatelement.func_177369_e(); +- +- switch (WorldVertexBufferUploader.SwitchEnumUsage.field_178958_a[enumusage.ordinal()]) +- { +- case 1: +- GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); +- break; +- case 2: +- OpenGlHelper.func_77472_b(OpenGlHelper.field_77478_a + k); +- GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); +- OpenGlHelper.func_77472_b(OpenGlHelper.field_77478_a); +- break; +- case 3: +- GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); +- GlStateManager.func_179117_G(); +- break; +- case 4: +- GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY); +- } ++ vertexformatelement.func_177375_c().postDraw(vertexformatelement, j, bytebuffer); + } + } + 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 new file mode 100644 index 0000000000..c9b9efd629 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java.patch @@ -0,0 +1,76 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.java +@@ -22,7 +22,12 @@ + + public BakedQuad func_178414_a(Vector3f p_178414_1_, Vector3f p_178414_2_, BlockPartFace p_178414_3_, TextureAtlasSprite p_178414_4_, EnumFacing p_178414_5_, ModelRotation p_178414_6_, BlockPartRotation p_178414_7_, boolean p_178414_8_, boolean p_178414_9_) + { +- int[] aint = this.func_178405_a(p_178414_3_, p_178414_4_, p_178414_5_, this.func_178403_a(p_178414_1_, p_178414_2_), p_178414_6_, p_178414_7_, p_178414_8_, p_178414_9_); ++ return makeBakedQuad(p_178414_1_, p_178414_2_, p_178414_3_, p_178414_4_, p_178414_5_, (net.minecraftforge.client.model.ITransformation)p_178414_6_, p_178414_7_, p_178414_8_, p_178414_9_); ++ } ++ ++ public BakedQuad makeBakedQuad(Vector3f p_178414_1_, Vector3f p_178414_2_, BlockPartFace p_178414_3_, TextureAtlasSprite p_178414_4_, EnumFacing p_178414_5_, net.minecraftforge.client.model.ITransformation p_178414_6_, BlockPartRotation p_178414_7_, boolean p_178414_8_, boolean p_178414_9_) ++ { ++ int[] aint = this.makeQuadVertexData(p_178414_3_, p_178414_4_, p_178414_5_, this.func_178403_a(p_178414_1_, p_178414_2_), p_178414_6_, p_178414_7_, p_178414_8_, p_178414_9_); + EnumFacing enumfacing1 = func_178410_a(aint); + + if (p_178414_8_) +@@ -40,11 +45,16 @@ + + 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_) + { ++ return makeQuadVertexData(p_178405_1_, p_178405_2_, p_178405_3_, p_178405_4_, (net.minecraftforge.client.model.ITransformation)p_178405_5_, p_178405_6_, p_178405_7_, p_178405_8_); ++ } ++ ++ private int[] makeQuadVertexData(BlockPartFace p_178405_1_, TextureAtlasSprite p_178405_2_, EnumFacing p_178405_3_, float[] p_178405_4_, net.minecraftforge.client.model.ITransformation p_178405_5_, BlockPartRotation p_178405_6_, boolean p_178405_7_, boolean p_178405_8_) ++ { + int[] aint = new int[28]; + + for (int i = 0; i < 4; ++i) + { +- this.func_178402_a(aint, i, p_178405_3_, p_178405_1_, p_178405_4_, p_178405_2_, p_178405_5_, p_178405_6_, p_178405_7_, p_178405_8_); ++ this.fillVertexData(aint, i, p_178405_3_, p_178405_1_, p_178405_4_, p_178405_2_, p_178405_5_, p_178405_6_, p_178405_7_, p_178405_8_); + } + + return aint; +@@ -90,12 +100,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_) + { +- EnumFacing enumfacing1 = p_178402_7_.func_177523_a(p_178402_3_); ++ fillVertexData(p_178402_1_, p_178402_2_, p_178402_3_, p_178402_4_, p_178402_5_, p_178402_6_, (net.minecraftforge.client.model.ITransformation)p_178402_7_, p_178402_8_, p_178402_9_, p_178402_10_); ++ } ++ ++ private void fillVertexData(int[] p_178402_1_, int p_178402_2_, EnumFacing p_178402_3_, BlockPartFace p_178402_4_, float[] p_178402_5_, TextureAtlasSprite p_178402_6_, net.minecraftforge.client.model.ITransformation p_178402_7_, BlockPartRotation p_178402_8_, boolean p_178402_9_, boolean p_178402_10_) ++ { ++ EnumFacing enumfacing1 = p_178402_7_.rotate(p_178402_3_); + int j = p_178402_10_ ? this.func_178413_a(enumfacing1) : -1; + EnumFaceDirection.VertexInformation vertexinformation = EnumFaceDirection.func_179027_a(p_178402_3_).func_179025_a(p_178402_2_); + Vector3d vector3d = new Vector3d((double)p_178402_5_[vertexinformation.field_179184_a], (double)p_178402_5_[vertexinformation.field_179182_b], (double)p_178402_5_[vertexinformation.field_179183_c]); + this.func_178407_a(vector3d, p_178402_8_); +- int k = this.func_178415_a(vector3d, p_178402_3_, p_178402_2_, p_178402_7_, p_178402_9_); ++ int k = this.rotateVertex(vector3d, p_178402_3_, p_178402_2_, p_178402_7_, p_178402_9_); + 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 @@ + + 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_) + { ++ return rotateVertex(p_178415_1_, p_178415_2_, p_178415_3_, (net.minecraftforge.client.model.ITransformation)p_178415_4_, p_178415_5_); ++ } ++ ++ public int rotateVertex(Vector3d p_178415_1_, EnumFacing p_178415_2_, int p_178415_3_, net.minecraftforge.client.model.ITransformation p_178415_4_, boolean p_178415_5_) ++ { + if (p_178415_4_ == ModelRotation.X0_Y0) + { + return p_178415_3_; + } + else + { +- this.func_178406_a(p_178415_1_, new Vector3d(0.5D, 0.5D, 0.5D), p_178415_4_.func_177525_a(), new Vector3d(1.0D, 1.0D, 1.0D)); +- return p_178415_4_.func_177520_a(p_178415_2_, p_178415_3_); ++ this.func_178406_a(p_178415_1_, new Vector3d(0.5D, 0.5D, 0.5D), new Matrix4d(p_178415_4_.getMatrix()), new Vector3d(1.0D, 1.0D, 1.0D)); ++ return p_178415_4_.rotate(p_178415_2_, p_178415_3_); + } + } + diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java.patch b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java.patch new file mode 100644 index 0000000000..48a2d8d092 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java +@@ -8,7 +8,11 @@ + import net.minecraftforge.fml.relauncher.Side; + import net.minecraftforge.fml.relauncher.SideOnly; + ++/* ++ * @deprecated use {@link net.minecraftforge.client.model.IPerspectiveAwareModel} instead ++ */ + @SideOnly(Side.CLIENT) ++@Deprecated + public class ItemCameraTransforms + { + public static final ItemCameraTransforms field_178357_a = new ItemCameraTransforms(ItemTransformVec3f.field_178366_a, ItemTransformVec3f.field_178366_a, ItemTransformVec3f.field_178366_a, ItemTransformVec3f.field_178366_a); diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java.patch b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java.patch new file mode 100644 index 0000000000..ef42bfbc91 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java +@@ -13,9 +13,14 @@ + import net.minecraftforge.fml.relauncher.Side; + import net.minecraftforge.fml.relauncher.SideOnly; + ++/* ++ * @deprecated use {@link net.minecraftforge.client.model.IModelState} and {@link net.minecraftforge.client.model.TRSRTransformation} ++ */ + @SideOnly(Side.CLIENT) +-public class ItemTransformVec3f ++@Deprecated ++public class ItemTransformVec3f implements net.minecraftforge.client.model.IModelState + { ++ public net.minecraftforge.client.model.TRSRTransformation apply(net.minecraftforge.client.model.IModelPart part) { return new net.minecraftforge.client.model.TRSRTransformation(this); } + public static final ItemTransformVec3f field_178366_a = new ItemTransformVec3f(new Vector3f(), new Vector3f(), new Vector3f(1.0F, 1.0F, 1.0F)); + public final Vector3f field_178364_b; + public final Vector3f field_178365_c; 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 deaee40ac0..48162e109d 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.java.patch @@ -20,7 +20,44 @@ if (modelresourcelocation != null) { -@@ -485,10 +489,11 @@ +@@ -314,6 +318,11 @@ + + protected void func_175034_a(ItemTransformVec3f p_175034_1_) + { ++ applyVanillaTransform(p_175034_1_); ++ } ++ ++ public static void applyVanillaTransform(ItemTransformVec3f p_175034_1_) ++ { + 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 +344,7 @@ + GlStateManager.func_179120_a(770, 771, 1, 0); + GlStateManager.func_179094_E(); + +- switch (RenderItem.SwitchTransformType.field_178640_a[p_175040_3_.ordinal()]) +- { +- case 1: +- default: +- break; +- case 2: +- this.func_175034_a(p_175040_2_.func_177552_f().field_178355_b); +- break; +- case 3: +- this.func_175034_a(p_175040_2_.func_177552_f().field_178356_c); +- break; +- case 4: +- this.func_175034_a(p_175040_2_.func_177552_f().field_178353_d); +- break; +- case 5: +- this.func_175034_a(p_175040_2_.func_177552_f().field_178354_e); +- } ++ p_175040_2_ = net.minecraftforge.client.ForgeHooksClient.handleCameraTransforms(p_175040_2_, p_175040_3_); + + this.func_180454_a(p_175040_1_, p_175040_2_); + GlStateManager.func_179121_F(); +@@ -485,10 +478,11 @@ GlStateManager.func_179126_j(); } @@ -35,7 +72,7 @@ GlStateManager.func_179140_f(); GlStateManager.func_179097_i(); GlStateManager.func_179090_x(); -@@ -501,7 +506,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); @@ -44,7 +81,7 @@ GlStateManager.func_179141_d(); GlStateManager.func_179098_w(); GlStateManager.func_179145_e(); -@@ -1078,6 +1083,19 @@ +@@ -1078,6 +1072,19 @@ { this.field_175059_m.func_178085_b(); } diff --git a/patches/minecraft/net/minecraft/client/renderer/vertex/VertexFormatElement.java.patch b/patches/minecraft/net/minecraft/client/renderer/vertex/VertexFormatElement.java.patch new file mode 100644 index 0000000000..5c3f9e29eb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/vertex/VertexFormatElement.java.patch @@ -0,0 +1,49 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/vertex/VertexFormatElement.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/vertex/VertexFormatElement.java +@@ -114,13 +114,20 @@ + @SideOnly(Side.CLIENT) + public static enum EnumType + { +- FLOAT(4, "Float", 5126), +- UBYTE(1, "Unsigned Byte", 5121), +- BYTE(1, "Byte", 5120), +- USHORT(2, "Unsigned Short", 5123), +- SHORT(2, "Short", 5122), +- UINT(4, "Unsigned Int", 5125), +- INT(4, "Int", 5124); ++ FLOAT(4, "Float", org.lwjgl.opengl.GL11.GL_FLOAT), ++ UBYTE(1, "Unsigned Byte", org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE), ++ BYTE(1, "Byte", org.lwjgl.opengl.GL11.GL_BYTE), ++ USHORT(2, "Unsigned Short", org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT), ++ SHORT(2, "Short", org.lwjgl.opengl.GL11.GL_SHORT), ++ UINT(4, "Unsigned Int", org.lwjgl.opengl.GL11.GL_UNSIGNED_INT), ++ INT(4, "Int", org.lwjgl.opengl.GL11.GL_INT); ++ // Commented for now, might be added in the future if anyone needs them ++ //HALF_FLOAT(2, "Half Float", org.lwjgl.opengl.GL30.GL_HALF_FLOAT), ++ //DOUBLE(8, "Double", org.lwjgl.opengl.GL11.GL_DOUBLE), ++ //INT_2_10_10_10_REV(4, "Int 2-10-10-10 reversed", org.lwjgl.opengl.GL33.GL_INT_2_10_10_10_REV), ++ //UINT_2_10_10_10_REV(4, "Unsigned Int 2-10-10-10 reversed", org.lwjgl.opengl.GL12.GL_UNSIGNED_INT_2_10_10_10_REV), ++ //UINT_10F_11F_11F_REV(4, "Unsigned Int 10F 11F 11F reversed", GL_UNSIGNED_INT_10F_11F_11F_REV); ++ + private final int field_177407_h; + private final String field_177408_i; + private final int field_177405_j; +@@ -157,9 +164,17 @@ + NORMAL("Normal"), + COLOR("Vertex Color"), + UV("UV"), ++ // As of 1.8 - unused in vanilla; use GENERIC for now ++ @Deprecated + MATRIX("Bone Matrix"), ++ @Deprecated + BLEND_WEIGHT("Blend Weight"), +- PADDING("Padding"); ++ PADDING("Padding"), ++ GENERIC("Generic Attribute"); ++ ++ public void preDraw(VertexFormatElement element, int stride, java.nio.ByteBuffer buffer) { net.minecraftforge.client.ForgeHooksClient.preDraw(this, element, stride, buffer); } ++ public void postDraw(VertexFormatElement element, int stride, java.nio.ByteBuffer buffer) { net.minecraftforge.client.ForgeHooksClient.postDraw(this, element, stride, buffer); } ++ + private final String field_177392_h; + + private static final String __OBFID = "CL_00002397"; diff --git a/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.java.patch b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.java.patch new file mode 100644 index 0000000000..92a87972d8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/client/resources/model/IBakedModel.java ++++ ../src-work/minecraft/net/minecraft/client/resources/model/IBakedModel.java +@@ -7,7 +7,11 @@ + import net.minecraftforge.fml.relauncher.Side; + import net.minecraftforge.fml.relauncher.SideOnly; + ++/* ++ * @deprecated use {@link net.minecraftforge.client.model.IFlexibleBakedModel}, {@link net.minecraftforge.client.model.IPerspectiveAwareModel} ++ */ + @SideOnly(Side.CLIENT) ++@Deprecated + public interface IBakedModel + { + List func_177551_a(EnumFacing p_177551_1_); +@@ -22,5 +26,6 @@ + + TextureAtlasSprite func_177554_e(); + ++ @Deprecated + ItemCameraTransforms func_177552_f(); + } diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.java.patch b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.java.patch new file mode 100644 index 0000000000..0c6385f13d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.java.patch @@ -0,0 +1,53 @@ +--- ../src-base/minecraft/net/minecraft/client/resources/model/ModelBakery.java ++++ ../src-work/minecraft/net/minecraft/client/resources/model/ModelBakery.java +@@ -123,7 +123,7 @@ + } + catch (Exception exception) + { +- field_177603_c.warn("Unable to load variant: " + modelresourcelocation.func_177518_c() + " from " + modelresourcelocation); ++ field_177603_c.warn("Unable to load variant: " + modelresourcelocation.func_177518_c() + " from " + modelresourcelocation, exception); + } + } + catch (Exception exception1) +@@ -499,6 +499,11 @@ + + private IBakedModel func_177578_a(ModelBlock p_177578_1_, ModelRotation p_177578_2_, boolean p_177578_3_) + { ++ return bakeModel(p_177578_1_, (net.minecraftforge.client.model.ITransformation)p_177578_2_, p_177578_3_); ++ } ++ ++ protected IBakedModel bakeModel(ModelBlock p_177578_1_, net.minecraftforge.client.model.ITransformation p_177578_2_, boolean p_177578_3_) ++ { + TextureAtlasSprite textureatlassprite = (TextureAtlasSprite)this.field_177599_g.get(new ResourceLocation(p_177578_1_.func_178308_c("particle"))); + SimpleBakedModel.Builder builder = (new SimpleBakedModel.Builder(p_177578_1_)).func_177646_a(textureatlassprite); + Iterator iterator = p_177578_1_.func_178298_a().iterator(); +@@ -516,11 +521,11 @@ + + if (blockpartface.field_178244_b == null) + { +- builder.func_177648_a(this.func_177589_a(blockpart, blockpartface, textureatlassprite1, enumfacing, p_177578_2_, p_177578_3_)); ++ builder.func_177648_a(this.makeBakedQuad(blockpart, blockpartface, textureatlassprite1, enumfacing, p_177578_2_, p_177578_3_)); + } + else + { +- builder.func_177650_a(p_177578_2_.func_177523_a(blockpartface.field_178244_b), this.func_177589_a(blockpart, blockpartface, textureatlassprite1, enumfacing, p_177578_2_, p_177578_3_)); ++ builder.func_177650_a(p_177578_2_.rotate(blockpartface.field_178244_b), this.makeBakedQuad(blockpart, blockpartface, textureatlassprite1, enumfacing, p_177578_2_, p_177578_3_)); + } + } + } +@@ -530,9 +535,14 @@ + + private BakedQuad func_177589_a(BlockPart p_177589_1_, BlockPartFace p_177589_2_, TextureAtlasSprite p_177589_3_, EnumFacing p_177589_4_, ModelRotation p_177589_5_, boolean p_177589_6_) + { +- return this.field_177607_l.func_178414_a(p_177589_1_.field_178241_a, p_177589_1_.field_178239_b, p_177589_2_, p_177589_3_, p_177589_4_, p_177589_5_, p_177589_1_.field_178237_d, p_177589_6_, p_177589_1_.field_178238_e); ++ return makeBakedQuad(p_177589_1_, p_177589_2_, p_177589_3_, p_177589_4_, (net.minecraftforge.client.model.ITransformation)p_177589_5_, p_177589_6_); + } + ++ private BakedQuad makeBakedQuad(BlockPart p_177589_1_, BlockPartFace p_177589_2_, TextureAtlasSprite p_177589_3_, EnumFacing p_177589_4_, net.minecraftforge.client.model.ITransformation p_177589_5_, boolean p_177589_6_) ++ { ++ return this.field_177607_l.makeBakedQuad(p_177589_1_.field_178241_a, p_177589_1_.field_178239_b, p_177589_2_, p_177589_3_, p_177589_4_, p_177589_5_, p_177589_1_.field_178237_d, p_177589_6_, p_177589_1_.field_178238_e); ++ } ++ + private void func_177597_h() + { + this.func_177574_i(); diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch index 9b2da14ec1..ec981fa497 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch @@ -1,7 +1,11 @@ --- ../src-base/minecraft/net/minecraft/client/resources/model/ModelManager.java +++ ../src-work/minecraft/net/minecraft/client/resources/model/ModelManager.java -@@ -28,6 +28,7 @@ - ModelBakery modelbakery = new ModelBakery(p_110549_1_, this.field_174956_b, this.field_174957_c); +@@ -25,9 +25,10 @@ + + public void func_110549_a(IResourceManager p_110549_1_) + { +- ModelBakery modelbakery = new ModelBakery(p_110549_1_, this.field_174956_b, this.field_174957_c); ++ ModelBakery modelbakery = new net.minecraftforge.client.model.ModelLoader(p_110549_1_, this.field_174956_b, this.field_174957_c); this.field_174958_a = modelbakery.func_177570_a(); this.field_174955_d = (IBakedModel)this.field_174958_a.func_82594_a(ModelBakery.field_177604_a); + net.minecraftforge.client.ForgeHooksClient.onModelBake(this, this.field_174958_a, modelbakery); diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.java.patch b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.java.patch new file mode 100644 index 0000000000..01c361669f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/client/resources/model/ModelRotation.java ++++ ../src-work/minecraft/net/minecraft/client/resources/model/ModelRotation.java +@@ -10,7 +10,7 @@ + import net.minecraftforge.fml.relauncher.SideOnly; + + @SideOnly(Side.CLIENT) +-public enum ModelRotation ++public enum ModelRotation implements net.minecraftforge.client.model.IModelState, net.minecraftforge.client.model.ITransformation + { + X0_Y0(0, 0), + X0_Y90(0, 90), +@@ -122,4 +122,9 @@ + field_177546_q.put(Integer.valueOf(var3.field_177545_r), var3); + } + } ++ ++ public net.minecraftforge.client.model.TRSRTransformation apply(net.minecraftforge.client.model.IModelPart part) { return new net.minecraftforge.client.model.TRSRTransformation(getMatrix()); } ++ public javax.vecmath.Matrix4f getMatrix() { return new javax.vecmath.Matrix4f(func_177525_a()); } ++ public EnumFacing rotate(EnumFacing facing) { return func_177523_a(facing); } ++ public int rotate(EnumFacing facing, int vertexIndex) { return func_177520_a(facing, vertexIndex); } + } diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index 39b4d21a5c..f93959cb73 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -2,6 +2,14 @@ package net.minecraftforge.client; import static net.minecraftforge.common.ForgeVersion.Status.BETA; import static net.minecraftforge.common.ForgeVersion.Status.BETA_OUTDATED; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import javax.vecmath.Matrix4f; + import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -13,10 +21,18 @@ import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.model.ModelBase; import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemTransformVec3f; +import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage; import net.minecraft.client.resources.I18n; +import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.settings.GameSettings; @@ -42,13 +58,17 @@ import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.event.sound.PlaySoundEvent; +import net.minecraftforge.client.model.IPerspectiveAwareModel; import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.ForgeVersion.Status; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.common.FMLLog; +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; //import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*; //import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*; @@ -469,4 +489,138 @@ public class ForgeHooksClient { MinecraftForge.EVENT_BUS.post(new ModelBakeEvent(modelManager, modelRegistry, modelBakery)); } + + public static Matrix4f getMatrix(ItemTransformVec3f transform) + { + javax.vecmath.Matrix4f m = new javax.vecmath.Matrix4f(), t = new javax.vecmath.Matrix4f(); + m.setIdentity(); + m.setTranslation(transform.translation); + t.setIdentity(); + t.rotY(transform.rotation.y); + m.mul(t); + t.setIdentity(); + t.rotX(transform.rotation.x); + m.mul(t); + t.setIdentity(); + t.rotZ(transform.rotation.z); + m.mul(t); + t.setIdentity(); + t.m00 = transform.scale.x; + t.m11 = transform.scale.y; + t.m22 = transform.scale.z; + m.mul(t); + return m; + } + + public static IBakedModel handleCameraTransforms(IBakedModel model, ItemCameraTransforms.TransformType cameraTransformType) + { + if(model instanceof IPerspectiveAwareModel) + { + Pair pair = ((IPerspectiveAwareModel)model).handlePerspective(cameraTransformType); + + if(pair.getRight() != null) multiplyCurrentGlMatrix(pair.getRight()); + return pair.getLeft(); + } + switch(cameraTransformType) + { + case FIRST_PERSON: + RenderItem.applyVanillaTransform(model.getItemCameraTransforms().firstPerson); + break; + case GUI: + RenderItem.applyVanillaTransform(model.getItemCameraTransforms().gui); + break; + case HEAD: + RenderItem.applyVanillaTransform(model.getItemCameraTransforms().head); + break; + case THIRD_PERSON: + RenderItem.applyVanillaTransform(model.getItemCameraTransforms().thirdPerson); + break; + default: + break; + } + return model; + } + + private static final FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16); + + public static void multiplyCurrentGlMatrix(Matrix4f matrix) + { + matrixBuf.clear(); + float[] t = new float[4]; + for(int i = 0; i < 4; i++) + { + matrix.getColumn(i, t); + matrixBuf.put(t); + } + matrixBuf.flip(); + GL11.glMultMatrix(matrixBuf); + } + + // moved and expanded from WorldVertexBufferUploader.draw + + public static void preDraw(EnumUsage attrType, VertexFormatElement attr, int stride, ByteBuffer buffer) + { + buffer.position(attr.getOffset()); + switch(attrType) + { + case POSITION: + glVertexPointer(attr.getElementCount(), attr.getType().getGlConstant(), stride, buffer); + glEnableClientState(GL_VERTEX_ARRAY); + break; + case NORMAL: + if(attr.getElementCount() != 3) + { + throw new IllegalArgumentException("Normal attribute should have the size 3: " + attr); + } + glNormalPointer(attr.getType().getGlConstant(), stride, buffer); + glEnableClientState(GL_NORMAL_ARRAY); + break; + case COLOR: + glColorPointer(attr.getElementCount(), attr.getType().getGlConstant(), stride, buffer); + glEnableClientState(GL_COLOR_ARRAY); + break; + case UV: + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit + attr.getIndex()); + glTexCoordPointer(attr.getElementCount(), attr.getType().getGlConstant(), stride, buffer); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + break; + case PADDING: + break; + case GENERIC: + glEnableVertexAttribArray(attr.getIndex()); + glVertexAttribPointer(attr.getIndex(), attr.getElementCount(), attr.getType().getGlConstant(), false, stride, buffer); + default: + FMLLog.severe("Unimplemented vanilla attribute upload: %s", attrType.getDisplayName()); + } + } + + public static void postDraw(EnumUsage attrType, VertexFormatElement attr, int stride, ByteBuffer buffer) + { + switch(attrType) + { + case POSITION: + glDisableClientState(GL_VERTEX_ARRAY); + break; + case NORMAL: + glDisableClientState(GL_NORMAL_ARRAY); + break; + case COLOR: + glDisableClientState(GL_COLOR_ARRAY); + // is this really needed? + GlStateManager.resetColor(); + break; + case UV: + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit + attr.getIndex()); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + break; + case PADDING: + break; + case GENERIC: + glDisableVertexAttribArray(attr.getIndex()); + default: + FMLLog.severe("Unimplemented vanilla attribute upload: %s", attrType.getDisplayName()); + } + } } diff --git a/src/main/java/net/minecraftforge/client/model/Attributes.java b/src/main/java/net/minecraftforge/client/model/Attributes.java new file mode 100644 index 0000000000..90e0d7697b --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/Attributes.java @@ -0,0 +1,107 @@ +package net.minecraftforge.client.model; + +import java.nio.ByteBuffer; +import java.util.List; + +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumType; +import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage; + +public class Attributes +{ + /* + * Default format of the data in IBakedModel + */ + public static final VertexFormat DEFAULT_BAKED_FORMAT; + + static + { + DEFAULT_BAKED_FORMAT = new VertexFormat(); + DEFAULT_BAKED_FORMAT.setElement(new VertexFormatElement(0, EnumType.FLOAT, EnumUsage.POSITION, 3)); + DEFAULT_BAKED_FORMAT.setElement(new VertexFormatElement(0, EnumType.UBYTE, EnumUsage.COLOR, 4)); + DEFAULT_BAKED_FORMAT.setElement(new VertexFormatElement(0, EnumType.FLOAT, EnumUsage.UV, 2)); + DEFAULT_BAKED_FORMAT.setElement(new VertexFormatElement(0, EnumType.BYTE, EnumUsage.PADDING, 4)); + } + + /* + * Can first format be used where second is expected + */ + public static boolean moreSpecific(VertexFormat first, VertexFormat second) + { + int size = first.getNextOffset(); + if(size != second.getNextOffset()) return false; + + int padding = 0; + int j = 0; + for(VertexFormatElement firstAttr : (List)first.getElements()) + { + while(j < second.getElementCount() && second.getElement(j).getUsage() == EnumUsage.PADDING) + { + padding += second.getElement(j++).getSize(); + } + if(j >= second.getElementCount() && padding == 0) + { + // if no padding is left, but there are still elements in first (we're processing one) - it doesn't fit + return false; + } + if(padding == 0) + { + // no padding - attributes have to match + VertexFormatElement secondAttr = second.getElement(j++); + if( + firstAttr.getIndex() != secondAttr.getIndex() || + firstAttr.getElementCount() != secondAttr.getElementCount() || + firstAttr.getType() != secondAttr.getType() || + firstAttr.getUsage() != secondAttr.getUsage()) + { + return false; + } + } + else + { + // padding - attribute should fit in it + padding -= firstAttr.getSize(); + if(padding < 0) return false; + } + } + + if(padding != 0 || j != second.getElementCount()) return false; + return true; + } + + public static void put(ByteBuffer buf, VertexFormatElement e, boolean normalize, Number fill, Number... ns) + { + if(e.getElementCount() > ns.length && fill == null) throw new IllegalArgumentException("not enough elements"); + Number n; + for(int i = 0; i < e.getElementCount(); i++) + { + if(i < ns.length) n = ns[i]; + else n = fill; + switch(e.getType()) + { + case BYTE: + buf.put(normalize ? (byte)(n.floatValue() / (Byte.MAX_VALUE - 1)) : n.byteValue()); + break; + case UBYTE: + buf.put(normalize ? (byte)(n.floatValue() / ((byte) -1)) : n.byteValue()); + break; + case SHORT: + buf.putShort(normalize ? (short)(n.floatValue() / (Short.MAX_VALUE - 1)) : n.shortValue()); + break; + case USHORT: + buf.putShort(normalize ? (short)(n.floatValue() / ((short) -1)) : n.shortValue()); + break; + case INT: + buf.putInt(normalize ? (int)(n.doubleValue() / (Integer.MAX_VALUE - 1)) : n.intValue()); + break; + case UINT: + buf.putInt(normalize ? (int)(n.doubleValue() / ((int) - 1)) : n.intValue()); + break; + case FLOAT: + buf.putFloat(n.floatValue()); + break; + } + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ICustomModelLoader.java b/src/main/java/net/minecraftforge/client/model/ICustomModelLoader.java new file mode 100644 index 0000000000..6dd74668f9 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ICustomModelLoader.java @@ -0,0 +1,18 @@ +package net.minecraftforge.client.model; + +import net.minecraft.client.resources.IResourceManagerReloadListener; +import net.minecraft.util.ResourceLocation; + +public interface ICustomModelLoader extends IResourceManagerReloadListener +{ + /* + * Checks if given model should be loaded by this loader. + * Reading file contents is inadvisable, if possible decision should be made based on the location alone. + */ + public boolean accepts(ResourceLocation modelLocation); + + /* + * loads (or reloads) specified model + */ + public IModel loadModel(ResourceLocation modelLocation); +} diff --git a/src/main/java/net/minecraftforge/client/model/IFlexibleBakedModel.java b/src/main/java/net/minecraftforge/client/model/IFlexibleBakedModel.java new file mode 100644 index 0000000000..e01989a563 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/IFlexibleBakedModel.java @@ -0,0 +1,82 @@ +package net.minecraftforge.client.model; + +import java.util.List; + +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.IBakedModel; +import net.minecraft.util.EnumFacing; + +/* + * Version of IBakedModel with less restriction on camera transformations and with explicit format of the baked array. + */ +public interface IFlexibleBakedModel extends IBakedModel +{ + // non-erased versions of the IBakedModel methods + List getFaceQuads(EnumFacing side); + List getGeneralQuads(); + /* + * Specifies the format which BakedQuads' getVertexData will have. + */ + VertexFormat getFormat(); + + /* + * Default implementation of IFlexibleBakedModel that should be useful in most cases + */ + public static class Wrapper implements IFlexibleBakedModel + { + private final IBakedModel parent; + VertexFormat format; + + public Wrapper(IBakedModel parent, VertexFormat format) + { + this.parent = parent; + this.format = format; + } + + @SuppressWarnings("unchecked") + public List getFaceQuads(EnumFacing side) + { + return parent.getFaceQuads(side); + } + + @SuppressWarnings("unchecked") + public List getGeneralQuads() + { + return parent.getGeneralQuads(); + } + + public boolean isAmbientOcclusion() + { + return parent.isAmbientOcclusion(); + } + + public boolean isGui3d() + { + return parent.isGui3d(); + } + + public boolean isBuiltInRenderer() + { + return parent.isBuiltInRenderer(); + } + + public TextureAtlasSprite getTexture() + { + return parent.getTexture(); + } + + @Deprecated + public ItemCameraTransforms getItemCameraTransforms() + { + return parent.getItemCameraTransforms(); + } + + public VertexFormat getFormat() + { + return new VertexFormat(format); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/IModel.java b/src/main/java/net/minecraftforge/client/model/IModel.java new file mode 100644 index 0000000000..457f8a5814 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/IModel.java @@ -0,0 +1,45 @@ +package net.minecraftforge.client.model; + +import java.util.Collection; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.ResourceLocation; + +import com.google.common.base.Function; + +/* + * Interface for models that can be baked + * (possibly to different vertex formats and with different state). + */ +public interface IModel extends IModelPart +{ + /* + * Returns all model locations that this model depends on. + * Assume that returned collection is immutable. + */ + Collection getDependencies(); + + /* + * Returns all texture locations that this model depends on. + * Assume that returned collection is immutable. + */ + Collection getTextures(); + + /* + * All model texture coordinates should be resolved at this method. + * Returned model should be in the simplest form possible, for performance + * reasons (if it's not ISmartBlock/ItemModel - then it should be + * represented by List internally). + * Returned model's getFormat() can me less specific than the passed + * format argument (some attributes can be replaced with padding), + * if there's no such info in this model. + */ + IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter); + + /* + * Default state this model will be baked with. + * See IModelState. + */ + IModelState getDefaultState(); +} diff --git a/src/main/java/net/minecraftforge/client/model/IModelPart.java b/src/main/java/net/minecraftforge/client/model/IModelPart.java new file mode 100644 index 0000000000..e8fe6bedd9 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/IModelPart.java @@ -0,0 +1,8 @@ +package net.minecraftforge.client.model; + +/* + * Represents the part of the model you can refer to, for example: mesh, + * skeleton, bone or some separately animated submodel. + * Primary function of this interface is the argument type of IModelState.apply. + */ +public interface IModelPart {} diff --git a/src/main/java/net/minecraftforge/client/model/IModelState.java b/src/main/java/net/minecraftforge/client/model/IModelState.java new file mode 100644 index 0000000000..927d135c3a --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/IModelState.java @@ -0,0 +1,18 @@ +package net.minecraftforge.client.model; + +import javax.vecmath.Matrix4f; + +import com.google.common.base.Function; + +/* + * Represents the dynamic information associated with the model. + * Common use case is (possibly interpolated) animation frame. + */ +public interface IModelState extends Function +{ + /* + * returns the transformation (in the local coordinates) that needs to be applied to the specific part of the model. + */ + @Override + TRSRTransformation apply(IModelPart part); +} diff --git a/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java b/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java new file mode 100644 index 0000000000..930437b3df --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java @@ -0,0 +1,22 @@ +package net.minecraftforge.client.model; + +import javax.vecmath.Matrix4f; + +import org.apache.commons.lang3.tuple.Pair; + +import net.minecraft.item.ItemStack; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.resources.model.IBakedModel; + +/* + * Model that changes based on the rendering perspective + * (first-person, GUI, e.t.c - see TransformType) + */ +public interface IPerspectiveAwareModel extends IBakedModel +{ + /* + * Returns the pair of the model for the given perspective, and the matrix + * that should be applied to the GL state before rendering it (matrix may be null). + */ + Pair handlePerspective(TransformType cameraTransformType); +} diff --git a/src/main/java/net/minecraftforge/client/model/ITransformation.java b/src/main/java/net/minecraftforge/client/model/ITransformation.java new file mode 100644 index 0000000000..36f22b03f0 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ITransformation.java @@ -0,0 +1,18 @@ +package net.minecraftforge.client.model; + +import javax.vecmath.Matrix4f; + +import net.minecraft.util.EnumFacing; + +/* + * Replacement interface for ModelRotation to allow custom transformations of vanilla models. + * You should probably use TRSRTransformation directly. + */ +public interface ITransformation +{ + public Matrix4f getMatrix(); + + public EnumFacing rotate(EnumFacing facing); + + public int rotate(EnumFacing facing, int vertexIndex); +} diff --git a/src/main/java/net/minecraftforge/client/model/MapModelState.java b/src/main/java/net/minecraftforge/client/model/MapModelState.java new file mode 100644 index 0000000000..72c874e0bc --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/MapModelState.java @@ -0,0 +1,31 @@ +package net.minecraftforge.client.model; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +/* + * Simple implementation of IModelState via a map and a default value. + */ +public class MapModelState implements IModelState +{ + private final ImmutableMap map; + private final TRSRTransformation def; + + public MapModelState(Map map) + { + this(map, TRSRTransformation.identity()); + } + + public MapModelState(Map map, TRSRTransformation def) + { + this.map = ImmutableMap.copyOf(map); + this.def = def; + } + + public TRSRTransformation apply(IModelPart part) + { + if(!map.containsKey(part)) return def; + return map.get(part); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java new file mode 100644 index 0000000000..72190cb86f --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -0,0 +1,399 @@ +package net.minecraftforge.client.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemModelGenerator; +import net.minecraft.client.renderer.block.model.ModelBlock; +import net.minecraft.client.renderer.block.model.ModelBlockDefinition; +import net.minecraft.client.renderer.block.model.ModelBlockDefinition.MissingVariantException; +import net.minecraft.client.renderer.block.model.ModelBlockDefinition.Variant; +import net.minecraft.client.renderer.block.model.ModelBlockDefinition.Variants; +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.VertexFormat; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.model.BuiltInModel; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.ModelRotation; +import net.minecraft.client.resources.model.WeightedBakedModel; +import net.minecraft.item.Item; +import net.minecraft.util.IRegistry; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.registry.GameData; + +import org.apache.logging.log4j.Level; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; + +public class ModelLoader extends ModelBakery +{ + private final Map stateModels = new HashMap(); + private final Set resolveTextures = new HashSet(); + private final Set uvLocked = new HashSet(); + private final Set textures = new HashSet(); + private final Set loadingModels = new HashSet(); + + public ModelLoader(IResourceManager manager, TextureMap map, BlockModelShapes shapes) + { + super(manager, map, shapes); + VanillaLoader.instance.setLoader(this); + ModelLoaderRegistry.clearModelCache(); + } + + @Override + public IRegistry setupModelRegistry() + { + loadBlocks(); + loadItems(); + stateModels.put(MODEL_MISSING, getModel(new ResourceLocation(MODEL_MISSING.getResourceDomain(), MODEL_MISSING.getResourcePath()))); + textures.remove(TextureMap.LOCATION_MISSING_TEXTURE); + textures.addAll(LOCATIONS_BUILTIN_TEXTURES); + textureMap.loadSprites(resourceManager, new IIconCreator() + { + public void registerSprites(TextureMap map) + { + for(ResourceLocation t : textures) + { + sprites.put(t, map.registerSprite(t)); + } + } + }); + sprites.put(new ResourceLocation("missingno"), textureMap.getMissingSprite()); + Function textureGetter = Functions.forMap(sprites, textureMap.getMissingSprite()); + for(Entry e : stateModels.entrySet()) + { + bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter)); + } + return bakedRegistry; + } + + private void loadBlocks() + { + Map stateMap = blockModelShapes.getBlockStateMapper().putAllStateModelLocations(); + loadVariants(stateMap.values()); + } + + @Override + protected void registerVariant(ModelBlockDefinition definition, ModelResourceLocation location) + { + Variants variants = null; + try + { + variants = definition.getVariants(location.getVariant()); + } + catch(MissingVariantException e) {} + if(variants == null) + { + // adding default variant for simple blocks + ResourceLocation loc = new ResourceLocation(location.getResourceDomain(), "block/" + location.getResourcePath()); + variants = new Variants("normal", Lists.newArrayList(new Variant(loc, ModelRotation.X0_Y0, false, 1))); + } + if(!variants.getVariants().isEmpty()) + { + try + { + stateModels.put(location, new WeightedRandomModel(variants)); + } + catch(Throwable e) + { + throw new RuntimeException(e); + } + } + } + + private void loadItems() + { + registerVariantNames(); + for(Item item : GameData.getItemRegistry().typeSafeIterable()) + { + for(String s : (List)getVariantNames(item)) + { + ResourceLocation file = getItemLocation(s); + ModelResourceLocation memory = new ModelResourceLocation(s, "inventory"); + resolveTextures.add(ModelLoaderRegistry.getActualLocation(file)); + IModel model = getModel(file); + if(model != null) stateModels.put(memory, model); + } + } + } + + public IModel getModel(ResourceLocation location) + { + if(!ModelLoaderRegistry.loaded(location)) loadAnyModel(location); + return ModelLoaderRegistry.getModel(location); + } + + @Override + protected ResourceLocation getModelLocation(ResourceLocation model) + { + return new ResourceLocation(model.getResourceDomain(), model.getResourcePath() + ".json"); + } + + private void loadAnyModel(ResourceLocation location) + { + if(loadingModels.contains(location)) + { + throw new IllegalStateException("circular model dependencies involving model " + location); + } + loadingModels.add(location); + IModel model = ModelLoaderRegistry.getModel(location); + for(ResourceLocation dep : model.getDependencies()) + { + getModel(dep); + } + textures.addAll(model.getTextures()); + loadingModels.remove(location); + } + + private class VanillaModelWrapper implements IModel + { + private final ResourceLocation location; + private final ModelBlock model; + + public VanillaModelWrapper(ResourceLocation location, ModelBlock model) + { + this.location = location; + this.model = model; + } + + public Collection getDependencies() + { + if(model.getParentLocation() == null || model.getParentLocation().getResourcePath().startsWith("builtin/")) return Collections.emptyList(); + return Collections.singletonList(model.getParentLocation()); + } + + public Collection getTextures() + { + // setting parent here to make textures resolve properly + if(model.getParentLocation() != null) + { + IModel parent = getModel(model.getParentLocation()); + if(parent instanceof VanillaModelWrapper) + { + model.parent = ((VanillaModelWrapper) parent).model; + } + else + { + throw new IllegalStateException("vanilla model" + model + "can't have non-vanilla parent"); + } + } + + if(!resolveTextures.contains(location)) return Collections.emptyList(); + + ImmutableSet.Builder builder = ImmutableSet.builder(); + builder.add(new ResourceLocation(model.resolveTextureName("particle"))); + + if(hasItemModel(model)) + { + for(String s : (List)ItemModelGenerator.LAYERS) + { + String r = model.resolveTextureName(s); + ResourceLocation loc = new ResourceLocation(r); + if(!r.equals(s)) + { + builder.add(loc); + } + // mojang hardcode + if(model.getRootModel() == MODEL_COMPASS && !loc.equals(TextureMap.LOCATION_MISSING_TEXTURE)) + { + TextureAtlasSprite.setLocationNameCompass(loc.toString()); + } + else if(model.getRootModel() == MODEL_CLOCK && !loc.equals(TextureMap.LOCATION_MISSING_TEXTURE)) + { + TextureAtlasSprite.setLocationNameClock(loc.toString()); + } + } + } + if(location.getResourcePath().startsWith("models/block/") || !ModelLoader.this.isBuiltinModel(model.getRootModel())) + { + builder.addAll(ModelLoader.this.getTextureLocations(model)); + } + return builder.build(); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + if(!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT)) + { + throw new IllegalArgumentException("can't bake vanilla models to the format that doesn't fit into the default one: " + format); + } + ModelBlock model = this.model; + if(hasItemModel(model)) model = makeItemModel(model); + if(isCustomRenderer(model)) return new IFlexibleBakedModel.Wrapper(new BuiltInModel(new ItemCameraTransforms(model.getThirdPersonTransform(), model.getFirstPersonTransform(), model.getHeadTransform(), model.getInGuiTransform())), Attributes.DEFAULT_BAKED_FORMAT); + return new IFlexibleBakedModel.Wrapper(bakeModel(model, state.apply(this), uvLocked.contains(location)), Attributes.DEFAULT_BAKED_FORMAT); + } + + public IModelState getDefaultState() + { + return ModelRotation.X0_Y0; + } + } + + // Weighted models can contain multiple copies of 1 model with different rotations - this is to make it work with IModelState (different copies will be different objects). + private static class WeightedPartWrapper implements IModel + { + private final IModel model; + + public WeightedPartWrapper(IModel model) + { + this.model = model; + } + + public Collection getDependencies() + { + return model.getDependencies(); + } + + public Collection getTextures() + { + return model.getTextures(); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + return model.bake(state, format, bakedTextureGetter); + } + + public IModelState getDefaultState() + { + return model.getDefaultState(); + } + } + + private class WeightedRandomModel implements IModel + { + private final List variants; + private final List locations = new ArrayList(); + private final List models = new ArrayList(); + private final IModelState defaultState; + + public WeightedRandomModel(Variants variants) + { + this.variants = variants.getVariants(); + ImmutableMap.Builder builder = ImmutableMap.builder(); + for(Variant v : (List)variants.getVariants()) + { + ResourceLocation loc = v.getModelLocation(); + resolveTextures.add(ModelLoaderRegistry.getActualLocation(loc)); + locations.add(loc); + IModel model = new WeightedPartWrapper(getModel(loc)); + models.add(model); + builder.put(model, new TRSRTransformation(v.getRotation())); + if(v.isUvLocked()) uvLocked.add(ModelLoaderRegistry.getActualLocation(loc)); + } + defaultState = new MapModelState(builder.build()); + } + + public Collection getDependencies() + { + return ImmutableList.copyOf(locations); + } + + public Collection getTextures() + { + /*ImmutableSet.Builder builder = ImmutableSet.builder(); + for(ResourceLocation loc : locations) + { + builder.addAll(getModel(loc).getTextures()); + } + return builder.build();*/ + return Collections.emptyList(); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + if(!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT)) + { + throw new IllegalArgumentException("can't bake vanilla weighted models to the format that doesn't fit into the default one: " + format); + } + if(variants.size() == 1) + { + Variant v = variants.get(0); + IModel model = models.get(0); + return model.bake(state.apply(model), format, bakedTextureGetter); + } + WeightedBakedModel.Builder builder = new WeightedBakedModel.Builder(); + for(int i = 0; i < variants.size(); i++) + { + IModel model = models.get(i); + builder.add(model.bake(state.apply(model), format, bakedTextureGetter), variants.get(i).getWeight()); + } + return new IFlexibleBakedModel.Wrapper(builder.build(), Attributes.DEFAULT_BAKED_FORMAT); + } + + public IModelState getDefaultState() + { + return defaultState; + } + } + + private boolean isBuiltinModel(ModelBlock model) + { + return model == MODEL_GENERATED || model == MODEL_COMPASS || model == MODEL_CLOCK || model == MODEL_ENTITY; + } + + public IModel getMissingModel() + { + return getModel(new ResourceLocation(MODEL_MISSING.getResourceDomain(), MODEL_MISSING.getResourcePath())); + } + + static enum VanillaLoader implements ICustomModelLoader + { + instance; + + private ModelLoader loader; + + void setLoader(ModelLoader loader) + { + this.loader = loader; + } + + ModelLoader getLoader() + { + return loader; + } + + public void onResourceManagerReload(IResourceManager resourceManager) + { + // do nothing, cause loader will store the reference to the resourceManager + } + + public boolean accepts(ResourceLocation modelLocation) + { + return true; + } + + public IModel loadModel(ResourceLocation modelLocation) + { + try + { + return loader.new VanillaModelWrapper(modelLocation, loader.loadModel(modelLocation)); + } + catch(IOException e) + { + FMLLog.log(Level.ERROR, e, "Exception loading model %s with vanilla loader, skipping", modelLocation); + return loader.getMissingModel(); + } + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java new file mode 100644 index 0000000000..db24104728 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -0,0 +1,112 @@ +package net.minecraftforge.client.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.IReloadableResourceManager; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.IResourceManagerReloadListener; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.ModelLoader.VanillaLoader; +import net.minecraftforge.fml.common.FMLLog; + +import org.apache.logging.log4j.Level; + +/* + * Central hub for custom model loaders. + */ +public class ModelLoaderRegistry +{ + private static final Set loaders = new HashSet(); + private static final Map cache = new HashMap(); + + /* + * Makes system aware of your loader. + */ + public static void registerLoader(ICustomModelLoader loader) + { + loaders.add(loader); + ((IReloadableResourceManager)Minecraft.getMinecraft().getResourceManager()).registerReloadListener(new IResourceManagerReloadListener() + { + public void onResourceManagerReload(IResourceManager manager) + { + for(ICustomModelLoader loader : loaders) loader.onResourceManagerReload(manager); + } + }); + } + + public static boolean loaded(ResourceLocation location) + { + return cache.containsKey(location); + } + + + public static ResourceLocation getActualLocation(ResourceLocation location) + { + if(location.getResourcePath().startsWith("builtin/")) return location; + return new ResourceLocation(location.getResourceDomain(), "models/" + location.getResourcePath()); + } + + public static IModel getModel(ResourceLocation location) + { + ResourceLocation actual = getActualLocation(location); + if(cache.containsKey(location)) return cache.get(location); + ICustomModelLoader accepted = null; + for(ICustomModelLoader loader : loaders) + { + try + { + if(loader.accepts(actual)) + { + if(accepted != null) + { + FMLLog.severe("2 loaders (%s and %s) want to load the same model %s", accepted, loader, location); + throw new IllegalStateException("2 loaders want to load the same model"); + } + accepted = loader; + } + } + catch(Exception e) + { + FMLLog.log(Level.ERROR, e, "Exception checking if model %s can be loaded with loader %s, skipping", location, loader); + } + } + + // no custom loaders found, try vanilla one + if(accepted == null) + { + if(VanillaLoader.instance.accepts(actual)) accepted = VanillaLoader.instance; + } + + IModel model; + if(accepted == null) + { + FMLLog.severe("no suitable loader found for the model %s, skipping", location); + model = getMissingModel(); + } + else try + { + model = accepted.loadModel(actual); + } + catch(Exception e) + { + FMLLog.log(Level.ERROR, e, "Exception loading model %s with loader %s, skipping", location, accepted); + model = getMissingModel(); + } + cache.put(location, model); + return model; + } + + public static IModel getMissingModel() + { + return ModelLoader.VanillaLoader.instance.getLoader().getMissingModel(); + } + + public static void clearModelCache() + { + cache.clear(); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java b/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java new file mode 100644 index 0000000000..c9c356fb83 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java @@ -0,0 +1,445 @@ +package net.minecraftforge.client.model; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4f; + +import net.minecraft.client.renderer.block.model.ItemTransformVec3f; +import net.minecraft.client.resources.model.ModelRotation; +import net.minecraft.util.EnumFacing; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; + +/* + * Interpolation-friendly affine transformation. + * If created with matrix, should successfully decompose it to a composition + * of easily interpolatable transformations (translation, first rotation, scale + * (with generally speaking different factors for each axis) and second rotation. + * If the inpit matrix is a composition of translation, rotation and scale (in + * any order), then the interpolation of the derived primitive transformations + * should result in the same transformation as the interpolation of the originals. + * Decomposition happens lazily (and is hopefully fast enough), so performance + * should be comparable to using Matrix4f directly. + * Immutable. + */ +public class TRSRTransformation implements IModelState, ITransformation +{ + private final Matrix4f matrix; + + private boolean full; + private Vector3f translation; + private Quat4f leftRot; + private Vector3f scale; + private Quat4f rightRot; + + public TRSRTransformation(Matrix4f matrix) + { + this.matrix = matrix; + } + + public TRSRTransformation(Vector3f translation, Quat4f leftRot, Vector3f scale, Quat4f rightRot) + { + this.matrix = mul(translation, leftRot, scale, rightRot); + this.translation = translation != null ? translation : new Vector3f(); + this.leftRot = leftRot != null ? leftRot : new Quat4f(0, 0, 0, 1); + this.scale = scale != null ? scale : new Vector3f(1, 1, 1); + this.rightRot = rightRot!= null ? rightRot : new Quat4f(0, 0, 0, 1); + full = true; + } + + public TRSRTransformation(ItemTransformVec3f transform) + { + this(transform.translation, quatFromYXZ(transform.rotation), transform.scale, null); + } + + public TRSRTransformation(ModelRotation rotation) + { + this(new Matrix4f(rotation.getMatrix4d())); + } + + private static final TRSRTransformation identity; + + static + { + Matrix4f m = new Matrix4f(); + m.setIdentity(); + identity = new TRSRTransformation(m); + identity.getLeftRot(); + } + + public static TRSRTransformation identity() + { + return identity; + } + + public TRSRTransformation compose(TRSRTransformation b) + { + Matrix4f m = getMatrix(); + m.mul(b.getMatrix()); + return new TRSRTransformation(m); + } + + private void genCheck() + { + if(!full) + { + Pair pair = toAffine(matrix); + Triple triple = svdDecompose(pair.getLeft()); + this.translation = pair.getRight(); + this.leftRot = triple.getLeft(); + this.scale = triple.getMiddle(); + this.rightRot = triple.getRight(); + full = true; + } + } + + public static Quat4f quatFromYXZ(Vector3f yxz) + { + return quatFromYXZ(yxz.y, yxz.x, yxz.z); + } + + public static Quat4f quatFromYXZ(float y, float x, float z) + { + Quat4f ret = new Quat4f(0, 0, 0, 1), t = new Quat4f(); + t.set(0, (float)Math.sin(y/2), 0, (float)Math.cos(y/2)); + ret.mul(t); + t.set((float)Math.sin(x/2), 0, 0, (float)Math.cos(x/2)); + ret.mul(t); + t.set(0, 0, (float)Math.sin(z/2), (float)Math.cos(z/2)); + ret.mul(t); + return ret; + } + + public static Vector3f toYXZ(Quat4f q) + { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float l = w2 + x2 + y2 + z2; + float sx = 2 * q.y * q.z - 2 * q.w * q.x; + float x = (float)Math.asin(sx / l); + if(Math.abs(sx) > .999f * l) + { + return new Vector3f( + 2 * (float)Math.atan2(q.y, q.w), + x, + 0 + ); + } + return new Vector3f( + (float)Math.atan2(2 * q.x * q.z + 2 * q.y * q.w, w2 - x2 - y2 + z2), + x, + (float)Math.atan2(2 * q.x * q.y + 2 * q.w * q.z, w2 - x2 + y2 - z2) + ); + } + + public static Matrix4f mul(Vector3f translation, Quat4f leftRot, Vector3f scale, Quat4f rightRot) + { + Matrix4f res = new Matrix4f(), t = new Matrix4f(); + res.setIdentity(); + if(translation != null) res.setTranslation(translation); + if(leftRot != null) + { + t.set(leftRot); + res.mul(t); + } + if(scale != null) + { + t.setIdentity(); + t.m00 = scale.x; + t.m11 = scale.y; + t.m22 = scale.z; + res.mul(t); + } + if(rightRot != null) + { + t.set(rightRot); + res.mul(t); + } + return res; + } + + /* + * Performs SVD decomposition of m, accumulating reflection in the scale (U and V are pure rotations). + */ + public static Triple svdDecompose(Matrix3f m) + { + // determine V by doing 5 steps of Jacobi iteration on MT * M + Quat4f u = new Quat4f(0, 0, 0, 1), v = new Quat4f(0, 0, 0, 1), qt = new Quat4f(); + Matrix3f b = new Matrix3f(m), t = new Matrix3f(); + t.transpose(m); + b.mul(t, b); + + for(int i = 0; i < 5; i++) v.mul(stepJacobi(b)); + + v.normalize(); + t.set(v); + b.set(m); + b.mul(t); + + sortSingularValues(b, v); + + Pair p; + + float ul = 1f; + + p = qrGivensQuat(b.m00, b.m10); + qt.set(0, 0, p.getLeft(), p.getRight()); + u.mul(qt); + t.setIdentity(); + t.m00 = qt.w * qt.w - qt.z * qt.z; + t.m11 = t.m00; + t.m10 = -2 * qt.z * qt.w; + t.m01 = -t.m10; + t.m22 = qt.w * qt.w + qt.z * qt.z; + ul *= t.m22; + b.mul(t, b); + + p = qrGivensQuat(b.m00, b.m20); + qt.set(0, -p.getLeft(), 0, p.getRight()); + u.mul(qt); + t.setIdentity(); + t.m00 = qt.w * qt.w - qt.y * qt.y; + t.m22 = t.m00; + t.m20 = 2 * qt.y * qt.w; + t.m02 = -t.m20; + t.m11 = qt.w * qt.w + qt.y * qt.y; + ul *= t.m11; + b.mul(t, b); + + p = qrGivensQuat(b.m11, b.m21); + qt.set(p.getLeft(), 0, 0, p.getRight()); + u.mul(qt); + t.setIdentity(); + t.m11 = qt.w * qt.w - qt.x * qt.x; + t.m22 = t.m11; + t.m21 = -2 * qt.x * qt.w; + t.m12 = -t.m21; + t.m00 = qt.w * qt.w + qt.x * qt.x; + ul *= t.m00; + b.mul(t, b); + + ul = 1f / ul; + u.scale((float)Math.sqrt(ul)); + + Vector3f s = new Vector3f(b.m00 * ul, b.m11 * ul, b.m22 * ul); + + return Triple.of(u, s, v); + } + + private static float rsqrt(float f) + { + float f2 = .5f * f; + int i = Float.floatToIntBits(f); + i = 0x5f3759df - (i >> 1); + f = Float.intBitsToFloat(i); + f *= 1.5f - f2 * f * f; + return f; + } + + private static final float eps = 1e-7f; + private static final float g = 3f + 2f * (float)Math.sqrt(2); + private static final float cs = (float)Math.cos(Math.PI / 8); + private static final float ss = (float)Math.sin(Math.PI / 8); + private static final float sq2 = 1f / (float)Math.sqrt(2); + + private static Pair approxGivensQuat(float a11, float a12, float a22) + { + float ch = 2f * (a11 - a22); + float sh = a12; + boolean b = g * sh * sh < ch * ch; + float w = rsqrt(sh * sh + ch * ch); + ch = b ? w * ch : cs; + sh = b ? w * sh : ss; + return Pair.of(sh, ch); + } + + private static final void swapNeg(Matrix3f m, int i, int j) + { + float[] t = new float[3]; + m.getColumn(j, t); + for(int k = 0; k < 3; k++) + { + m.setElement(k, j, -m.getElement(k, i)); + } + m.setColumn(i, t); + } + + private static void sortSingularValues(Matrix3f b, Quat4f v) + { + float p0 = b.m00 * b.m00 + b.m10 * b.m10 + b.m20 * b.m20; + float p1 = b.m01 * b.m01 + b.m11 * b.m11 + b.m21 * b.m21; + float p2 = b.m02 * b.m02 + b.m12 * b.m12 + b.m22 * b.m22; + Quat4f t = new Quat4f(); + if(p0 < p1) + { + swapNeg(b, 0, 1); + t.set(0, 0, sq2, sq2); + v.mul(t); + float f = p0; + p0 = p1; + p1 = f; + } + if(p0 < p2) + { + swapNeg(b, 0, 2); + t.set(0, sq2, 0, sq2); + v.mul(t); + float f = p0; + p0 = p2; + p2 = f; + } + if(p1 < p2) + { + swapNeg(b, 1, 2); + t.set(sq2, 0, 0, sq2); + v.mul(t); + } + } + + private static Pair qrGivensQuat(float a1, float a2) + { + float p = (float)Math.sqrt(a1 * a1 + a2 * a2); + float sh = p > eps ? a2 : 0; + float ch = Math.abs(a1) + Math.max(p, eps); + if(a1 < 0) + { + float f = sh; + sh = ch; + ch = f; + } + //float w = 1.f / (float)Math.sqrt(ch * ch + sh * sh); + float w = rsqrt(ch * ch + sh * sh); + ch *= w; + sh *= w; + return Pair.of(sh, ch); + } + + private static Quat4f stepJacobi(Matrix3f m) + { + Matrix3f t = new Matrix3f(); + Quat4f qt = new Quat4f(), ret = new Quat4f(0, 0, 0, 1); + Pair p; + // 01 + p = approxGivensQuat(m.m00, .5f * (m.m01 + m.m10), m.m11); + qt.set(0, 0, p.getLeft(), p.getRight()); + //qt.normalize(); + ret.mul(qt); + //t.set(qt); + t.setIdentity(); + t.m00 = qt.w * qt.w - qt.z * qt.z; + t.m11 = t.m00; + t.m10 = 2 * qt.z * qt.w; + t.m01 = -t.m10; + t.m22 = qt.w * qt.w + qt.z * qt.z; + m.mul(m, t); + t.transpose(); + m.mul(t, m); + // 02 + p = approxGivensQuat(m.m00, .5f * (m.m02 + m.m20), m.m22); + qt.set(0, -p.getLeft(), 0, p.getRight()); + //qt.normalize(); + ret.mul(qt); + //t.set(qt); + t.setIdentity(); + t.m00 = qt.w * qt.w - qt.y * qt.y; + t.m22 = t.m00; + t.m20 = -2 * qt.y * qt.w; + t.m02 = -t.m20; + t.m11 = qt.w * qt.w + qt.y * qt.y; + m.mul(m, t); + t.transpose(); + m.mul(t, m); + // 12 + p = approxGivensQuat(m.m11, .5f * (m.m12 + m.m21), m.m22); + qt.set(p.getLeft(), 0, 0, p.getRight()); + //qt.normalize(); + ret.mul(qt); + //t.set(qt); + t.setIdentity(); + t.m11 = qt.w * qt.w - qt.x * qt.x; + t.m22 = t.m11; + t.m21 = 2 * qt.x * qt.w; + t.m12 = -t.m21; + t.m00 = qt.w * qt.w + qt.x * qt.x; + m.mul(m, t); + t.transpose(); + m.mul(t, m); + return ret; + } + + /* + * Divides m by m33, sets last row to (0, 0, 0, 1), extracts linear and translation parts + */ + public static Pair toAffine(Matrix4f m) + { + m.mul(1.f / m.m33); + Vector3f trans = new Vector3f(m.m03, m.m13, m.m23); + Matrix3f linear = new Matrix3f(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22); + return Pair.of(linear, trans); + } + + /* + * Don't use this if you don't need to, conversion is lossy (second rotation component is lost). + */ + public ItemTransformVec3f toItemTransform() + { + return new ItemTransformVec3f(getTranslation(), toYXZ(getLeftRot()), getScale()); + } + + public Matrix4f getMatrix() + { + return (Matrix4f)matrix.clone(); + } + + public Vector3f getTranslation() + { + genCheck(); + return (Vector3f)translation.clone(); + } + + public Quat4f getLeftRot() + { + genCheck(); + return (Quat4f)leftRot.clone(); + } + + public Vector3f getScale() + { + genCheck(); + return (Vector3f)scale.clone(); + } + + public Quat4f getRightRot() + { + genCheck(); + return (Quat4f)rightRot.clone(); + } + + public TRSRTransformation apply(IModelPart part) + { + return this; + } + + public EnumFacing rotate(EnumFacing facing) + { + return rotate(matrix, facing); + } + + public static EnumFacing rotate(Matrix4f matrix, EnumFacing facing) + { + Vector4f vec = new Vector4f(facing.getDirectionVec().getX(), facing.getDirectionVec().getY(), facing.getDirectionVec().getZ(), 1); + matrix.transform(vec); + return EnumFacing.getFacingFromVector(vec.x / vec.w, vec.y / vec.w, vec.z / vec.w); + } + + public int rotate(EnumFacing facing, int vertexIndex) + { + // FIXME check if this is good enough + return vertexIndex; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java new file mode 100644 index 0000000000..1164197b95 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -0,0 +1,607 @@ +package net.minecraftforge.client.model.b3d; + +import static net.minecraftforge.client.model.b3d.B3DModel.logger; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.vecmath.Matrix4f; + +import net.minecraft.block.state.IBlockState; +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.IResource; +import net.minecraft.client.resources.IResourceManager; +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.ICustomModelLoader; +import net.minecraftforge.client.model.IFlexibleBakedModel; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.IModelPart; +import net.minecraftforge.client.model.IModelState; +import net.minecraftforge.client.model.ISmartBlockModel; +import net.minecraftforge.client.model.ISmartItemModel; +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; +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.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.apache.logging.log4j.Level; +import org.lwjgl.BufferUtils; + +import com.google.common.base.Function; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; + +/* + * Loader for Blitz3D models. + * To enable for your mod call instance.addDomain(modid). + * If you need more control over accepted resources - extend the class, and register a new instance with ModelLoaderRegistry. + */ +public class B3DLoader implements ICustomModelLoader +{ + public static final B3DLoader instance = new B3DLoader(); + + private IResourceManager manager; + + private final Set enabledDomains = new HashSet(); + private final Map cache = new HashMap(); + + public B3DLoader() + { + ModelLoaderRegistry.registerLoader(this); + } + + public void addDomain(String domain) + { + enabledDomains.add(domain.toLowerCase()); + } + + public void onResourceManagerReload(IResourceManager manager) + { + this.manager = manager; + cache.clear(); + } + + public boolean accepts(ResourceLocation modelLocation) + { + return enabledDomains.contains(modelLocation.getResourceDomain()) && modelLocation.getResourcePath().endsWith(".b3d"); + } + + @SuppressWarnings("unchecked") + public IModel loadModel(ResourceLocation modelLocation) + { + ResourceLocation file = new ResourceLocation(modelLocation.getResourceDomain(), modelLocation.getResourcePath()); + if(!cache.containsKey(file)) + { + try + { + IResource resource = null; + try + { + resource = manager.getResource(file); + } + catch(FileNotFoundException e) + { + if(modelLocation.getResourcePath().startsWith("models/block/")) + resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/item/" + file.getResourcePath().substring("models/block/".length()))); + else if(modelLocation.getResourcePath().startsWith("models/item/")) + resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/block/" + file.getResourcePath().substring("models/item/".length()))); + else throw e; + } + B3DModel.Parser parser = new B3DModel.Parser(resource.getInputStream()); + B3DModel model = parser.parse(); + cache.put(modelLocation, model); + } + catch(IOException e) + { + FMLLog.log(Level.ERROR, e, "Exception loading model %s with B3D loader, skipping", modelLocation); + cache.put(modelLocation, null); + } + } + B3DModel model = cache.get(file); + if(model == null) return ModelLoaderRegistry.getMissingModel(); + if(modelLocation instanceof B3DMeshLocation) + { + String mesh = ((B3DMeshLocation)modelLocation).getMesh(); + if(!model.getMeshes().containsKey(mesh)) + { + FMLLog.severe("No mesh named %s in model %s, skipping", mesh, modelLocation); + return ModelLoaderRegistry.getMissingModel(); + } + return new Wrapper(modelLocation, model.getTextures(), model.getMeshes().get(mesh)); + } + if(!(model.getRoot().getKind() instanceof Mesh)) + { + FMLLog.severe("No root mesh in model %s and no mesh name in location, skipping", modelLocation); + return ModelLoaderRegistry.getMissingModel(); + } + return new Wrapper(modelLocation, model.getTextures(), (Node)model.getRoot()); + } + + public static class B3DMeshLocation extends ResourceLocation + { + public final String mesh; + + public B3DMeshLocation(String domain, String path, String mesh) + { + super(domain, path); + this.mesh = mesh; + } + + public String getMesh() + { + return mesh; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((mesh == null) ? 0 : mesh.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + B3DMeshLocation other = (B3DMeshLocation) obj; + if (mesh == null) + { + if (other.mesh != null) return false; + } + else if (!mesh.equals(other.mesh)) return false; + return true; + } + } + + public static class B3DState implements IModelState + { + private final Animation animation; + private final int frame; + + public B3DState(Animation animation, int frame) + { + this.animation = animation; + this.frame = frame; + } + + public Animation getAnimation() + { + return animation; + } + + public int getFrame() + { + return frame; + } + + public TRSRTransformation apply(IModelPart part) + { + if(!(part instanceof PartWrapper)) + { + throw new IllegalArgumentException("B3DState can only be applied to b3d models"); + } + return getNodeMatrix(((PartWrapper)part).getNode()); + } + + private static LoadingCache, Integer>, TRSRTransformation> cache = CacheBuilder.newBuilder() + .maximumSize(16384) + .expireAfterAccess(2, TimeUnit.MINUTES) + .build(new CacheLoader, Integer>, TRSRTransformation>() + { + public TRSRTransformation load(Triple, Integer> key) throws Exception + { + return getNodeMatrix(key.getLeft(), key.getMiddle(), key.getRight()); + } + }); + + public TRSRTransformation getNodeMatrix(Node node) + { + return cache.getUnchecked(Triple., Integer>of(animation, node, frame)); + } + + public static TRSRTransformation getNodeMatrix(Animation animation, Node node, int frame) + { + TRSRTransformation ret = TRSRTransformation.identity(); + Key key = null; + if(animation != null) key = animation.getKeys().get(frame, node); + else if(key == null && node.getAnimation() != null && node.getAnimation() != animation) key = node.getAnimation().getKeys().get(frame, node); + if(key == null) + { + FMLLog.severe("invalid key index: " + frame); + logger.debug(String.format("getMatrix: %s %s\n%s %s %s\n%s", frame, node, node.getPos(), node.getScale(), node.getRot(), ret)); + } + else + { + ret = ret.compose(new TRSRTransformation(key.getPos(), key.getRot(), key.getScale(), null)); + Matrix4f rm = new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null).getMatrix(); + rm.invert(); + ret = ret.compose(new TRSRTransformation(rm)); + logger.debug(String.format("getMatrix: %s %s\n%s %s %s\n%s %s %s\n%s", frame, node, node.getPos(), node.getScale(), node.getRot(), key.getPos(), key.getScale(), key.getRot(), ret)); + } + return ret; + } + } + + public static enum B3DFrameProperty implements IUnlistedProperty + { + instance; + public String getName() + { + return "B3DFrame"; + } + + public boolean isValid(B3DState value) + { + return value instanceof B3DState; + } + + public Class getType() + { + return B3DState.class; + } + + public String valueToString(B3DState value) + { + return value.toString(); + } + } + + public static class PartWrapper> implements IModelPart + { + private final Node node; + + public static > PartWrapper create(Node node) + { + return new PartWrapper(node); + } + + public PartWrapper(Node node) + { + this.node = node; + } + + public Node getNode() + { + return node; + } + } + + public static class Wrapper extends PartWrapper implements IModel + { + private final ResourceLocation location; + private final ImmutableMap textures; + + public Wrapper(ResourceLocation location, List textures, B3DModel.Node mesh) + { + this(location, buildTextures(location, textures), mesh); + } + + public Wrapper(ResourceLocation location, ImmutableMap textures, B3DModel.Node mesh) + { + super(mesh); + this.location = location; + this.textures = textures; + } + + private static ImmutableMap buildTextures(ResourceLocation location, List textures) + { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for(Texture t : textures) + { + String path = t.getPath(); + if(path.endsWith(".png")) path = path.substring(0, path.length() - ".png".length()); + builder.put(t.getPath(), new ResourceLocation(location.getResourceDomain(), path)); + System.out.println("Texture: " + path); + } + return builder.build(); + } + + public Collection getDependencies() + { + // no dependencies for in-file models + // FIXME maybe add child meshes + return Collections.emptyList(); + } + + public Collection getTextures() + { + return textures.values(); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for(String path : textures.keySet()) + { + builder.put(path, bakedTextureGetter.apply(textures.get(path))); + } + builder.put("missingno", bakedTextureGetter.apply(new ResourceLocation("missingno"))); + return new BakedWrapper(this, state, format, builder.build()); + } + + public B3DState getDefaultState() + { + return new B3DState(getNode().getAnimation(), 1); + } + + public ResourceLocation getLocation() + { + return location; + } + + public ImmutableMap getTextureMap() + { + return textures; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((location == null) ? 0 : location.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Wrapper other = (Wrapper) obj; + if (location == null) + { + if (other.location != null) return false; + } + else if (!location.equals(other.location)) return false; + return true; + } + } + + private static class BakedWrapper implements IFlexibleBakedModel, ISmartBlockModel, ISmartItemModel + { + private final B3DLoader.Wrapper model; + private final IModelState state; + 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; + private static final int VERTICES_IN_QUAD = 4; + + public BakedWrapper(B3DLoader.Wrapper model, IModelState state, VertexFormat format, ImmutableMap textures) + { + this.model = model; + this.state = state; + this.format = format; + this.textures = textures; + + buf = BufferUtils.createByteBuffer(VERTICES_IN_QUAD * format.getNextOffset()); + } + + public List getFaceQuads(EnumFacing side) + { + return Collections.emptyList(); + } + + @SuppressWarnings("unchecked") + public List getGeneralQuads() + { + if(quads == null) + { + Node mesh = model.getNode(); + ImmutableList.Builder builder = ImmutableList.builder(); + for(Node child : mesh.getNodes().values()) + { + if(child.getKind() instanceof Mesh) + { + Node childMesh = (Node)child; + builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads()); + } + } + Multimap>> weightMap = mesh.getKind().getWeightMap(); + Collection faces = mesh.getKind().getFaces(); + faces = mesh.getKind().bake(new Function, Matrix4f>() + { + // gets transformation in global space + public Matrix4f apply(Node node) + { + TRSRTransformation ret = TRSRTransformation.identity(), pm = null; + if(node.getParent() != null) pm = new TRSRTransformation(apply(node.getParent())); + if(pm != null) + { + ret = ret.compose(pm); + } + ret = ret.compose(state.apply(PartWrapper.create(node))); + return ret.getMatrix(); + } + }); + for(Face f : faces) + { + buf.clear(); + List textures = f.getBrush().getTextures(); + TextureAtlasSprite sprite; + if(textures.isEmpty()) sprite = this.textures.get("missingno"); + 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 BakedQuad(data, -1, EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z))); + } + quads = builder.build(); + } + return quads; + } + + private void put(VertexFormatElement e, Number... ns) + { + Attributes.put(buf, e, true, 0, ns); + } + + @SuppressWarnings("unchecked") + private final void putVertexData(Vertex v, 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()) + { + switch(e.getUsage()) + { + case POSITION: + put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1); + break; + case COLOR: + if(v.getColor() != null) + { + put(e, v.getColor().x, v.getColor().y, v.getColor().z, v.getColor().w); + } + else + { + put(e, 1, 1, 1, 0); + } + break; + case UV: + // TODO handle more brushes + if(e.getIndex() < v.getTexCoords().length) + { + put(e, + sprite.getInterpolatedU(v.getTexCoords()[0].x * 16), + sprite.getInterpolatedV(v.getTexCoords()[0].y * 16), + 0, + 1 + ); + } + else + { + put(e, 0, 0, 0, 1); + } + break; + case NORMAL: + // TODO + put(e, 0, 1, 0, 1); + break; + case GENERIC: + // TODO + put(e, 0, 0, 0, 1); + break; + default: + break; + } + } + buf.position(oldPos + format.getNextOffset()); + } + + public boolean isAmbientOcclusion() + { + return true; + } + + public boolean isGui3d() + { + return true; + } + + public boolean isBuiltInRenderer() + { + return false; + } + + public TextureAtlasSprite getTexture() + { + // FIXME somehow specify particle texture in the model + return textures.values().asList().get(0); + } + + public ItemCameraTransforms getItemCameraTransforms() + { + return ItemCameraTransforms.DEFAULT; + } + + @Override + public BakedWrapper handleBlockState(IBlockState state) + { + System.out.println("handleBlockState " + state); + if(state instanceof IExtendedBlockState) + { + IExtendedBlockState exState = (IExtendedBlockState)state; + if(exState.getUnlistedNames().contains(B3DFrameProperty.instance)) + { + B3DState s = (B3DState)exState.getValue(B3DFrameProperty.instance); + if(s != null) + { + return getCachedModel(s.getFrame()); + } + } + } + return this; + } + + private final Map cache = new HashMap(); + + public BakedWrapper getCachedModel(int frame) + { + if(!cache.containsKey(frame)) + { + cache.put(frame, new BakedWrapper(model, new B3DState(model.getNode().getAnimation(), frame), format, textures)); + } + return cache.get(frame); + } + + public VertexFormat getFormat() + { + return format; + } + + public BakedWrapper handleItemState(ItemStack stack) + { + // TODO specify how to get B3DState from ItemStack + return this; + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java new file mode 100644 index 0000000000..9c2e18b7ff --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java @@ -0,0 +1,1083 @@ +package net.minecraftforge.client.model.b3d; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.vecmath.Matrix4f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector2f; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4f; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; + +public class B3DModel +{ + static final Logger logger = LogManager.getLogger(B3DModel.class); + private final List textures; + private final List brushes; + private final Node root; + private final ImmutableMap> meshes; + + public B3DModel(List textures, List brushes, Node root, ImmutableMap> meshes) + { + this.textures = textures; + this.brushes = brushes; + this.root = root; + this.meshes = meshes; + } + + public static class Parser + { + private static final int version = 0001; + private final ByteBuffer buf; + + private byte[] tag = new byte[4]; + private int length; + public Parser(InputStream in) throws IOException + { + if(in instanceof FileInputStream) + { + // fast shorthand for normal files + FileChannel channel = ((FileInputStream)in).getChannel(); + buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()).order(ByteOrder.LITTLE_ENDIAN); + } + else + { + // slower default for others + IOUtils.readFully(in, tag); + byte[] tmp = new byte[4]; + IOUtils.readFully(in, tmp); + int l = ByteBuffer.wrap(tmp).order(ByteOrder.LITTLE_ENDIAN).getInt(); + if(l < 0 || l + 8 < 0) throw new IOException("File is too large"); + buf = ByteBuffer.allocate(l + 8).order(ByteOrder.LITTLE_ENDIAN); + buf.clear(); + buf.put(tag); + buf.put(tmp); + buf.put(IOUtils.toByteArray(in, l)); + buf.flip(); + } + } + + private B3DModel res; + + public B3DModel parse() throws IOException + { + if(res != null) return res; + readHeader(); + res = bb3d(); + return res; + } + + private final List textures = new ArrayList(); + + private Texture getTexture(int texture) + { + if(texture > textures.size()) + { + logger.error(String.format("texture %s is out of range", texture)); + return null; + } + else if(texture == -1) return null; + return textures.get(texture); + } + + private final List brushes = new ArrayList(); + + private Brush getBrush(int brush) + { + if(brush > brushes.size()) + { + logger.error(String.format("brush %s is out of range", brush)); + return null; + } + else if(brush == -1) return null; + return brushes.get(brush); + } + + private final List vertices = new ArrayList(); + + private Vertex getVertex(int vertex) + { + if(vertex > vertices.size()) + { + logger.error(String.format("vertex %s is out of range", vertex)); + return null; + } + return vertices.get(vertex); + } + + private final ImmutableMap.Builder> meshes = ImmutableMap.builder(); + + private void readHeader() throws IOException + { + buf.get(tag); + length = buf.getInt(); + } + + private boolean isChunk(String tag) throws IOException + { + return Arrays.equals(this.tag, tag.getBytes("US-ASCII")); + } + + private void chunk(String tag) throws IOException + { + if(!isChunk(tag)) throw new IOException("Expected chunk " + tag + ", got " + new String(this.tag, "US-ASCII")); + pushLimit(); + } + + private String readString() throws IOException + { + int start = buf.position(); + while(buf.get() != 0); + int end = buf.position(); + byte[] tmp = new byte[end - start - 1]; + buf.position(start); + buf.get(tmp); + buf.get(); + String ret = new String(tmp, "UTF8"); + return ret; + } + + private Deque limitStack = new ArrayDeque(); + + private void pushLimit() + { + limitStack.push(buf.limit()); + buf.limit(buf.position() + length); + } + + private void popLimit() + { + buf.limit(limitStack.pop()); + } + + private B3DModel bb3d() throws IOException + { + chunk("BB3D"); + int version = buf.getInt(); + if(version / 100 > this.version / 100) + throw new IOException("Unsupported major model version: " + ((float)version / 100)); + if(version % 100 > this.version % 100) + logger.warn(String.format("Minor version differnce in model: ", ((float)version / 100))); + List textures = null; + List brushes = null; + Node root = null; + while(buf.hasRemaining()) + { + readHeader(); + if (isChunk("TEXS")) textures = texs(); + else if(isChunk("BRUS")) brushes = brus(); + else if(isChunk("NODE")) root = node(); + else skip(); + } + popLimit(); + return new B3DModel(textures, brushes, root, meshes.build()); + } + + private List texs() throws IOException + { + chunk("TEXS"); + List ret = new ArrayList(); + while(buf.hasRemaining()) + { + logger.debug("TEST"); + String path = readString(); + logger.debug("path: '" + path + "'"); + int flags = buf.getInt(); + int blend = buf.getInt(); + Vector2f pos = new Vector2f(buf.getFloat(), buf.getFloat()); + Vector2f scale = new Vector2f(buf.getFloat(), buf.getFloat()); + float rot = buf.getFloat(); + ret.add(new Texture(path, flags, blend, pos, scale, rot)); + logger.debug("TEX: '" + path + "' " + flags + " " + blend + " " + pos + " " + scale + " " + rot); + } + popLimit(); + this.textures.addAll(ret); + return ret; + } + + private List brus() throws IOException + { + chunk("BRUS"); + List ret = new ArrayList(); + int n_texs = buf.getInt(); + while(buf.hasRemaining()) + { + String name = readString(); + Vector4f color = new Vector4f(buf.getFloat(), buf.getFloat(), buf.getFloat(), buf.getFloat()); + float shininess = buf.getFloat(); + int blend = buf.getInt(); + int fx = buf.getInt(); + List textures = new ArrayList(); + for(int i = 0; i < n_texs; i++) textures.add(getTexture(buf.getInt())); + ret.add(new Brush(name, color, shininess, blend, fx, textures)); + } + popLimit(); + this.brushes.addAll(ret); + return ret; + } + + private List vrts() throws IOException + { + chunk("VRTS"); + List ret = new ArrayList(); + int flags = buf.getInt(); + int tex_coord_sets = buf.getInt(); + int tex_coord_set_size = buf.getInt(); + while(buf.hasRemaining()) + { + Vector3f v = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()), n = null; + Vector4f color = null; + if((flags & 1) != 0) + { + n = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()); + } + if((flags & 2) != 0) + { + color = new Vector4f(buf.getFloat(), buf.getFloat(), buf.getFloat(), buf.getFloat()); + } + Vector4f[] tex_coords = new Vector4f[tex_coord_sets]; + for(int i = 0; i < tex_coord_sets; i++) + { + switch(tex_coord_set_size) + { + case 1: + tex_coords[i] = new Vector4f(buf.getFloat(), 0, 0, 1); + break; + case 2: + tex_coords[i] = new Vector4f(buf.getFloat(), buf.getFloat(), 0, 1); + break; + case 3: + tex_coords[i] = new Vector4f(buf.getFloat(), buf.getFloat(), buf.getFloat(), 1); + break; + case 4: + tex_coords[i] = new Vector4f(buf.getFloat(), buf.getFloat(), buf.getFloat(), buf.getFloat()); + break; + default: + logger.error(String.format("Unsupported number of texture coords: ", tex_coord_set_size)); + tex_coords[i] = new Vector4f(0, 0, 0, 1); + } + } + ret.add(new Vertex(v, n, color, tex_coords)); + } + popLimit(); + this.vertices.clear(); + this.vertices.addAll(ret); + return ret; + } + + private List tris() throws IOException + { + chunk("TRIS"); + List ret = new ArrayList(); + int brush_id = buf.getInt(); + while(buf.hasRemaining()) + { + ret.add(new Face(getVertex(buf.getInt()), getVertex(buf.getInt()), getVertex(buf.getInt()), getBrush(brush_id))); + } + popLimit(); + return ret; + } + + private Pair> mesh() throws IOException + { + chunk("MESH"); + int brush_id = buf.getInt(); + readHeader(); + vrts(); + List ret = new ArrayList(); + while(buf.hasRemaining()) + { + readHeader(); + ret.addAll(tris()); + } + popLimit(); + return Pair.of(getBrush(brush_id), ret); + } + + private List> bone() throws IOException + { + chunk("BONE"); + List> ret = new ArrayList>(); + while(buf.hasRemaining()) + { + ret.add(Pair.of(getVertex(buf.getInt()), buf.getFloat())); + } + popLimit(); + return ret; + } + + private final Deque>, Key>> animations = new ArrayDeque>, Key>>(); + + private Map keys() throws IOException + { + chunk("KEYS"); + Map ret = new HashMap(); + int flags = buf.getInt(); + Vector3f pos = null, scale = null; + Quat4f rot = null; + while(buf.hasRemaining()) + { + int frame = buf.getInt(); + if((flags & 1) != 0) + { + pos = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()); + } + if((flags & 2) != 0) + { + scale = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()); + } + if((flags & 4) != 0) + { + rot = readQuat(); + } + Key key = new Key(pos, scale, rot); + //logger.debug("Key: " + frame + " " + key); + Key oldKey = animations.peek().get(frame, null); + if(oldKey != null) + { + if(pos != null) + { + if(oldKey.getPos() != null) logger.error("Duplicate keys: %s and %s (ignored)", oldKey, key); + else key = new Key(oldKey.getPos(), key.getScale(), key.getRot()); + } + if(scale != null) + { + if(oldKey.getScale() != null) logger.error("Duplicate keys: %s and %s (ignored)", oldKey, key); + else key = new Key(key.getPos(), oldKey.getScale(), key.getRot()); + } + if(rot != null) + { + if(oldKey.getRot() != null) logger.error("Duplicate keys: %s and %s (ignored)", oldKey, key); + else key = new Key(key.getPos(), key.getScale(), oldKey.getRot()); + } + } + animations.peek().put(frame, Optional.>absent(), key); + ret.put(frame, key); + } + popLimit(); + return ret; + } + + private Triple anim() throws IOException + { + chunk("ANIM"); + int flags = buf.getInt(); + int frames = buf.getInt(); + float fps = buf.getFloat(); + popLimit(); + return Triple.of(flags, frames, fps); + } + + private Node node() throws IOException + { + chunk("NODE"); + animations.push(HashBasedTable.>, Key>create()); + Triple animData = null; + Animation animation = null; + Pair> mesh = null; + List> bone = null; + Map keys = new HashMap(); + List> nodes = new ArrayList>(); + String name = readString(); + Vector3f pos = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()); + Vector3f scale = new Vector3f(buf.getFloat(), buf.getFloat(), buf.getFloat()); + Quat4f rot = readQuat(); + while(buf.hasRemaining()) + { + readHeader(); + if (isChunk("MESH")) mesh = mesh(); + else if(isChunk("BONE")) bone = bone(); + else if(isChunk("KEYS")) keys.putAll(keys()); + else if(isChunk("NODE")) nodes.add(node()); + else if(isChunk("ANIM")) animData = anim(); + else skip(); + } + popLimit(); + Table>, Key> keyData = animations.pop(); + Node node; + if(mesh != null) + { + Node mNode = Node.create(name, pos, scale, rot, nodes, new Mesh(mesh)); + meshes.put(name, mNode); + node = mNode; + } + else if(bone != null) node = Node.create(name, pos, scale, rot, nodes, new Bone(bone)); + else node = Node.create(name, pos, scale, rot, nodes, new Pivot()); + if(animData == null) + { + for(Table.Cell>, Key> key : keyData.cellSet()) + { + logger.debug("KEY1: " + key + " " + node); + animations.peek().put(key.getRowKey(), key.getColumnKey().or(Optional.of(node)), key.getValue()); + } + } + else + { + node.setAnimation(animData, keyData); + } + return node; + } + + private Quat4f readQuat() + { + float w = buf.getFloat(); + float x = buf.getFloat(); + float y = buf.getFloat(); + float z = buf.getFloat(); + return new Quat4f(x, y, z, w); + } + + private void skip() + { + buf.position(buf.position() + length); + } + } + + // boilerplate below + + public List getTextures() + { + return textures; + } + + public List getBrushes() + { + return brushes; + } + + public Node getRoot() + { + return root; + } + + public ImmutableMap> getMeshes() + { + return meshes; + } + + public static class Texture + { + private final String path; + private final int flags; + private final int blend; + private final Vector2f pos; + private final Vector2f scale; + private final float rot; + + public Texture(String path, int flags, int blend, Vector2f pos, Vector2f scale, float rot) + { + this.path = path; + this.flags = flags; + this.blend = blend; + this.pos = pos; + this.scale = scale; + this.rot = rot; + } + + public String getPath() + { + return path; + } + + public int getFlags() + { + return flags; + } + + public int getBlend() + { + return blend; + } + + public Vector2f getPos() + { + return pos; + } + + public Vector2f getScale() + { + return scale; + } + + public float getRot() + { + return rot; + } + + @Override + public String toString() + { + return String.format("Texture [path=%s, flags=%s, blend=%s, pos=%s, scale=%s, rot=%s]", path, flags, blend, pos, scale, rot); + } + } + + public static class Brush + { + private final String name; + private final Vector4f color; + private final float shininess; + private final int blend; + private final int fx; + private final List textures; + + public Brush(String name, Vector4f color, float shininess, int blend, int fx, List textures) + { + this.name = name; + this.color = color; + this.shininess = shininess; + this.blend = blend; + this.fx = fx; + this.textures = textures; + } + + public String getName() + { + return name; + } + + public Vector4f getColor() + { + return color; + } + + public float getShininess() + { + return shininess; + } + + public int getBlend() + { + return blend; + } + + public int getFx() + { + return fx; + } + + public List getTextures() + { + return textures; + } + + @Override + public String toString() + { + return String.format("Brush [name=%s, color=%s, shininess=%s, blend=%s, fx=%s, textures=%s]", name, color, shininess, blend, fx, textures); + } + } + + public static class Vertex + { + private final Vector3f pos; + private final Vector3f normal; + private final Vector4f color; + private final Vector4f[] texCoords; + public Vertex(Vector3f pos, Vector3f normal, Vector4f color, Vector4f[] texCoords) + { + this.pos = pos; + this.normal = normal; + this.color = color; + this.texCoords = texCoords; + } + + public Vertex bake(Mesh mesh, Function, Matrix4f> animator) + { + // geometry + Float totalWeight = 0f; + Matrix4f t = new Matrix4f(); + if(mesh.getWeightMap().get(this).isEmpty()) + { + t.setIdentity(); + } + else + { + for(Pair> bone : mesh.getWeightMap().get(this)) + { + totalWeight += bone.getLeft(); + Matrix4f bm = animator.apply(bone.getRight()); + logger.debug("w:" + totalWeight + " bone: " + bone.getRight().getName() + " bm: \n" + bm); + bm.mul(bone.getLeft()); + t.add(bm); + logger.debug("t: \n" + t); + } + if(totalWeight != 0) t.mul(1f / totalWeight); + } + + logger.debug("t: \n" + t); + + // pos + Vector4f pos = new Vector4f(this.pos), newPos = new Vector4f(); + pos.w = 1; + t.transform(pos, newPos); + Vector3f rPos = new Vector3f(newPos.x / newPos.w, newPos.y / newPos.w, newPos.z / newPos.w); + + // normal + t.invert(); + t.transpose(); + Vector4f normal = new Vector4f(this.normal), newNormal = new Vector4f(); + normal.w = 1; + t.transform(normal, newNormal); + Vector3f rNormal = new Vector3f(newNormal.x / newNormal.w, newNormal.y / newNormal.w, newNormal.z / newNormal.w); + rNormal.normalize(); + + // texCoords TODO + return new Vertex(rPos, rNormal, color, texCoords); + } + + public Vector3f getPos() + { + return pos; + } + + public Vector3f getNormal() + { + return normal; + } + + public Vector4f getColor() + { + return color; + } + + public Vector4f[] getTexCoords() + { + return texCoords; + } + + @Override + public String toString() + { + return String.format("Vertex [pos=%s, normal=%s, color=%s, texCoords=%s]", pos, normal, color, java.util.Arrays.toString(texCoords)); + } + } + + public static class Face + { + private final Vertex v1, v2, v3; + private final Brush brush; + private final Vector3f normal; + + public Face(Vertex v1, Vertex v2, Vertex v3, Brush brush) + { + this(v1, v2, v3, brush, getNormal(v1, v2, v3)); + } + + public Face(Vertex v1, Vertex v2, Vertex v3, Brush brush, Vector3f normal) + { + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + this.brush = brush; + this.normal = normal; + } + + public Vertex getV1() + { + return v1; + } + + public Vertex getV2() + { + return v2; + } + + public Vertex getV3() + { + return v3; + } + + public Brush getBrush() + { + return brush; + } + + @Override + public String toString() + { + return String.format("Face [v1=%s, v2=%s, v3=%s]", v1, v2, v3); + } + + public Vector3f getNormal() + { + return normal; + } + + public static Vector3f getNormal(Vertex v1, Vertex v2, Vertex v3) + { + Vector3f a = new Vector3f(v2.getPos()); + a.sub(v1.getPos()); + Vector3f b = new Vector3f(v3.getPos()); + b.sub(v1.getPos()); + Vector3f c = new Vector3f(); + c.cross(a, b); + c.normalize(); + return c; + } + } + + public static class Key + { + private final Vector3f pos; + private final Vector3f scale; + private final Quat4f rot; + + public Key(Vector3f pos, Vector3f scale, Quat4f rot) + { + this.pos = pos; + this.scale = scale; + this.rot = rot; + } + + public Vector3f getPos() + { + return pos; + } + + public Vector3f getScale() + { + return scale; + } + + public Quat4f getRot() + { + return rot; + } + + @Override + public String toString() + { + return String.format("Key [pos=%s, scale=%s, rot=%s]", pos, scale, rot); + } + } + + public static class Animation + { + private final int flags; + private final int frames; + private final float fps; + private final ImmutableTable, Key> keys; + + public Animation(int flags, int frames, float fps, ImmutableTable, Key> keys) + { + this.flags = flags; + this.frames = frames; + this.fps = fps; + this.keys = keys; + } + + public int getFlags() + { + return flags; + } + + public int getFrames() + { + return frames; + } + + public float getFps() + { + return fps; + } + + public ImmutableTable, Key> getKeys() + { + return keys; + } + + @Override + public String toString() + { + return String.format("Animation [flags=%s, frames=%s, fps=%s, keys=...]", flags, frames, fps); + } + } + + public static interface IKind> + { + void setParent(Node parent); + Node getParent(); + } + + public static class Node> + { + private final String name; + private final Vector3f pos; + private final Vector3f scale; + private final Quat4f rot; + private final ImmutableMap> nodes; + private Animation animation; + private final K kind; + private Node> parent; + + public static > Node create(String name, Vector3f pos, Vector3f scale, Quat4f rot, List> nodes, K kind) + { + return new Node(name, pos, scale, rot, nodes, kind); + } + + public Node(String name, Vector3f pos, Vector3f scale, Quat4f rot, List> nodes, K kind) + { + this.name = name; + this.pos = pos; + this.scale = scale; + this.rot = rot; + this.nodes = buildNodeMap(nodes); + this.kind = kind; + kind.setParent(this); + for(Node child : this.nodes.values()) + { + child.setParent(this); + } + } + + public void setAnimation(Animation animation) + { + logger.debug("setAnimation " + animation + " " + this); + this.animation = animation; + Deque> q = new ArrayDeque>(nodes.values()); + + while(!q.isEmpty()) + { + Node node = q.pop(); + if(node.getAnimation() != null) continue; + node.setAnimation(animation); + q.addAll(node.getNodes().values()); + } + } + + public void setAnimation(Triple animData, Table>, Key> keyData) + { + ImmutableTable.Builder, Key> builder = ImmutableTable.builder(); + for(Table.Cell>, Key> key : keyData.cellSet()) + { + //logger.debug("KEY2: " + key + " " + this); + builder.put(key.getRowKey(), key.getColumnKey().or(this), key.getValue()); + } + setAnimation(new Animation(animData.getLeft(), animData.getMiddle(), animData.getRight(), builder.build())); + } + + private ImmutableMap> buildNodeMap(List> nodes) + { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for(Node node : nodes) + { + builder.put(node.getName(), node); + } + return builder.build(); + } + + public String getName() + { + return name; + } + + public K getKind() + { + return kind; + } + + public Vector3f getPos() + { + return pos; + } + + public Vector3f getScale() + { + return scale; + } + + public Quat4f getRot() + { + return rot; + } + + public ImmutableMap> getNodes() + { + return nodes; + } + + public Animation getAnimation() + { + return animation; + } + + public Node> getParent() + { + return parent; + } + + public void setParent(Node> parent) + { + this.parent = parent; + } + + @Override + public String toString() + { + return String.format("Node [name=%s, kind=%s, pos=%s, scale=%s, rot=%s, keys=..., nodes=..., animation=%s]", name, kind, pos, scale, rot, animation); + } + } + + public static class Pivot implements IKind + { + private Node parent; + + public void setParent(Node parent) + { + this.parent = parent; + } + + public Node getParent() + { + return parent; + } + } + + public static class Mesh implements IKind + { + private Node parent; + private final Brush brush; + private final ImmutableList faces; + + private Set> bones = new HashSet>(); + + private ImmutableMultimap>> weightMap = ImmutableMultimap.of(); + + public Mesh(Pair> data) + { + this.brush = data.getLeft(); + this.faces = ImmutableList.copyOf(data.getRight()); + } + + public ImmutableMultimap>> getWeightMap() + { + return weightMap; + } + + public ImmutableList bake(Function, Matrix4f> animator) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for(Face f : getFaces()) + { + Vertex v1 = f.getV1().bake(this, animator); + Vertex v2 = f.getV2().bake(this, animator); + Vertex v3 = f.getV3().bake(this, animator); + builder.add(new Face(v1, v2, v3, f.getBrush())); + } + return builder.build(); + } + + public Brush getBrush() + { + return brush; + } + + public ImmutableList getFaces() + { + return faces; + } + + public ImmutableSet> getBones() + { + return ImmutableSet.copyOf(bones); + } + + @Override + public String toString() + { + return String.format("Mesh [pivot=%s, brush=%s, data=...]", super.toString(), brush); + } + + @SuppressWarnings("unchecked") + public void setParent(Node parent) + { + this.parent = parent; + Deque> queue = new ArrayDeque>(parent.getNodes().values()); + while(!queue.isEmpty()) + { + Node node = queue.pop(); + if(node.getKind() instanceof Bone) + { + bones.add((Node)node); + queue.addAll(node.getNodes().values()); + } + } + ImmutableMultimap.Builder>> builder = ImmutableMultimap.builder(); + for(Node bone : getBones()) + { + for(Pair b : bone.getKind().getData()) + { + builder.put(b.getLeft(), Pair.of(b.getRight(), bone)); + logger.debug("Weight: " + b.getRight() + " " + bone.getName() + " " + b.getLeft().getPos()); + } + } + weightMap = builder.build(); + } + + public Node getParent() + { + return parent; + } + } + + public static class Bone implements IKind + { + private Node parent; + private final List> data; + + public Bone(List> data) + { + this.data = data; + } + + public List> getData() + { + return data; + } + + /*@Override + public String toString() + { + return String.format("Bone [data=%s]", data); + }*/ + + public void setParent(Node parent) + { + this.parent = parent; + } + + public Node getParent() + { + return parent; + } + } +} diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index f59ef26811..752a6af825 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -28,3 +28,10 @@ net/minecraft/item/ItemDye.applyBonemeal(Lnet/minecraft/item/ItemStack;Lnet/mine net/minecraft/server/management/ItemInWorldManager.removeBlock(Lnet/minecraft/util/BlockPos;Z)Z=|p_180235_1_,canHarvest net/minecraft/client/gui/GuiScreen.drawHoveringText(Ljava/util/List;IILnet/minecraft/client/gui/FontRenderer;)V=|p_146283_1_,p_146283_2_,p_146283_3_,font net/minecraft/block/state/BlockState.(Lnet/minecraft/block/Block;[Lnet/minecraft/block/properties/IProperty;Lcom/google/common/collect/ImmutableMap;)V=|p_i45663_1_,p_i45663_2_,unlistedProperties +net/minecraft/client/renderer/entity/RenderItem.applyVanillaTransform(Lnet/minecraft/client/renderer/block/model/ItemTransformVec3f;)V=|p_175034_1_ +net/minecraft/client/resources/model/ModelBakery.bakeModel(Lnet/minecraft/client/renderer/block/model/ModelBlock;Lnet/minecraftforge/client/model/ITransformation;Z)Lnet/minecraft/client/resources/model/IBakedModel;=|p_177578_1_,p_177578_2_,p_177578_3_ +net/minecraft/client/resources/model/ModelBakery.makeBakedQuad(Lnet/minecraft/client/renderer/block/model/BlockPart;Lnet/minecraft/client/renderer/block/model/BlockPartFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/util/EnumFacing;Lnet/minecraftforge/client/model/ITransformation;Z)Lnet/minecraft/client/renderer/block/model/BakedQuad;=|p_177589_1_,p_177589_2_,p_177589_3_,p_177589_4_,p_177589_5_,p_177589_6_ +net/minecraft/client/renderer/block/model/FaceBakery.makeBakedQuad(Ljavax/vecmath/Vector3f;Ljavax/vecmath/Vector3f;Lnet/minecraft/client/renderer/block/model/BlockPartFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/util/EnumFacing;Lnet/minecraftforge/client/model/ITransformation;Lnet/minecraft/client/renderer/block/model/BlockPartRotation;ZZ)Lnet/minecraft/client/renderer/block/model/BakedQuad;=|p_178414_1_,p_178414_2_,p_178414_3_,p_178414_4_,p_178414_5_,p_178414_6_,p_178414_7_,p_178414_8_,p_178414_9_ +net/minecraft/client/renderer/block/model/FaceBakery.makeQuadVertexData(Lnet/minecraft/client/renderer/block/model/BlockPartFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/util/EnumFacing;[FLnet/minecraftforge/client/model/ITransformation;Lnet/minecraft/client/renderer/block/model/BlockPartRotation;ZZ)[I=|p_178405_1_,p_178405_2_,p_178405_3_,p_178405_4_,p_178405_5_,p_178405_6_,p_178405_7_,p_178405_8_ +net/minecraft/client/renderer/block/model/FaceBakery.fillVertexData([IILnet/minecraft/util/EnumFacing;Lnet/minecraft/client/renderer/block/model/BlockPartFace;[FLnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraftforge/client/model/ITransformation;Lnet/minecraft/client/renderer/block/model/BlockPartRotation;ZZ)V=|p_178402_1_,p_178402_2_,p_178402_3_,p_178402_4_,p_178402_5_,p_178402_6_,p_178402_7_,p_178402_8_,p_178402_9_,p_178402_10_ +net/minecraft/client/renderer/block/model/FaceBakery.rotateVertex(Ljavax/vecmath/Vector3d;Lnet/minecraft/util/EnumFacing;ILnet/minecraftforge/client/model/ITransformation;Z)I=|p_178415_1_,p_178415_2_,p_178415_3_,p_178415_4_,p_178415_5_ diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index f35c533b29..11d7172bba 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -123,3 +123,27 @@ public net.minecraft.item.crafting.RecipesBanners$RecipeDuplicatePattern public net.minecraft.block.state.BlockState$StateImplementation protected net.minecraft.block.state.BlockState$StateImplementation (Lnet/minecraft/block/Block;Lcom/google/common/collect/ImmutableMap;)V protected net.minecraft.block.state.BlockState$StateImplementation field_177238_c # propertyValueTable +public net.minecraft.client.renderer.block.model.ModelBlock field_178315_d # parent +protected net.minecraft.client.resources.model.ModelBakery field_177602_b # LOCATIONS_BUILTIN_TEXTURES +protected net.minecraft.client.resources.model.ModelBakery field_177598_f # resourceManager +protected net.minecraft.client.resources.model.ModelBakery field_177599_g # sprites +protected net.minecraft.client.resources.model.ModelBakery field_177609_j # textureMap +protected net.minecraft.client.resources.model.ModelBakery field_177610_k # blockModelShapes +protected net.minecraft.client.resources.model.ModelBakery field_177605_n # bakedRegistry +protected net.minecraft.client.resources.model.ModelBakery field_177606_o # MODEL_GENERATED +protected net.minecraft.client.resources.model.ModelBakery field_177618_p # MODEL_COMPASS +protected net.minecraft.client.resources.model.ModelBakery field_177617_q # MODEL_CLOCK +protected net.minecraft.client.resources.model.ModelBakery field_177616_r # MODEL_ENTITY +protected net.minecraft.client.resources.model.ModelBakery func_177591_a(Ljava/util/Collection;)V # loadVariants +protected net.minecraft.client.resources.model.ModelBakery func_177569_a(Lnet/minecraft/client/renderer/block/model/ModelBlockDefinition;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V # registerVariant +protected net.minecraft.client.resources.model.ModelBakery func_177596_a(Lnet/minecraft/item/Item;)Ljava/util/List; # getVariantNames +protected net.minecraft.client.resources.model.ModelBakery func_177583_a(Ljava/lang/String;)Lnet/minecraft/util/ResourceLocation; # getItemLocation +protected net.minecraft.client.resources.model.ModelBakery func_177594_c(Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/block/model/ModelBlock; # loadModel +protected net.minecraft.client.resources.model.ModelBakery func_177592_e()V # registerVariantNames +protected net.minecraft.client.resources.model.ModelBakery func_177596_a(Lnet/minecraft/item/Item;)Ljava/util/List; # getVariantNames +protected net.minecraft.client.resources.model.ModelBakery func_177583_a(Ljava/lang/String;)Lnet/minecraft/util/ResourceLocation; # getItemLocation +protected net.minecraft.client.resources.model.ModelBakery func_177585_a(Lnet/minecraft/client/renderer/block/model/ModelBlock;)Ljava/util/Set; # getTextureLocations +protected net.minecraft.client.resources.model.ModelBakery func_177581_b(Lnet/minecraft/client/renderer/block/model/ModelBlock;)Z # hasItemModel +protected net.minecraft.client.resources.model.ModelBakery func_177587_c(Lnet/minecraft/client/renderer/block/model/ModelBlock;)Z # isCustomRenderer +protected net.minecraft.client.resources.model.ModelBakery func_177582_d(Lnet/minecraft/client/renderer/block/model/ModelBlock;)Lnet/minecraft/client/renderer/block/model/ModelBlock; # makeItemModel +protected net.minecraft.client.resources.model.ModelBakery func_177580_d(Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/util/ResourceLocation; # getModelLocation diff --git a/src/test/java/net/minecraftforge/debug/ModelLoaderRegistryDebug.java b/src/test/java/net/minecraftforge/debug/ModelLoaderRegistryDebug.java new file mode 100644 index 0000000000..8a8ecd6c8c --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ModelLoaderRegistryDebug.java @@ -0,0 +1,220 @@ +package net.minecraftforge.debug; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +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.IResourceManager; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.ModelRotation; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.client.model.Attributes; +import net.minecraftforge.client.model.ICustomModelLoader; +import net.minecraftforge.client.model.IFlexibleBakedModel; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.IModelState; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.client.model.b3d.B3DLoader; +import net.minecraftforge.common.property.ExtendedBlockState; +import net.minecraftforge.common.property.IExtendedBlockState; +import net.minecraftforge.common.property.IUnlistedProperty; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; + +import com.google.common.base.Function; +import com.google.common.primitives.Ints; + +@Mod(modid = ModelLoaderRegistryDebug.MODID, version = ModelLoaderRegistryDebug.VERSION) +public class ModelLoaderRegistryDebug +{ + public static final String MODID = "ForgeDebugModelLoaderRegistry"; + public static final String VERSION = "1.0"; + + @EventHandler + public void preInit(FMLPreInitializationEvent event) + { + //ModelLoaderRegistry.registerLoader(DummyModelLoader.instance); + B3DLoader.instance.addDomain(MODID.toLowerCase()); + GameRegistry.registerBlock(CustomModelBlock.instance, CustomModelBlock.name); + //ModelBakery.addVariantName(Item.getItemFromBlock(CustomModelBlock.instance), "forgedebug:dummymodel"); + ModelBakery.addVariantName(Item.getItemFromBlock(CustomModelBlock.instance), MODID.toLowerCase() + ":untitled2.b3d"); + } + + @EventHandler + public void init(FMLInitializationEvent event) + { + Item item = Item.getItemFromBlock(CustomModelBlock.instance); + //Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, 0, new ModelResourceLocation("forgedebug:dummymodel", "inventory")); + Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, 0, new ModelResourceLocation(MODID.toLowerCase() + ":untitled2.b3d", "inventory")); + } + + public static class CustomModelBlock extends Block + { + public static final CustomModelBlock instance = new CustomModelBlock(); + public static final String name = "CustomModelBlock"; + private int counter = 1; + private ExtendedBlockState state = new ExtendedBlockState(this, new IProperty[0], new IUnlistedProperty[]{ B3DLoader.B3DFrameProperty.instance }); + + private CustomModelBlock() + { + super(Material.iron); + setCreativeTab(CreativeTabs.tabBlock); + setUnlocalizedName(MODID + ":" + name); + } + + @Override + public boolean isOpaqueCube() { return false; } + + @Override + public boolean isFullCube() { return false; } + + @Override + public boolean isVisuallyOpaque() { return false; } + + @Override + public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) + { + IModel model = ModelLoaderRegistry.getModel(new ResourceLocation(MODID.toLowerCase(),"block/untitled2.b3d")); + B3DLoader.B3DState defaultState = ((B3DLoader.Wrapper)model).getDefaultState(); + B3DLoader.B3DState newState = new B3DLoader.B3DState(defaultState.getAnimation(), counter); + return ((IExtendedBlockState)this.state.getBaseState()).withProperty(B3DLoader.B3DFrameProperty.instance, newState); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ) + { + if(world.isRemote) + { + System.out.println("click " + counter); + if(player.isSneaking()) counter--; + else counter++; + //if(counter >= model.getNode().getKeys().size()) counter = 0; + world.markBlockRangeForRenderUpdate(pos, pos); + } + return false; + } + } + + public static class DummyModelLoader implements ICustomModelLoader + { + public static final DummyModelLoader instance = new DummyModelLoader(); + public static final ResourceLocation dummyTexture = new ResourceLocation("minecraft:blocks/dirt"); + + public boolean accepts(ResourceLocation modelLocation) + { + return modelLocation.getResourceDomain().equals("forgedebug") && modelLocation.getResourcePath().contains("dummymodel"); + } + + public IModel loadModel(ResourceLocation model) + { + return DummyModel.instance; + } + + public static enum DummyModel implements IModel + { + instance; + + public Collection getDependencies() + { + return Collections.emptyList(); + } + + public Collection getTextures() + { + return Collections.singletonList(dummyTexture); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function textures) + { + return new DummyBakedModel(textures.apply(dummyTexture)); + } + + public IModelState getDefaultState() + { + return ModelRotation.X0_Y0; + } + } + + public static class DummyBakedModel implements IFlexibleBakedModel + { + private final TextureAtlasSprite texture; + + public DummyBakedModel(TextureAtlasSprite texture) + { + this.texture = texture; + } + + public List getFaceQuads(EnumFacing side) + { + return Collections.emptyList(); + } + + private int[] vertexToInts(float x, float y, float z, int color, float u, float v) + { + return new int[] { + Float.floatToRawIntBits(x), + Float.floatToRawIntBits(y), + Float.floatToRawIntBits(z), + color, + Float.floatToRawIntBits(texture.getInterpolatedU(u)), + Float.floatToRawIntBits(texture.getInterpolatedV(v)), + 0 + }; + } + + public List getGeneralQuads() + { + List ret = new ArrayList(); + // 1 half-way rotated quad looking UP + ret.add(new BakedQuad(Ints.concat( + vertexToInts(0, .5f, .5f, -1, 0, 0), + vertexToInts(.5f, .5f, 1, -1, 0, 16), + vertexToInts(1, .5f, .5f, -1, 16, 16), + vertexToInts(.5f, .5f, 0, -1, 16, 0) + ), -1, EnumFacing.UP)); + return ret; + } + + public boolean isGui3d() { return true; } + + public boolean isAmbientOcclusion() { return true; } + + public boolean isBuiltInRenderer() { return false; } + + public TextureAtlasSprite getTexture() { return this.texture; } + + public ItemCameraTransforms getItemCameraTransforms() + { + return ItemCameraTransforms.DEFAULT; + } + + public VertexFormat getFormat() + { + return Attributes.DEFAULT_BAKED_FORMAT; + } + } + + public void onResourceManagerReload(IResourceManager resourceManager) {} + } +} diff --git a/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json b/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json new file mode 100644 index 0000000000..1ebdfc8c8a --- /dev/null +++ b/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal" : { "model" : "forgedebugmodelloaderregistry:untitled2.b3d" } + } +} diff --git a/src/test/resources/assets/forgedebugmodelloaderregistry/models/block/untitled2.b3d b/src/test/resources/assets/forgedebugmodelloaderregistry/models/block/untitled2.b3d new file mode 100644 index 0000000000..14e9c4d4aa Binary files /dev/null and b/src/test/resources/assets/forgedebugmodelloaderregistry/models/block/untitled2.b3d differ diff --git a/src/test/resources/assets/forgedebugmodelloaderregistry/textures/texture.png b/src/test/resources/assets/forgedebugmodelloaderregistry/textures/texture.png new file mode 100644 index 0000000000..e66fab9537 Binary files /dev/null and b/src/test/resources/assets/forgedebugmodelloaderregistry/textures/texture.png differ