From 1582e401ed439dc3b6e72bf2d2c91ec91a230ec8 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Tue, 23 Jun 2015 04:23:05 +0300 Subject: [PATCH] Added ItemLayerModel - less awkward, simpler and faster version of ItemModelGenerator. --- .../client/model/ItemLayerModel.java | 288 ++++++++++++++++++ .../client/model/ModelLoader.java | 2 +- .../client/model/ModelLoaderRegistry.java | 1 + .../debug/ItemLayerModelDebug.java | 55 ++++ .../blockstates/TestItem.json | 11 + 5 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/minecraftforge/client/model/ItemLayerModel.java create mode 100644 src/test/java/net/minecraftforge/debug/ItemLayerModelDebug.java create mode 100644 src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json diff --git a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java new file mode 100644 index 000000000..4276430c0 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java @@ -0,0 +1,288 @@ +package net.minecraftforge.client.model; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collection; +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.block.model.ModelBlock; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.FMLLog; + +import org.lwjgl.BufferUtils; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class ItemLayerModel implements IRetexturableModel { + + public static final ItemLayerModel instance = new ItemLayerModel(ImmutableList.of()); + private final ImmutableList textures; + + public ItemLayerModel(ImmutableList textures) + { + this.textures = textures; + } + + public ItemLayerModel(ModelBlock model) + { + this(getTextures(model)); + } + + private static ImmutableList getTextures(ModelBlock model) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for(int i = 0; model.isTexturePresent("layer" + i); i++) + { + builder.add(new ResourceLocation(model.resolveTextureName("layer" + i))); + } + return builder.build(); + } + + public Collection getDependencies() + { + return ImmutableList.of(); + } + + public Collection getTextures() + { + return textures; + } + + public IModelState getDefaultState() + { + return TRSRTransformation.identity(); + } + + public IModel retexture(ImmutableMap textures) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for(int i = 0; i < textures.size(); i++) + { + if(textures.containsKey("layer" + i)) + { + builder.add(new ResourceLocation(textures.get("layer" + i))); + } + } + return new ItemLayerModel(builder.build()); + } + + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for(int i = 0; i < textures.size(); i++) + { + TextureAtlasSprite sprite = bakedTextureGetter.apply(textures.get(i)); + builder.addAll(getQuadsForSprite(i, sprite, format)); + } + TextureAtlasSprite particle = bakedTextureGetter.apply(textures.isEmpty() ? new ResourceLocation("missingno") : textures.get(0)); + return new BakedModel(builder.build(), particle, format); + } + + public static class BakedModel implements IFlexibleBakedModel + { + private final ImmutableList quads; + private final TextureAtlasSprite particle; + private final VertexFormat format; + + public BakedModel(ImmutableList quads, TextureAtlasSprite particle, VertexFormat format) + { + this.quads = quads; + this.particle = particle; + this.format = format; + } + + public boolean isAmbientOcclusion() { return true; } + public boolean isGui3d() { return false; } + public boolean isBuiltInRenderer() { return false; } + public TextureAtlasSprite getTexture() { return null; } + public ItemCameraTransforms getItemCameraTransforms() { return ItemCameraTransforms.DEFAULT; } + public List getFaceQuads(EnumFacing side) { return ImmutableList.of(); } + public List getGeneralQuads() { return quads; } + public VertexFormat getFormat() { return format; } + } + + public static final ImmutableList getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format) + { + ImmutableList.Builder builder = ImmutableList.builder(); + + int uMax = sprite.getIconWidth(); + int vMax = sprite.getIconHeight(); + + ByteBuffer buf = BufferUtils.createByteBuffer(4 * format.getNextOffset()); + int[] data; + + for(int f = 0; f < sprite.getFrameCount(); f++) + { + int[] pixels = sprite.getFrameTextureData(f)[0]; + boolean ptu; + boolean[] ptv = new boolean[uMax]; + Arrays.fill(ptv, true); + for(int v = 0; v < vMax; v++) + { + ptu = true; + for(int u = 0; u < uMax; u++) + { + boolean t = (pixels[u + (vMax - 1 - v) * uMax] >> 24 & 0xFF) == 0; + if(ptu && !t) // left - transparent, right - opaque + { + builder.add(buildSideQuad(buf, format, EnumFacing.WEST, tint, sprite, u, v)); + } + if(!ptu && t) // left - opaque, right - transparent + { + builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, u, v)); + } + if(ptv[u] && !t) // up - transparent, down - opaque + { + builder.add(buildSideQuad(buf, format, EnumFacing.UP, tint, sprite, u, v)); + } + if(!ptv[u] && t) // up - opaque, down - transparent + { + builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, v)); + } + ptu = t; + ptv[u] = t; + } + if(!ptu) // last - opaque + { + builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, uMax, v)); + } + } + // last line + for(int u = 0; u < uMax; u++) + { + if(!ptv[u]) + { + builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, vMax)); + } + } + } + // front + builder.add(buildQuad(buf, format, EnumFacing.SOUTH, tint, + 0, 0, 7.5f / 16f, sprite.getMinU(), sprite.getMaxV(), + 0, 1, 7.5f / 16f, sprite.getMinU(), sprite.getMinV(), + 1, 1, 7.5f / 16f, sprite.getMaxU(), sprite.getMinV(), + 1, 0, 7.5f / 16f, sprite.getMaxU(), sprite.getMaxV() + )); + // back + builder.add(buildQuad(buf, format, EnumFacing.NORTH, tint, + 0, 0, 8.5f / 16f, sprite.getMinU(), sprite.getMaxV(), + 1, 0, 8.5f / 16f, sprite.getMaxU(), sprite.getMaxV(), + 1, 1, 8.5f / 16f, sprite.getMaxU(), sprite.getMinV(), + 0, 1, 8.5f / 16f, sprite.getMinU(), sprite.getMinV() + )); + return builder.build(); + } + + private static BakedQuad buildSideQuad(ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v) + { + float x0 = (float)u / sprite.getIconWidth(); + float y0 = (float)v / sprite.getIconHeight(); + float x1 = x0, y1 = y0; + float z1 = 7.5f / 16f, z2 = 8.5f / 16f; + switch(side) + { + case WEST: + z1 = 8.5f / 16f; + z2 = 7.5f / 16f; + case EAST: + y1 = (v + 1f) / sprite.getIconHeight(); + break; + case DOWN: + z1 = 8.5f / 16f; + z2 = 7.5f / 16f; + case UP: + x1 = (u + 1f) / sprite.getIconWidth(); + break; + default: + throw new IllegalArgumentException("can't handle z-oriented side"); + } + float u0 = 16f * (x0 - side.getDirectionVec().getX() * 1e-2f / sprite.getIconWidth()); + float u1 = 16f * (x1 - side.getDirectionVec().getX() * 1e-2f / sprite.getIconWidth()); + float v0 = 16f * (1f - y0 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); + float v1 = 16f * (1f - y1 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); + return buildQuad( + buf, format, side.getOpposite(), tint, // getOpposite is related either to the swapping of V direction, or something else + x0, y0, z1, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0), + x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), + x1, y1, z2, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), + x0, y0, z2, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0) + ); + } + + private static final BakedQuad buildQuad( + ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, + float x0, float y0, float z0, float u0, float v0, + float x1, float y1, float z1, float u1, float v1, + float x2, float y2, float z2, float u2, float v2, + float x3, float y3, float z3, float u3, float v3) + { + buf.clear(); + putVertex(buf, format, side, x0, y0, z0, u0, v0); + putVertex(buf, format, side, x1, y1, z1, u1, v1); + putVertex(buf, format, side, x2, y2, z2, u2, v2); + putVertex(buf, format, side, x3, y3, z3, u3, v3); + buf.flip(); + int[] data = new int[4 * format.getNextOffset() / 4]; + buf.asIntBuffer().get(data); + return new BakedQuad(data, tint, side); + } + + private static void put(ByteBuffer buf, VertexFormatElement e, Float... fs) + { + Attributes.put(buf, e, true, 0f, fs); + } + + private static void putVertex(ByteBuffer buf, VertexFormat format, EnumFacing side, float x, float y, float z, float u, float v) + { + for(VertexFormatElement e : (List)format.getElements()) + { + switch(e.getUsage()) + { + case POSITION: + put(buf, e, x, y, z, 1f); + break; + case COLOR: + put(buf, e, 1f, 1f, 1f, 1f); + break; + case UV: + put(buf, e, u, v, 0f, 1f); + break; + case NORMAL: + put(buf, e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f); + break; + default: + put(buf, e); + break; + } + } + } + + public static enum Loader implements ICustomModelLoader + { + instance; + + public void onResourceManagerReload(IResourceManager resourceManager) {} + + public boolean accepts(ResourceLocation modelLocation) + { + return modelLocation.getResourceDomain().equals("forge") && ( + modelLocation.getResourcePath().equals("item-layer") || + modelLocation.getResourcePath().equals("models/block/item-layer") || + modelLocation.getResourcePath().equals("models/item/item-layer")); + } + + public IModel loadModel(ResourceLocation modelLocation) + { + return ItemLayerModel.instance; + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index 04c6e785d..2327d5b2c 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -320,7 +320,7 @@ public class ModelLoader extends ModelBakery 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(hasItemModel(model)) return new ItemLayerModel(model).bake(state, format, bakedTextureGetter);//model = makeItemModel(model); if(model == null) return getMissingModel().bake(state, format, bakedTextureGetter); 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), state instanceof UVLock), Attributes.DEFAULT_BAKED_FORMAT); diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java index f5264f1d4..608e304ca 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -33,6 +33,7 @@ public class ModelLoaderRegistry { registerLoader(B3DLoader.instance); registerLoader(ModelFluid.FluidLoader.instance); + registerLoader(ItemLayerModel.Loader.instance); } /* diff --git a/src/test/java/net/minecraftforge/debug/ItemLayerModelDebug.java b/src/test/java/net/minecraftforge/debug/ItemLayerModelDebug.java new file mode 100644 index 000000000..78f74ed62 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ItemLayerModelDebug.java @@ -0,0 +1,55 @@ +package net.minecraftforge.debug; + +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; + +@Mod(modid = ItemLayerModelDebug.MODID, version = ItemLayerModelDebug.VERSION) +public class ItemLayerModelDebug +{ + public static final String MODID = "ForgeDebugItemLayerModel"; + public static final String VERSION = "1.0"; + + @SidedProxy(serverSide = "net.minecraftforge.debug.ItemLayerModelDebug$CommonProxy", clientSide = "net.minecraftforge.debug.ItemLayerModelDebug$ClientProxy") + public static CommonProxy proxy; + + @EventHandler + public void preInit(FMLPreInitializationEvent event) { proxy.preInit(event); } + + public static class CommonProxy + { + public void preInit(FMLPreInitializationEvent event) + { + GameRegistry.registerItem(TestItem.instance, TestItem.name); + } + } + + public static class ClientProxy extends CommonProxy + { + private static ModelResourceLocation modelLocation = new ModelResourceLocation(MODID.toLowerCase() + ":" + TestItem.name, "inventory"); + @Override + public void preInit(FMLPreInitializationEvent event) + { + super.preInit(event); + ModelLoader.setCustomModelResourceLocation(TestItem.instance, 0, modelLocation); + } + } + + public static final class TestItem extends Item + { + public static final TestItem instance = new TestItem(); + public static final String name = "TestItem"; + + private TestItem() + { + setCreativeTab(CreativeTabs.tabBlock); + setUnlocalizedName(MODID + ":" + name); + } + } +} diff --git a/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json b/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json new file mode 100644 index 000000000..99e0306c6 --- /dev/null +++ b/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json @@ -0,0 +1,11 @@ +{ + "forge_marker": 1, + "variants": { + "inventory": { + "model": "forge:item-layer", + "textures": { + "layer0": "items/diamond_pickaxe" + } + } + } +}