Implement loader for ItemLayerModel.

Add support for specifying which layers are fullbright.
Fix improper generation of model edge. - Thanks to iChun and KnightMiner for the fix suggestions.
This commit is contained in:
David Quintana 2020-06-04 16:03:45 +02:00
parent 0c0603dc91
commit 28f7b5d885
5 changed files with 124 additions and 33 deletions

View file

@ -21,25 +21,31 @@ package net.minecraftforge.client.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.*;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.Direction;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec2f;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.TRSRTransformer;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Function;
/**
@ -59,15 +65,22 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
private static final Direction[] VERTICALS = {Direction.WEST, Direction.EAST};
private ImmutableList<Material> textures;
public ItemLayerModel(ImmutableList<Material> textures)
{
this.textures = textures;
}
private final ImmutableSet<Integer> fullbrightLayers;
public ItemLayerModel()
{
this.textures = null;
this(null, ImmutableSet.of());
}
public ItemLayerModel(ImmutableList<Material> textures)
{
this(textures, ImmutableSet.of());
}
public ItemLayerModel(@Nullable ImmutableList<Material> textures, ImmutableSet<Integer> fullbrightLayers)
{
this.textures = textures;
this.fullbrightLayers = fullbrightLayers;
}
private static ImmutableList<Material> getTextures(IModelConfiguration model)
@ -85,7 +98,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
//TODO: Verify
TransformationMatrix transform = modelTransform.getRotation();
ImmutableList<BakedQuad> quads = getQuadsForSprites(textures, transform, spriteGetter);
ImmutableList<BakedQuad> quads = getQuadsForSprites(textures, transform, spriteGetter, fullbrightLayers);
TextureAtlasSprite particle = spriteGetter.apply(
owner.isTexturePresent("particle") ? owner.resolveTexture("particle") : textures.get(0)
);
@ -94,17 +107,27 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
}
public static ImmutableList<BakedQuad> getQuadsForSprites(List<Material> textures, TransformationMatrix transform, Function<Material, TextureAtlasSprite> spriteGetter)
{
return getQuadsForSprites(textures, transform, spriteGetter, Collections.emptySet());
}
public static ImmutableList<BakedQuad> getQuadsForSprites(List<Material> textures, TransformationMatrix transform, Function<Material, TextureAtlasSprite> spriteGetter, Set<Integer> fullbrights)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
for(int i = 0; i < textures.size(); i++)
{
TextureAtlasSprite tas = spriteGetter.apply(textures.get(i));
builder.addAll(getQuadsForSprite(i, tas, transform));
builder.addAll(getQuadsForSprite(i, tas, transform, fullbrights.contains(i)));
}
return builder.build();
}
public static ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, TransformationMatrix transform)
{
return getQuadsForSprite(tint, sprite, transform, false);
}
public static ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, TransformationMatrix transform, boolean fullbright)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
@ -124,7 +147,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
ptu = true;
for(int u = 0; u < uMax; u++)
{
int alpha = sprite.getPixelRGBA(f, u, v) >> 24 & 0xFF;
int alpha = sprite.getPixelRGBA(f, u, vMax - v - 1) >> 24 & 0xFF;
boolean t = alpha / 255f <= 0.1f;
if (!t && alpha < 255)
@ -195,7 +218,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
// make quad [uStart, u]
int off = facing == Direction.DOWN ? 1 : 0;
builder.add(buildSideQuad(transform, facing, tint, sprite, uStart, v+off, u-uStart));
builder.add(buildSideQuad(transform, facing, tint, sprite, uStart, v+off, u-uStart, fullbright));
building = false;
}
else if (!building && face) // start new quad
@ -209,7 +232,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
// make quad [uStart, uEnd]
int off = facing == Direction.DOWN ? 1 : 0;
builder.add(buildSideQuad(transform, facing, tint, sprite, uStart, v+off, uEnd-uStart));
builder.add(buildSideQuad(transform, facing, tint, sprite, uStart, v+off, uEnd-uStart, fullbright));
}
}
}
@ -242,7 +265,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
// make quad [vStart, v]
int off = facing == Direction.EAST ? 1 : 0;
builder.add(buildSideQuad(transform, facing, tint, sprite, u+off, vStart, v-vStart));
builder.add(buildSideQuad(transform, facing, tint, sprite, u+off, vStart, v-vStart, fullbright));
building = false;
}
else if (!building && face) // start new quad
@ -256,20 +279,20 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
// make quad [vStart, vEnd]
int off = facing == Direction.EAST ? 1 : 0;
builder.add(buildSideQuad(transform, facing, tint, sprite, u+off, vStart, vEnd-vStart));
builder.add(buildSideQuad(transform, facing, tint, sprite, u+off, vStart, vEnd-vStart, fullbright));
}
}
}
// front
builder.add(buildQuad(transform, Direction.NORTH, sprite, tint,
builder.add(buildQuad(transform, Direction.NORTH, sprite, tint, fullbright,
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(transform, Direction.SOUTH, sprite, tint,
builder.add(buildQuad(transform, Direction.SOUTH, sprite, tint, fullbright,
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(),
@ -318,7 +341,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
}
}
private static BakedQuad buildSideQuad(TransformationMatrix transform, Direction side, int tint, TextureAtlasSprite sprite, int u, int v, int size)
private static BakedQuad buildSideQuad(TransformationMatrix transform, Direction side, int tint, TextureAtlasSprite sprite, int u, int v, int size, boolean fullbright)
{
final float eps = 1e-2f;
@ -357,7 +380,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
float v1 = 16f * (1f - y1 - dy);
return buildQuad(
transform, remap(side), sprite, tint,
transform, remap(side), sprite, tint, fullbright,
x0, y0, z0, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0),
x1, y1, z0, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
@ -371,7 +394,7 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
return side.getAxis() == Direction.Axis.Y ? side.getOpposite() : side;
}
private static BakedQuad buildQuad(TransformationMatrix transform, Direction side, TextureAtlasSprite sprite, int tint,
private static BakedQuad buildQuad(TransformationMatrix transform, Direction side, TextureAtlasSprite sprite, int tint, boolean fullbright,
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,
@ -381,24 +404,29 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
builder.setQuadTint(tint);
builder.setQuadOrientation(side);
builder.setApplyDiffuseLighting(false);
boolean hasTransform = !transform.isIdentity();
IVertexConsumer consumer = hasTransform ? new TRSRTransformer(builder, transform) : builder;
putVertex(consumer, side, x0, y0, z0, u0, v0);
putVertex(consumer, side, x1, y1, z1, u1, v1);
putVertex(consumer, side, x2, y2, z2, u2, v2);
putVertex(consumer, side, x3, y3, z3, u3, v3);
int uLight, vLight;
uLight = vLight = fullbright ? 15 : 0;
putVertex(consumer, side, x0, y0, z0, u0, v0, uLight, vLight);
putVertex(consumer, side, x1, y1, z1, u1, v1, uLight, vLight);
putVertex(consumer, side, x2, y2, z2, u2, v2, uLight, vLight);
putVertex(consumer, side, x3, y3, z3, u3, v3, uLight, vLight);
return builder.build();
}
private static void putVertex(IVertexConsumer consumer, Direction side, float x, float y, float z, float u, float v)
private static void putVertex(IVertexConsumer consumer, Direction side, float x, float y, float z, float u, float v, int uLight, int vLight)
{
VertexFormat format = consumer.getVertexFormat();
for(int e = 0; e < format.getElements().size(); e++)
{
switch(format.getElements().get(e).getUsage())
VertexFormatElement element = format.getElements().get(e);
outer:switch(element.getUsage())
{
case POSITION:
consumer.put(e, x, y, z, 1f);
@ -413,10 +441,14 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
consumer.put(e, offX, offY, offZ, 0f);
break;
case UV:
if(format.getElements().get(e).getIndex() == 0)
switch(element.getIndex())
{
case 0:
consumer.put(e, u, v, 0f, 1f);
break;
break outer;
case 2:
consumer.put(e, (uLight<<4)/32768.0f, (vLight<<4)/32768.0f, 0, 1);
break outer;
}
// else fallthrough to default
default:
@ -425,4 +457,30 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
}
}
}
public static class Loader implements IModelLoader<ItemLayerModel>
{
public static final Loader INSTANCE = new Loader();
@Override
public void onResourceManagerReload(IResourceManager resourceManager)
{
// nothing to do
}
@Override
public ItemLayerModel read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
{
ImmutableSet.Builder<Integer> fullbrightLayers = ImmutableSet.builder();
if (modelContents.has("fullbright_layers"))
{
JsonArray arr = JSONUtils.getJsonArray(modelContents, "fullbright_layers");
for(int i=0;i<arr.size();i++)
{
fullbrightLayers.add(arr.get(i).getAsInt());
}
}
return new ItemLayerModel(null, fullbrightLayers.build());
}
}
}

View file

@ -67,6 +67,7 @@ public class ModelLoaderRegistry
registerLoader(new ResourceLocation("forge","bucket"), DynamicBucketModel.Loader.INSTANCE);
registerLoader(new ResourceLocation("forge","composite"), CompositeModel.Loader.INSTANCE);
registerLoader(new ResourceLocation("forge","multi-layer"), MultiLayerModel.Loader.INSTANCE);
registerLoader(new ResourceLocation("forge","item-layers"), ItemLayerModel.Loader.INSTANCE);
// TODO: Implement as new model loaders
//registerLoader(new ResourceLocation("forge:b3d"), new ModelLoaderAdapter(B3DLoader.INSTANCE));

View file

@ -118,6 +118,10 @@ public class NewModelLoaderTest
new Item(new Item.Properties().group(ItemGroup.MISC))
);
public static RegistryObject<Item> item_layers = ITEMS.register("item_layers", () ->
new Item(new Item.Properties().group(ItemGroup.MISC))
);
public NewModelLoaderTest()
{
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();

View file

@ -1,3 +1,22 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.debug.client.rendering;
import net.minecraft.client.Minecraft;

View file

@ -0,0 +1,9 @@
{
"parent": "forge:item/default",
"loader": "forge:item-layers",
"textures": {
"layer0": "minecraft:item/coal",
"layer1": "minecraft:item/stick"
},
"fullbright_layers": [ 1 ]
}