Added ItemLayerModel - less awkward, simpler and faster version of ItemModelGenerator.
This commit is contained in:
parent
bae83e6724
commit
1582e401ed
5 changed files with 356 additions and 1 deletions
|
@ -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.<ResourceLocation>of());
|
||||
private final ImmutableList<ResourceLocation> textures;
|
||||
|
||||
public ItemLayerModel(ImmutableList<ResourceLocation> textures)
|
||||
{
|
||||
this.textures = textures;
|
||||
}
|
||||
|
||||
public ItemLayerModel(ModelBlock model)
|
||||
{
|
||||
this(getTextures(model));
|
||||
}
|
||||
|
||||
private static ImmutableList<ResourceLocation> getTextures(ModelBlock model)
|
||||
{
|
||||
ImmutableList.Builder<ResourceLocation> 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<ResourceLocation> getDependencies()
|
||||
{
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public Collection<ResourceLocation> getTextures()
|
||||
{
|
||||
return textures;
|
||||
}
|
||||
|
||||
public IModelState getDefaultState()
|
||||
{
|
||||
return TRSRTransformation.identity();
|
||||
}
|
||||
|
||||
public IModel retexture(ImmutableMap<String, String> textures)
|
||||
{
|
||||
ImmutableList.Builder<ResourceLocation> 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<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
|
||||
{
|
||||
ImmutableList.Builder<BakedQuad> 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<BakedQuad> quads;
|
||||
private final TextureAtlasSprite particle;
|
||||
private final VertexFormat format;
|
||||
|
||||
public BakedModel(ImmutableList<BakedQuad> 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<BakedQuad> getFaceQuads(EnumFacing side) { return ImmutableList.of(); }
|
||||
public List<BakedQuad> getGeneralQuads() { return quads; }
|
||||
public VertexFormat getFormat() { return format; }
|
||||
}
|
||||
|
||||
public static final ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format)
|
||||
{
|
||||
ImmutableList.Builder<BakedQuad> 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<VertexFormatElement>)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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -33,6 +33,7 @@ public class ModelLoaderRegistry
|
|||
{
|
||||
registerLoader(B3DLoader.instance);
|
||||
registerLoader(ModelFluid.FluidLoader.instance);
|
||||
registerLoader(ItemLayerModel.Loader.instance);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"forge_marker": 1,
|
||||
"variants": {
|
||||
"inventory": {
|
||||
"model": "forge:item-layer",
|
||||
"textures": {
|
||||
"layer0": "items/diamond_pickaxe"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue