Added model loader registry

Entry point: ModelLoaderRegistry
loader interface: ICustomModelLoader
custom model: IModel

ModelLoader is responsible for splicing into vanilla model system.
(you probably don't need to use it directly)

Interop with vanilla models isn't great yet
(vanilla models can't refer to custom ones as parents), will improve in
the future.

Includes loader for B3D models, with animation support
(net.minecraftforge.client.model.b3d).
Blender export plugin with compatible coordinate system:
https://github.com/RainWarrior/B3DExport

OBJ loader is being written, will be included at some point in the
future. For now you can convert OBJ to B3D via blender, or wait.
This commit is contained in:
RainWarrior 2014-12-28 07:10:54 +03:00
parent 8b6ef01ecc
commit 6eba0ebb93
31 changed files with 3773 additions and 5 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<BakedQuad> getFaceQuads(EnumFacing side);
List<BakedQuad> 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<BakedQuad> getFaceQuads(EnumFacing side)
{
return parent.getFaceQuads(side);
}
@SuppressWarnings("unchecked")
public List<BakedQuad> 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);
}
}
}

View file

@ -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<ResourceLocation> getDependencies();
/*
* Returns all texture locations that this model depends on.
* Assume that returned collection is immutable.
*/
Collection<ResourceLocation> 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<BakedQuad> 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<ResourceLocation, TextureAtlasSprite> bakedTextureGetter);
/*
* Default state this model will be baked with.
* See IModelState.
*/
IModelState getDefaultState();
}

View file

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

View file

@ -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<IModelPart, TRSRTransformation>
{
/*
* returns the transformation (in the local coordinates) that needs to be applied to the specific part of the model.
*/
@Override
TRSRTransformation apply(IModelPart part);
}

View file

@ -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<IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType);
}

View file

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

View file

@ -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<IModelPart, TRSRTransformation> map;
private final TRSRTransformation def;
public MapModelState(Map<IModelPart, TRSRTransformation> map)
{
this(map, TRSRTransformation.identity());
}
public MapModelState(Map<IModelPart, TRSRTransformation> 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);
}
}

View file

@ -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<ModelResourceLocation, IModel> stateModels = new HashMap<ModelResourceLocation, IModel>();
private final Set<ResourceLocation> resolveTextures = new HashSet<ResourceLocation>();
private final Set<ResourceLocation> uvLocked = new HashSet<ResourceLocation>();
private final Set<ResourceLocation> textures = new HashSet<ResourceLocation>();
private final Set<ResourceLocation> loadingModels = new HashSet<ResourceLocation>();
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<ResourceLocation, TextureAtlasSprite> textureGetter = Functions.forMap(sprites, textureMap.getMissingSprite());
for(Entry<ModelResourceLocation, IModel> e : stateModels.entrySet())
{
bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter));
}
return bakedRegistry;
}
private void loadBlocks()
{
Map<IBlockState, ModelResourceLocation> 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<String>)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<ResourceLocation> getDependencies()
{
if(model.getParentLocation() == null || model.getParentLocation().getResourcePath().startsWith("builtin/")) return Collections.emptyList();
return Collections.singletonList(model.getParentLocation());
}
public Collection<ResourceLocation> 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<ResourceLocation> builder = ImmutableSet.builder();
builder.add(new ResourceLocation(model.resolveTextureName("particle")));
if(hasItemModel(model))
{
for(String s : (List<String>)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<ResourceLocation, TextureAtlasSprite> 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<ResourceLocation> getDependencies()
{
return model.getDependencies();
}
public Collection<ResourceLocation> getTextures()
{
return model.getTextures();
}
public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
return model.bake(state, format, bakedTextureGetter);
}
public IModelState getDefaultState()
{
return model.getDefaultState();
}
}
private class WeightedRandomModel implements IModel
{
private final List<Variant> variants;
private final List<ResourceLocation> locations = new ArrayList<ResourceLocation>();
private final List<IModel> models = new ArrayList<IModel>();
private final IModelState defaultState;
public WeightedRandomModel(Variants variants)
{
this.variants = variants.getVariants();
ImmutableMap.Builder<IModelPart, TRSRTransformation> builder = ImmutableMap.builder();
for(Variant v : (List<Variant>)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<ResourceLocation> getDependencies()
{
return ImmutableList.copyOf(locations);
}
public Collection<ResourceLocation> getTextures()
{
/*ImmutableSet.Builder<ResourceLocation> 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<ResourceLocation, TextureAtlasSprite> 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();
}
}
}
}

View file

@ -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<ICustomModelLoader> loaders = new HashSet<ICustomModelLoader>();
private static final Map<ResourceLocation, IModel> cache = new HashMap<ResourceLocation, IModel>();
/*
* 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();
}
}

View file

@ -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<Matrix3f, Vector3f> pair = toAffine(matrix);
Triple<Quat4f, Vector3f, Quat4f> 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<Quat4f, Vector3f, Quat4f> 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<Float, Float> 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<Float, Float> 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<Float, Float> 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<Float, Float> 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<Matrix3f, Vector3f> 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;
}
}

View file

@ -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<String> enabledDomains = new HashSet<String>();
private final Map<ResourceLocation, B3DModel> cache = new HashMap<ResourceLocation, B3DModel>();
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<Mesh>)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<Triple<Animation, Node<?>, Integer>, TRSRTransformation> cache = CacheBuilder.newBuilder()
.maximumSize(16384)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new CacheLoader<Triple<Animation, Node<?>, Integer>, TRSRTransformation>()
{
public TRSRTransformation load(Triple<Animation, Node<?>, Integer> key) throws Exception
{
return getNodeMatrix(key.getLeft(), key.getMiddle(), key.getRight());
}
});
public TRSRTransformation getNodeMatrix(Node<?> node)
{
return cache.getUnchecked(Triple.<Animation, Node<?>, 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<B3DState>
{
instance;
public String getName()
{
return "B3DFrame";
}
public boolean isValid(B3DState value)
{
return value instanceof B3DState;
}
public Class<B3DState> getType()
{
return B3DState.class;
}
public String valueToString(B3DState value)
{
return value.toString();
}
}
public static class PartWrapper<K extends IKind<K>> implements IModelPart
{
private final Node<K> node;
public static <K extends IKind<K>> PartWrapper<K> create(Node<K> node)
{
return new PartWrapper<K>(node);
}
public PartWrapper(Node<K> node)
{
this.node = node;
}
public Node<K> getNode()
{
return node;
}
}
public static class Wrapper extends PartWrapper<Mesh> implements IModel
{
private final ResourceLocation location;
private final ImmutableMap<String, ResourceLocation> textures;
public Wrapper(ResourceLocation location, List<Texture> textures, B3DModel.Node<Mesh> mesh)
{
this(location, buildTextures(location, textures), mesh);
}
public Wrapper(ResourceLocation location, ImmutableMap<String, ResourceLocation> textures, B3DModel.Node<Mesh> mesh)
{
super(mesh);
this.location = location;
this.textures = textures;
}
private static ImmutableMap<String, ResourceLocation> buildTextures(ResourceLocation location, List<Texture> textures)
{
ImmutableMap.Builder<String, ResourceLocation> 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<ResourceLocation> getDependencies()
{
// no dependencies for in-file models
// FIXME maybe add child meshes
return Collections.emptyList();
}
public Collection<ResourceLocation> getTextures()
{
return textures.values();
}
public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap.Builder<String, TextureAtlasSprite> 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<String, ResourceLocation> 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<String, TextureAtlasSprite> textures;
private final ByteBuffer buf;
private ImmutableList<BakedQuad> 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<String, TextureAtlasSprite> textures)
{
this.model = model;
this.state = state;
this.format = format;
this.textures = textures;
buf = BufferUtils.createByteBuffer(VERTICES_IN_QUAD * format.getNextOffset());
}
public List<BakedQuad> getFaceQuads(EnumFacing side)
{
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
public List<BakedQuad> getGeneralQuads()
{
if(quads == null)
{
Node<Mesh> mesh = model.getNode();
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
for(Node<?> child : mesh.getNodes().values())
{
if(child.getKind() instanceof Mesh)
{
Node<Mesh> childMesh = (Node<Mesh>)child;
builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads());
}
}
Multimap<Vertex, Pair<Float, Node<Bone>>> weightMap = mesh.getKind().getWeightMap();
Collection<Face> faces = mesh.getKind().getFaces();
faces = mesh.getKind().bake(new Function<Node<?>, 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<Texture> 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<VertexFormatElement>)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<Integer, BakedWrapper> cache = new HashMap<Integer, BakedWrapper>();
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;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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.<init>(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_

View file

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

View file

@ -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<ResourceLocation> getDependencies()
{
return Collections.emptyList();
}
public Collection<ResourceLocation> getTextures()
{
return Collections.singletonList(dummyTexture);
}
public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> 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<BakedQuad> 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<BakedQuad> getGeneralQuads()
{
List<BakedQuad> ret = new ArrayList<BakedQuad>();
// 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) {}
}
}

View file

@ -0,0 +1,5 @@
{
"variants": {
"normal" : { "model" : "forgedebugmodelloaderregistry:untitled2.b3d" }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B