Model system improvements:

- Port some things I did in 1.14 which I couldn't do in 1.15 due to breaking changes.
- Fix multi-layer block models not working (1.16 RenderType doesn't override toString the same way anymore)
- Implement multi-layer item rendering.
- Improve CompositeModel submodel data passing.
This commit is contained in:
David Quintana 2020-07-02 17:05:20 +02:00
parent 45152c6073
commit ce3d8b40cf
17 changed files with 749 additions and 202 deletions

View file

@ -18,16 +18,30 @@
p_229111_4_.func_227861_a_(-0.5D, -0.5D, -0.5D);
if (!p_229111_8_.func_188618_c() && (p_229111_1_.func_77973_b() != Items.field_203184_eO || flag)) {
boolean flag1;
@@ -132,7 +132,7 @@
@@ -105,7 +105,8 @@
} else {
flag1 = true;
}
-
+ if (p_229111_8_.isLayered()) { net.minecraftforge.client.ForgeHooksClient.drawItemLayered(this, p_229111_8_, p_229111_1_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_, flag1); }
+ else {
RenderType rendertype = RenderTypeLookup.func_239219_a_(p_229111_1_, flag1);
IVertexBuilder ivertexbuilder;
if (p_229111_1_.func_77973_b() == Items.field_151111_aL && p_229111_1_.func_77962_s()) {
@@ -129,10 +130,10 @@
} else {
ivertexbuilder = func_229113_a_(p_229111_5_, rendertype, true, p_229111_1_.func_77962_s());
}
-
this.func_229114_a_(p_229111_8_, p_229111_1_, p_229111_6_, p_229111_7_, p_229111_4_, ivertexbuilder);
+ }
} else {
- ItemStackTileEntityRenderer.field_147719_a.func_239207_a_(p_229111_1_, p_229111_2_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_);
+ p_229111_1_.func_77973_b().getItemStackTileEntityRenderer().func_239207_a_(p_229111_1_, p_229111_2_, p_229111_4_, p_229111_5_, p_229111_6_, p_229111_7_);
}
p_229111_4_.func_227865_b_();
@@ -172,7 +172,7 @@
@@ -172,7 +173,7 @@
float f = (float)(i >> 16 & 255) / 255.0F;
float f1 = (float)(i >> 8 & 255) / 255.0F;
float f2 = (float)(i & 255) / 255.0F;
@ -36,7 +50,7 @@
}
}
@@ -263,6 +263,7 @@
@@ -263,6 +264,7 @@
crashreportcategory.func_189529_a("Item Type", () -> {
return String.valueOf((Object)p_239387_2_.func_77973_b());
});
@ -44,7 +58,7 @@
crashreportcategory.func_189529_a("Item Damage", () -> {
return String.valueOf(p_239387_2_.func_77952_i());
});
@@ -294,18 +295,16 @@
@@ -294,18 +296,16 @@
irendertypebuffer$impl.func_228461_a_();
}
@ -67,7 +81,7 @@
this.func_181565_a(bufferbuilder, p_180453_3_ + 2, p_180453_4_ + 13, 13, 2, 0, 0, 0, 255);
this.func_181565_a(bufferbuilder, p_180453_3_ + 2, p_180453_4_ + 13, i, 1, j >> 16 & 255, j >> 8 & 255, j & 255, 255);
RenderSystem.enableBlend();
@@ -343,4 +342,9 @@
@@ -343,4 +343,9 @@
public void func_195410_a(IResourceManager p_195410_1_) {
this.field_175059_m.func_178085_b();
}

View file

@ -810,4 +810,17 @@ public class ForgeHooksClient
MinecraftForge.EVENT_BUS.post(event);
return event;
}
public static void drawItemLayered(ItemRenderer renderer, IBakedModel modelIn, ItemStack itemStackIn, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, boolean fabulous)
{
for(com.mojang.datafixers.util.Pair<IBakedModel,RenderType> layerModel : modelIn.getLayerModels(itemStackIn, fabulous)) {
modelIn = layerModel.getFirst();
RenderType rendertype = layerModel.getSecond();
net.minecraftforge.client.ForgeHooksClient.setRenderLayer(rendertype); // neded for compatibility with MultiLayerModels
IVertexBuilder ivertexbuilder = ItemRenderer.getBuffer(bufferIn, rendertype, true, itemStackIn.hasEffect());
renderer.renderModel(modelIn, itemStackIn, combinedLightIn, combinedOverlayIn, matrixStackIn, ivertexbuilder);
}
net.minecraftforge.client.ForgeHooksClient.setRenderLayer(null);
}
}

View file

@ -0,0 +1,142 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2020.
*
* 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.client;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.util.NonNullLazy;
import net.minecraftforge.common.util.NonNullSupplier;
import org.lwjgl.opengl.GL11;
public enum ForgeRenderTypes
{
/**
* A cached copy of {@link ForgeRenderTypes#getUnsortedTranslucent(ResourceLocation)}
* for use in item models and TileEntityRenderers that use the block/item atlas.
*/
ITEM_UNSORTED_TRANSLUCENT(()-> getUnsortedTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE)),
ITEM_UNLIT_TRANSLUCENT(()-> getUnlitTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE)),
ITEM_UNSORTED_UNLIT_TRANSLUCENT(()-> getUnlitTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE, false));
/**
* @return A RenderType fit for translucent item/entity rendering, but with depth sorting disabled.
*/
public static RenderType getUnsortedTranslucent(ResourceLocation textureLocation)
{
return Internal.unsortedTranslucent(textureLocation);
}
/**
* @return A RenderType fit for translucent item/entity rendering, but with diffuse lighting disabled
* so that fullbright quads look correct.
*/
public static RenderType getUnlitTranslucent(ResourceLocation textureLocation)
{
return getUnlitTranslucent(textureLocation, true);
}
/**
* @return A RenderType fit for translucent item/entity rendering, but with diffuse lighting disabled
* so that fullbright quads look correct.
* @param sortingEnabled If false, depth sorting will not be performed.
*/
public static RenderType getUnlitTranslucent(ResourceLocation textureLocation, boolean sortingEnabled)
{
return Internal.unlitTranslucent(textureLocation, sortingEnabled);
}
/**
* @return Same as {@link RenderType#getEntityCutout(ResourceLocation)}, but with mipmapping enabled.
*/
public static RenderType getEntityCutoutMipped(ResourceLocation textureLocation)
{
return Internal.entityCutoutMipped(textureLocation);
}
// ----------------------------------------
// Implementation details below this line
// ----------------------------------------
private final NonNullSupplier<RenderType> renderTypeSupplier;
ForgeRenderTypes(NonNullSupplier<RenderType> renderTypeSupplier)
{
// Wrap in a Lazy<> to avoid running the supplier more than once.
this.renderTypeSupplier = NonNullLazy.of(renderTypeSupplier);
}
public RenderType get()
{
return renderTypeSupplier.get();
}
private static class Internal extends RenderType
{
private Internal(String name, VertexFormat fmt, int glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable)
{
super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable);
throw new IllegalStateException("This class must not be instantiated");
}
public static RenderType unsortedTranslucent(ResourceLocation textureLocation)
{
final boolean sortingEnabled = false;
State renderState = State.getBuilder()
.texture(new TextureState(textureLocation, false, false))
.transparency(TRANSLUCENT_TRANSPARENCY)
.diffuseLighting(DIFFUSE_LIGHTING_ENABLED)
.alpha(DEFAULT_ALPHA)
.cull(CULL_DISABLED)
.lightmap(LIGHTMAP_ENABLED)
.overlay(OVERLAY_ENABLED)
.build(true);
return makeType("entity_unsorted_translucent", DefaultVertexFormats.ENTITY, GL11.GL_QUADS, 256, true, sortingEnabled, renderState);
}
public static RenderType unlitTranslucent(ResourceLocation textureLocation, boolean sortingEnabled)
{
State renderState = State.getBuilder()
.texture(new TextureState(textureLocation, false, false))
.transparency(TRANSLUCENT_TRANSPARENCY)
.alpha(DEFAULT_ALPHA)
.cull(CULL_DISABLED)
.lightmap(LIGHTMAP_ENABLED)
.overlay(OVERLAY_ENABLED)
.build(true);
return makeType("entity_unlit_translucent", DefaultVertexFormats.ENTITY, GL11.GL_QUADS, 256, true, sortingEnabled, renderState);
}
public static RenderType entityCutoutMipped(ResourceLocation locationIn) {
RenderType.State rendertype$state = RenderType.State.getBuilder()
.texture(new RenderState.TextureState(locationIn, false, true))
.transparency(NO_TRANSPARENCY)
.diffuseLighting(DIFFUSE_LIGHTING_ENABLED)
.alpha(DEFAULT_ALPHA)
.lightmap(LIGHTMAP_ENABLED)
.overlay(OVERLAY_ENABLED)
.build(true);
return makeType("entity_cutout_mipped", DefaultVertexFormats.ENTITY, 7, 256, true, false, rendertype$state);
}
}
}

View file

@ -19,19 +19,25 @@
package net.minecraftforge.client.extensions;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.datafixers.util.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockDisplayReader;
@ -75,4 +81,20 @@ public interface IForgeBakedModel
{
return getBakedModel().getParticleTexture();
}
/**
* Override to true, to tell forge to call the getLayerModels method below.
*/
default boolean isLayered()
{
return false;
}
/**
* If {@see isLayered()} returns true, this is called to get the list of layers to draw.
*/
default List<Pair<IBakedModel, RenderType>> getLayerModels(ItemStack itemStack, boolean fabulous)
{
return Collections.singletonList(Pair.of(getBakedModel(), RenderTypeLookup.func_239219_a_(itemStack, fabulous)));
}
}

View file

@ -38,17 +38,17 @@ public interface IForgeVertexBuilder
{
default IVertexBuilder getVertexBuilder() { return (IVertexBuilder)this; }
// Copy of func_227889_a_, but enables tinting
// Copy of addQuad, but enables tinting and per-vertex alpha
default void addVertexData(MatrixStack.Entry matrixStack, BakedQuad bakedQuad, float red, float green, float blue, int lightmapCoord, int overlayColor, boolean readExistingColor) {
getVertexBuilder().addQuad(matrixStack, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, readExistingColor);
addVertexData(matrixStack, bakedQuad, red, green, blue, 1.0f, lightmapCoord, overlayColor, readExistingColor);
}
// Copy of func_227889_a_ with alpha support
// Copy of addQuad with alpha support
default void addVertexData(MatrixStack.Entry matrixEntry, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int lightmapCoord, int overlayColor) {
addVertexData(matrixEntry, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, alpha, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, false);
}
// Copy of func_227889_a_ with alpha support
// Copy of addQuad with alpha support
default void addVertexData(MatrixStack.Entry matrixEntry, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int lightmapCoord, int overlayColor, boolean readExistingColor) {
addVertexData(matrixEntry, bakedQuad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, red, green, blue, alpha, new int[]{lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayColor, readExistingColor);
}

View file

@ -31,10 +31,9 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IDynamicBakedModel;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.data.ModelProperty;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockDisplayReader;
import net.minecraftforge.client.model.data.*;
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
import net.minecraftforge.client.model.geometry.IMultipartModelGeometry;
@ -45,20 +44,20 @@ import java.util.function.Function;
public class CompositeModel implements IDynamicBakedModel
{
public static final ModelProperty<SubmodelModelData> SUBMODEL_DATA = new ModelProperty<>();
private final ImmutableMap<String, IBakedModel> bakedParts;
private final boolean isAmbientOcclusion;
private final boolean isGui3d;
private final boolean isSideLit;
private final TextureAtlasSprite particle;
private final ItemOverrideList overrides;
private final IModelTransform transforms;
public CompositeModel(boolean isGui3d, boolean isAmbientOcclusion, TextureAtlasSprite particle, ImmutableMap<String, IBakedModel> bakedParts, IModelTransform combinedTransform, ItemOverrideList overrides)
public CompositeModel(boolean isGui3d, boolean isSideLit, boolean isAmbientOcclusion, TextureAtlasSprite particle, ImmutableMap<String, IBakedModel> bakedParts, IModelTransform combinedTransform, ItemOverrideList overrides)
{
this.bakedParts = bakedParts;
this.isAmbientOcclusion = isAmbientOcclusion;
this.isGui3d = isGui3d;
this.isSideLit = isSideLit;
this.particle = particle;
this.overrides = overrides;
this.transforms = combinedTransform;
@ -71,12 +70,23 @@ public class CompositeModel implements IDynamicBakedModel
List<BakedQuad> quads = new ArrayList<>();
for(Map.Entry<String, IBakedModel> entry : bakedParts.entrySet())
{
// TODO: Some way to provide submodel data?
quads.addAll(entry.getValue().getQuads(state, side, rand, getSubmodelData(extraData, entry.getKey())));
quads.addAll(entry.getValue().getQuads(state, side, rand, CompositeModelData.get(extraData, entry.getKey())));
}
return quads;
}
@Nonnull
@Override
public IModelData getModelData(@Nonnull IBlockDisplayReader world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull IModelData tileData)
{
CompositeModelData composite = new CompositeModelData();
for(Map.Entry<String, IBakedModel> entry : bakedParts.entrySet())
{
composite.putSubmodelData(entry.getKey(), entry.getValue().getModelData(world, pos, state, ModelDataWrapper.wrap(tileData)));
}
return composite;
}
@Override
public boolean isAmbientOcclusion()
{
@ -92,8 +102,7 @@ public class CompositeModel implements IDynamicBakedModel
@Override
public boolean func_230044_c_()
{
// TODO: Forge: Auto-generated method stub
return false;
return isSideLit;
}
@Override
@ -132,29 +141,6 @@ public class CompositeModel implements IDynamicBakedModel
return bakedParts.get(name);
}
private IModelData getSubmodelData(IModelData extraData, String name)
{
SubmodelModelData data = extraData.getData(SUBMODEL_DATA);
if (data == null)
return EmptyModelData.INSTANCE;
return data.getSubmodelData(name);
}
public static class SubmodelModelData
{
private final Map<String, IModelData> parts = new HashMap<>();
public IModelData getSubmodelData(String name)
{
return parts.getOrDefault(name, EmptyModelData.INSTANCE);
}
public void putSubmodelData(String name, IModelData data)
{
parts.put(name, data);
}
}
private static class Submodel implements IModelGeometryPart
{
private final String name;
@ -228,7 +214,7 @@ public class CompositeModel implements IDynamicBakedModel
continue;
bakedParts.put(part.getKey(), submodel.bakeModel(bakery, spriteGetter, modelTransform, modelLocation));
}
return new CompositeModel(owner.isShadedInGui(), owner.useSmoothLighting(), particle, bakedParts.build(), owner.getCombinedTransform(), overrides);
return new CompositeModel(owner.isShadedInGui(), owner.useSmoothLighting(), owner.isSideLit(), particle, bakedParts.build(), owner.getCombinedTransform(), overrides);
}
@Override
@ -273,4 +259,116 @@ public class CompositeModel implements IDynamicBakedModel
return new Geometry(parts.build());
}
}
/**
* A model data container which stores data for child components.
*/
public static class CompositeModelData extends ModelDataMap
{
public static final ModelProperty<CompositeModelData> SUBMODEL_DATA = new ModelProperty<>();
/**
* Helper to get the CompositeModelData from an unknown IModelData instance.
* @param modelData The undetermined instance to get data from
* @return An optional representing the composite data, if present.
*/
public static Optional<CompositeModelData> get(IModelData modelData)
{
return Optional.ofNullable(modelData.getData(SUBMODEL_DATA));
}
/**
* Helper to get child data from an unknown IModelData instance.
* @param modelData The undetermined instance to get data from
* @param name The name of the child part to get data for.
* @return The data for the child, or empty if not available.
*/
public static IModelData get(IModelData modelData, String name)
{
return get(modelData).map(data -> data.getSubmodelData(name))
.orElse(EmptyModelData.INSTANCE);
}
// Implementation
private final Map<String, IModelData> parts = new HashMap<>();
public IModelData getSubmodelData(String name)
{
if (parts.containsKey(name))
return parts.get(name);
return EmptyModelData.INSTANCE;
}
public void putSubmodelData(String name, IModelData data)
{
parts.put(name, data);
}
@Override
public boolean hasProperty(ModelProperty<?> prop)
{
return prop == SUBMODEL_DATA ||super.hasProperty(prop);
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public <T> T getData(ModelProperty<T> prop)
{
if (prop == SUBMODEL_DATA)
return (T)this;
return super.getData(prop);
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public <T> T setData(ModelProperty<T> prop, T data)
{
if (prop == SUBMODEL_DATA)
return (T)this;
return super.setData(prop, data);
}
}
/**
* Wrapper for an IModelData instance which allows forwarding queries to the parent,
* but stores any new/modified values itself, avoiding modifications to the parent.
*/
private static class ModelDataWrapper extends ModelDataMap
{
private final IModelData parent;
public static IModelData wrap(IModelData parent)
{
return new ModelDataWrapper(parent);
}
private ModelDataWrapper(IModelData parent)
{
this.parent = parent;
}
@Override
public boolean hasProperty(ModelProperty<?> prop)
{
return super.hasProperty(prop) || parent.hasProperty(prop);
}
@Nullable
@Override
public <T> T getData(ModelProperty<T> prop)
{
return super.hasProperty(prop) ? super.getData(prop) : parent.getData(prop);
}
@Nullable
@Override
public <T> T setData(ModelProperty<T> prop, T data)
{
// We do not want to delegate setting to the parent
return super.setData(prop, data);
}
}
}

View file

@ -19,10 +19,7 @@
package net.minecraftforge.client.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair;
@ -31,7 +28,6 @@ import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraft.client.renderer.model.*;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.MissingTextureSprite;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.entity.LivingEntity;
import net.minecraft.fluid.Fluid;
@ -40,29 +36,24 @@ import net.minecraft.item.ItemStack;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.VanillaResourceType;
import net.minecraftforge.versions.forge.ForgeVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
public final class DynamicBucketModel implements IModelGeometry<DynamicBucketModel>
{
private static final Logger LOGGER = LogManager.getLogger();
public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation(ForgeVersion.MOD_ID, "dynbucket"), "inventory");
// minimal Z offset to prevent depth-fighting
private static final float NORTH_Z_COVER = 7.496f / 16f;
@ -76,13 +67,21 @@ public final class DynamicBucketModel implements IModelGeometry<DynamicBucketMod
private final boolean flipGas;
private final boolean tint;
private final boolean coverIsMask;
private final boolean applyFluidLuminosity;
@Deprecated
public DynamicBucketModel(Fluid fluid, boolean flipGas, boolean tint, boolean coverIsMask)
{
this(fluid, flipGas, tint, coverIsMask, true);
}
public DynamicBucketModel(Fluid fluid, boolean flipGas, boolean tint, boolean coverIsMask, boolean applyFluidLuminosity)
{
this.fluid = fluid;
this.flipGas = flipGas;
this.tint = tint;
this.coverIsMask = coverIsMask;
this.applyFluidLuminosity = applyFluidLuminosity;
}
/**
@ -91,63 +90,46 @@ public final class DynamicBucketModel implements IModelGeometry<DynamicBucketMod
*/
public DynamicBucketModel withFluid(Fluid newFluid)
{
return new DynamicBucketModel(newFluid, flipGas, tint, coverIsMask);
return new DynamicBucketModel(newFluid, flipGas, tint, coverIsMask, applyFluidLuminosity);
}
@Override
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation)
{
RenderMaterial particleLocation = owner.resolveTexture("particle");
if (MissingTextureSprite.getLocation().equals(particleLocation.getTextureLocation()))
{
particleLocation = null;
}
RenderMaterial baseLocation = owner.resolveTexture("base");
if (MissingTextureSprite.getLocation().equals(baseLocation.getTextureLocation()))
{
baseLocation = null;
}
RenderMaterial fluidMaskLocation = owner.resolveTexture("fluid");
if (MissingTextureSprite.getLocation().equals(fluidMaskLocation.getTextureLocation()))
{
fluidMaskLocation = null;
}
RenderMaterial coverLocation = owner.resolveTexture("cover");
if (!MissingTextureSprite.getLocation().equals(coverLocation.getTextureLocation()))
{
// cover (the actual item around the other two)
coverLocation = null;
}
RenderMaterial particleLocation = owner.isTexturePresent("particle") ? owner.resolveTexture("particle") : null;
RenderMaterial baseLocation = owner.isTexturePresent("base") ? owner.resolveTexture("base") : null;
RenderMaterial fluidMaskLocation = owner.isTexturePresent("fluid") ? owner.resolveTexture("fluid") : null;
RenderMaterial coverLocation = owner.isTexturePresent("fluid") ? owner.resolveTexture("cover") : null;
IModelTransform transformsFromModel = owner.getCombinedTransform();
ImmutableMap<TransformType, TransformationMatrix> transformMap = transformsFromModel != null ?
PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(transformsFromModel, modelTransform)) :
PerspectiveMapWrapper.getTransforms(modelTransform);
TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? spriteGetter.apply(ForgeHooksClient.getBlockMaterial(fluid.getAttributes().getStillTexture())) : null;
TextureAtlasSprite coverSprite = (coverLocation != null && (!coverIsMask || baseLocation != null)) ? spriteGetter.apply(coverLocation) : null;
ImmutableMap<TransformType, TransformationMatrix> transformMap =
PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(transformsFromModel, modelTransform));
TextureAtlasSprite particleSprite = particleLocation != null ? spriteGetter.apply(particleLocation) : null;
if (particleSprite == null) particleSprite = fluidSprite;
if (particleSprite == null && !coverIsMask) particleSprite = coverSprite;
// if the fluid is lighter than air, will manipulate the initial state to be rotated 180deg to turn it upside down
if (flipGas && fluid != Fluids.EMPTY && fluid.getAttributes().isLighterThanAir())
{
modelTransform = new ModelTransformComposition(modelTransform, new SimpleModelTransform(new TransformationMatrix(null, new Quaternion(0, 0, 1, 0), null, null)));
modelTransform = new SimpleModelTransform(
modelTransform.getRotation().blockCornerToCenter().composeVanilla(
new TransformationMatrix(null, new Quaternion(0, 0, 1, 0), null, null)).blockCenterToCorner());
}
TransformationMatrix transform = modelTransform.getRotation();
TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? spriteGetter.apply(ForgeHooksClient.getBlockMaterial(fluid.getAttributes().getStillTexture())) : null;
if (particleSprite == null) particleSprite = fluidSprite;
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
ItemMultiLayerBakedModel.Builder builder = ItemMultiLayerBakedModel.builder(owner, particleSprite, new ContainedFluidOverrideHandler(overrides, bakery, owner, this), transformMap);
if (baseLocation != null)
{
// build base (insidest)
builder.addAll(ItemLayerModel.getQuadsForSprites(ImmutableList.of(baseLocation), transform, spriteGetter));
builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemLayerModel.getQuadsForSprites(ImmutableList.of(baseLocation), transform, spriteGetter));
}
if (fluidMaskLocation != null && fluidSprite != null)
@ -156,36 +138,34 @@ public final class DynamicBucketModel implements IModelGeometry<DynamicBucketMod
if (templateSprite != null)
{
// build liquid layer (inside)
builder.addAll(ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, NORTH_Z_FLUID, Direction.NORTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
builder.addAll(ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, SOUTH_Z_FLUID, Direction.SOUTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
int luminosity = applyFluidLuminosity ? fluid.getAttributes().getLuminosity() : 0;
int color = tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF;
builder.addQuads(ItemLayerModel.getLayerRenderType(luminosity > 0), ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, NORTH_Z_FLUID, Direction.NORTH, color, 1, luminosity));
builder.addQuads(ItemLayerModel.getLayerRenderType(luminosity > 0), ItemTextureQuadConverter.convertTexture(transform, templateSprite, fluidSprite, SOUTH_Z_FLUID, Direction.SOUTH, color, 1, luminosity));
}
}
if (coverLocation != null && (!coverIsMask || baseLocation != null))
{
// cover (the actual item around the other two)
TextureAtlasSprite coverSprite = spriteGetter.apply(coverLocation);
if (coverSprite != null)
{
if (coverIsMask)
{
if (coverSprite != null && baseLocation != null)
{
TextureAtlasSprite baseSprite = spriteGetter.apply(baseLocation);
builder.addAll(ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, NORTH_Z_COVER, Direction.NORTH, 0xFFFFFFFF, 1));
builder.addAll(ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, SOUTH_Z_COVER, Direction.SOUTH, 0xFFFFFFFF, 1));
builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, NORTH_Z_COVER, Direction.NORTH, 0xFFFFFFFF, 2));
builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.convertTexture(transform, coverSprite, baseSprite, SOUTH_Z_COVER, Direction.SOUTH, 0xFFFFFFFF, 2));
}
}
else
{
builder.add(ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, NORTH_Z_COVER, coverSprite, Direction.NORTH, 0xFFFFFFFF, 2));
builder.add(ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, SOUTH_Z_COVER, coverSprite, Direction.SOUTH, 0xFFFFFFFF, 2));
if (particleSprite == null)
if (coverSprite != null)
{
particleSprite = coverSprite;
}
}
builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, NORTH_Z_COVER, coverSprite, Direction.NORTH, 0xFFFFFFFF, 2));
builder.addQuads(ItemLayerModel.getLayerRenderType(false), ItemTextureQuadConverter.genQuad(transform, 0, 0, 16, 16, SOUTH_Z_COVER, coverSprite, Direction.SOUTH, 0xFFFFFFFF, 2));
}
}
return new BakedModel(bakery, owner, this, builder.build(), particleSprite, Maps.immutableEnumMap(transformMap), Maps.newHashMap(), transform.isIdentity(), modelTransform, owner.isSideLit());
builder.setParticle(particleSprite);
return builder.build();
}
@Override
@ -193,10 +173,10 @@ public final class DynamicBucketModel implements IModelGeometry<DynamicBucketMod
{
Set<RenderMaterial> texs = Sets.newHashSet();
texs.add(owner.resolveTexture("particle"));
texs.add(owner.resolveTexture("base"));
texs.add(owner.resolveTexture("fluid"));
texs.add(owner.resolveTexture("cover"));
if (owner.isTexturePresent("particle")) texs.add(owner.resolveTexture("particle"));
if (owner.isTexturePresent("base")) texs.add(owner.resolveTexture("base"));
if (owner.isTexturePresent("fluid")) texs.add(owner.resolveTexture("fluid"));
if (owner.isTexturePresent("cover")) texs.add(owner.resolveTexture("cover"));
return texs;
}
@ -251,70 +231,55 @@ public final class DynamicBucketModel implements IModelGeometry<DynamicBucketMod
coverIsMask = modelContents.get("coverIsMask").getAsBoolean();
}
boolean applyFluidLuminosity = true;
if (modelContents.has("applyFluidLuminosity"))
{
applyFluidLuminosity = modelContents.get("applyFluidLuminosity").getAsBoolean();
}
// create new model with correct liquid
return new DynamicBucketModel(fluid, flip, tint, coverIsMask);
return new DynamicBucketModel(fluid, flip, tint, coverIsMask, applyFluidLuminosity);
}
}
private static final class ContainedFluidOverrideHandler extends ItemOverrideList
{
private final Map<String, IBakedModel> cache = Maps.newHashMap(); // contains all the baked models since they'll never change
private final ItemOverrideList nested;
private final ModelBakery bakery;
private final IModelConfiguration owner;
private final DynamicBucketModel parent;
private ContainedFluidOverrideHandler(ModelBakery bakery)
private ContainedFluidOverrideHandler(ItemOverrideList nested, ModelBakery bakery, IModelConfiguration owner, DynamicBucketModel parent)
{
this.nested = nested;
this.bakery = bakery;
this.owner = owner;
this.parent = parent;
}
@Override
public IBakedModel func_239290_a_(IBakedModel originalModel, ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity)
{
IBakedModel overriden = nested.func_239290_a_(originalModel, stack, world, entity);
if (overriden != originalModel) return overriden;
return FluidUtil.getFluidContained(stack)
.map(fluidStack -> {
BakedModel model = (BakedModel)originalModel;
Fluid fluid = fluidStack.getFluid();
String name = fluid.getRegistryName().toString();
if (!model.cache.containsKey(name))
if (!cache.containsKey(name))
{
DynamicBucketModel parent = model.parent.withFluid(fluid);
IBakedModel bakedModel = parent.bake(model.owner, bakery, ModelLoader.defaultTextureGetter(), model.originalTransform, model.getOverrides(), new ResourceLocation("forge:bucket_override"));
model.cache.put(name, bakedModel);
DynamicBucketModel unbaked = this.parent.withFluid(fluid);
IBakedModel bakedModel = unbaked.bake(owner, bakery, ModelLoader.defaultTextureGetter(), ModelRotation.X0_Y0, this, new ResourceLocation("forge:bucket_override"));
cache.put(name, bakedModel);
return bakedModel;
}
return model.cache.get(name);
return cache.get(name);
})
// not a fluid item apparently
.orElse(originalModel); // empty bucket
}
}
// the dynamic bucket is based on the empty bucket
private static final class BakedModel extends BakedItemModel
{
private final IModelConfiguration owner;
private final DynamicBucketModel parent;
private final Map<String, IBakedModel> cache; // contains all the baked models since they'll never change
private final IModelTransform originalTransform;
private final boolean isSideLit;
BakedModel(ModelBakery bakery,
IModelConfiguration owner, DynamicBucketModel parent,
ImmutableList<BakedQuad> quads,
TextureAtlasSprite particle,
ImmutableMap<TransformType, TransformationMatrix> transforms,
Map<String, IBakedModel> cache,
boolean untransformed,
IModelTransform originalTransform, boolean isSideLit)
{
super(quads, particle, transforms, new ContainedFluidOverrideHandler(bakery), untransformed, isSideLit);
this.owner = owner;
this.parent = parent;
this.cache = cache;
this.originalTransform = originalTransform;
this.isSideLit = isSideLit;
}
}
}

View file

@ -60,6 +60,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
// TODO: Write a model loader and test/fix as needed
public final class FluidModel implements IModelGeometry<FluidModel>
{
public static final FluidModel WATER = new FluidModel(Fluids.WATER);

View file

@ -76,7 +76,6 @@ public interface IModelConfiguration {
* Gets the vanilla camera transforms data.
* Do not use for non-vanilla code. For general usage, prefer getCombinedState.
*/
@Deprecated
ItemCameraTransforms getCameraTransforms();
/**

View file

@ -19,18 +19,13 @@
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.common.collect.*;
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.util.math.vector.TransformationMatrix;
import net.minecraft.client.renderer.RenderType;
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;
@ -38,6 +33,8 @@ 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.vector.TransformationMatrix;
import net.minecraftforge.client.ForgeRenderTypes;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
@ -55,7 +52,6 @@ import java.util.function.Function;
* - Various fixes in the baking logic.
* - Not limited to 4 layers maximum.
*/
// TODO: Implement as new model loader
public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
{
public static final ItemLayerModel INSTANCE = new ItemLayerModel(ImmutableList.of());
@ -93,16 +89,31 @@ public final class ItemLayerModel implements IModelGeometry<ItemLayerModel>
}
@Override
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation)
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery,
Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform modelTransform,
ItemOverrideList overrides, ResourceLocation modelLocation)
{
//TODO: Verify
ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> transformMap =
PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform));
TransformationMatrix transform = modelTransform.getRotation();
ImmutableList<BakedQuad> quads = getQuadsForSprites(textures, transform, spriteGetter, fullbrightLayers);
TextureAtlasSprite particle = spriteGetter.apply(
owner.isTexturePresent("particle") ? owner.resolveTexture("particle") : textures.get(0)
);
ImmutableMap<TransformType, TransformationMatrix> map = PerspectiveMapWrapper.getTransforms(modelTransform);
return new BakedItemModel(quads, particle, map, overrides, transform.isIdentity(), owner.isSideLit());
ItemMultiLayerBakedModel.Builder builder = ItemMultiLayerBakedModel.builder(owner, particle, overrides, transformMap);
for(int i = 0; i < textures.size(); i++)
{
TextureAtlasSprite tas = spriteGetter.apply(textures.get(i));
RenderType rt = getLayerRenderType(fullbrightLayers.contains(i));
builder.addQuads(rt, getQuadsForSprite(i, tas, transform, true));
}
return builder.build();
}
public static RenderType getLayerRenderType(boolean isFullbright)
{
return isFullbright ? ForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() : ForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get();
}
public static ImmutableList<BakedQuad> getQuadsForSprites(List<RenderMaterial> textures, TransformationMatrix transform, Function<RenderMaterial, TextureAtlasSprite> spriteGetter)

View file

@ -0,0 +1,211 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2020.
*
* 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.client.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.datafixers.util.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraftforge.client.model.data.EmptyModelData;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ItemMultiLayerBakedModel implements IBakedModel
{
private final boolean smoothLighting;
private final boolean shadedInGui;
private final boolean sideLit;
private final TextureAtlasSprite particle;
private final ItemOverrideList overrides;
private final ImmutableList<Pair<IBakedModel, RenderType>> layerModels;
private final ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> cameraTransforms;
public ItemMultiLayerBakedModel(boolean smoothLighting, boolean shadedInGui, boolean sideLit,
TextureAtlasSprite particle, ItemOverrideList overrides,
ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> cameraTransforms,
ImmutableList<Pair<IBakedModel, RenderType>> layerModels)
{
this.smoothLighting = smoothLighting;
this.shadedInGui = shadedInGui;
this.sideLit = sideLit;
this.particle = particle;
this.overrides = overrides;
this.layerModels = layerModels;
this.cameraTransforms = cameraTransforms;
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand)
{
List<BakedQuad> quads = Lists.newArrayList();
layerModels.forEach(lm -> quads.addAll(lm.getFirst().getQuads(state, side, rand, EmptyModelData.INSTANCE)));
return quads;
}
@Override
public boolean isAmbientOcclusion()
{
return smoothLighting;
}
@Override
public boolean isGui3d()
{
return shadedInGui;
}
@Override
public boolean func_230044_c_()
{
return sideLit;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return particle;
}
@Override
public ItemOverrideList getOverrides()
{
return overrides;
}
@Override
public boolean doesHandlePerspectives()
{
return true;
}
@Override
public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack mat)
{
return PerspectiveMapWrapper.handlePerspective(this, cameraTransforms, cameraTransformType, mat);
}
//@Override
public boolean isLayered()
{
return true;
}
//@Override
public List<Pair<IBakedModel, RenderType>> getLayerModels(ItemStack itemStack, boolean fabulous)
{
return layerModels;
}
public static Builder builder(IModelConfiguration owner, TextureAtlasSprite particle, ItemOverrideList overrides,
ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> cameraTransforms)
{
return new Builder(owner, particle, overrides, cameraTransforms);
}
public static class Builder
{
private final ImmutableList.Builder<Pair<IBakedModel, RenderType>> builder = ImmutableList.builder();
private final List<BakedQuad> quads = Lists.newArrayList();
private final ItemOverrideList overrides;
private final ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> cameraTransforms;
private final IModelConfiguration owner;
private TextureAtlasSprite particle;
private RenderType lastRt = null;
private Builder(IModelConfiguration owner, TextureAtlasSprite particle, ItemOverrideList overrides,
ImmutableMap<ItemCameraTransforms.TransformType, TransformationMatrix> cameraTransforms)
{
this.owner = owner;
this.particle = particle;
this.overrides = overrides;
this.cameraTransforms = cameraTransforms;
}
private void addLayer(ImmutableList.Builder<Pair<IBakedModel, RenderType>> builder, List<BakedQuad> quads, RenderType rt)
{
IBakedModel model = new BakedItemModel(ImmutableList.copyOf(quads), particle, ImmutableMap.of(), ItemOverrideList.EMPTY, true, owner.isSideLit());
builder.add(Pair.of(model, rt));
}
private void flushQuads(RenderType rt)
{
if (rt != lastRt)
{
if (quads.size() > 0)
{
addLayer(builder, quads, lastRt);
quads.clear();
}
lastRt = rt;
}
}
public Builder setParticle(TextureAtlasSprite particleSprite)
{
this.particle = particleSprite;
return this;
}
public Builder addQuads(RenderType rt, BakedQuad... quadsToAdd)
{
flushQuads(rt);
Collections.addAll(quads, quadsToAdd);
return this;
}
public Builder addQuads(RenderType rt, Collection<BakedQuad> quadsToAdd)
{
flushQuads(rt);
quads.addAll(quadsToAdd);
return this;
}
public IBakedModel build()
{
if (quads.size() > 0)
{
addLayer(builder, quads, lastRt);
}
return new ItemMultiLayerBakedModel(owner.useSmoothLighting(), owner.isShadedInGui(), owner.isSideLit(),
particle, overrides, cameraTransforms, builder.build());
}
}
}

View file

@ -21,11 +21,12 @@ package net.minecraftforge.client.model;
import com.google.common.collect.Lists;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.TRSRTransformer;
@ -47,15 +48,18 @@ public final class ItemTextureQuadConverter
* The resulting list of quads is the texture represented as a list of horizontal OR vertical quads,
* depending on which creates less quads. If the amount of quads is equal, horizontal is preferred.
*
* @param format
* @param template The input texture to convert
* @param sprite The texture whose UVs shall be used
* @return The generated quads.
*/
public static List<BakedQuad> convertTexture(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint)
{
List<BakedQuad> horizontal = convertTextureHorizontal(transform, template, sprite, z, facing, color, tint);
List<BakedQuad> vertical = convertTextureVertical(transform, template, sprite, z, facing, color, tint);
return convertTexture(transform, template, sprite, z, facing, color, tint, 0);
}
public static List<BakedQuad> convertTexture(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity)
{
List<BakedQuad> horizontal = convertTextureHorizontal(transform, template, sprite, z, facing, color, tint, luminosity);
List<BakedQuad> vertical = convertTextureVertical(transform, template, sprite, z, facing, color, tint, luminosity);
return horizontal.size() <= vertical.size() ? horizontal : vertical;
}
@ -65,6 +69,10 @@ public final class ItemTextureQuadConverter
* The height of the strips is as big as possible.
*/
public static List<BakedQuad> convertTextureHorizontal(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint)
{
return convertTextureHorizontal(transform, template, sprite, z, facing, color, tint, 0);
}
public static List<BakedQuad> convertTextureHorizontal(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity)
{
int w = template.getWidth();
int h = template.getHeight();
@ -115,7 +123,7 @@ public final class ItemTextureQuadConverter
(float)y * hScale,
(float)x * wScale,
(float)endY * hScale,
z, sprite, facing, color, tint));
z, sprite, facing, color, tint, luminosity));
// update Y if all the rows match. no need to rescan
if (endY - y > 1)
@ -136,6 +144,10 @@ public final class ItemTextureQuadConverter
* The width of the strips is as big as possible.
*/
public static List<BakedQuad> convertTextureVertical(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint)
{
return convertTextureVertical(transform, template, sprite, z, facing, color, tint, 0);
}
public static List<BakedQuad> convertTextureVertical(TransformationMatrix transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, Direction facing, int color, int tint, int luminosity)
{
int w = template.getWidth();
int h = template.getHeight();
@ -186,7 +198,7 @@ public final class ItemTextureQuadConverter
(float)start * hScale,
(float)endX * wScale,
(float)y * hScale,
z, sprite, facing, color, tint));
z, sprite, facing, color, tint, luminosity));
// update X if all the columns match. no need to rescan
if (endX - x > 1)
@ -212,6 +224,10 @@ public final class ItemTextureQuadConverter
* Coordinates are [0,16] to match the usual coordinates used in TextureAtlasSprites
*/
public static BakedQuad genQuad(TransformationMatrix transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, Direction facing, int color, int tint)
{
return genQuad(transform, x1, y1, x2, y2, z, sprite, facing, color, tint, 0);
}
public static BakedQuad genQuad(TransformationMatrix transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, Direction facing, int color, int tint, int luminosity)
{
float u1 = sprite.getInterpolatedU(x1);
float v1 = sprite.getInterpolatedV(y1);
@ -227,17 +243,18 @@ public final class ItemTextureQuadConverter
y1 = 1f - y2;
y2 = 1f - tmp;
return putQuad(transform, facing, sprite, color, tint, x1, y1, x2, y2, z, u1, v1, u2, v2);
return putQuad(transform, facing, sprite, color, tint, x1, y1, x2, y2, z, u1, v1, u2, v2, luminosity);
}
private static BakedQuad putQuad(TransformationMatrix transform, Direction side, TextureAtlasSprite sprite, int color, int tint,
float x1, float y1, float x2, float y2, float z,
float u1, float v1, float u2, float v2)
float u1, float v1, float u2, float v2, int luminosity)
{
BakedQuadBuilder builder = new BakedQuadBuilder(sprite);
builder.setQuadTint(tint);
builder.setQuadOrientation(side);
builder.setApplyDiffuseLighting(luminosity == 0);
// only apply the transform if it's not identity
boolean hasTransform = !transform.isIdentity();
@ -245,28 +262,29 @@ public final class ItemTextureQuadConverter
if (side == Direction.SOUTH)
{
putVertex(consumer, side, x1, y1, z, u1, v2, color);
putVertex(consumer, side, x2, y1, z, u2, v2, color);
putVertex(consumer, side, x2, y2, z, u2, v1, color);
putVertex(consumer, side, x1, y2, z, u1, v1, color);
putVertex(consumer, side, x1, y1, z, u1, v2, color, luminosity);
putVertex(consumer, side, x2, y1, z, u2, v2, color, luminosity);
putVertex(consumer, side, x2, y2, z, u2, v1, color, luminosity);
putVertex(consumer, side, x1, y2, z, u1, v1, color, luminosity);
}
else
{
putVertex(consumer, side, x1, y1, z, u1, v2, color);
putVertex(consumer, side, x1, y2, z, u1, v1, color);
putVertex(consumer, side, x2, y2, z, u2, v1, color);
putVertex(consumer, side, x2, y1, z, u2, v2, color);
putVertex(consumer, side, x1, y1, z, u1, v2, color, luminosity);
putVertex(consumer, side, x1, y2, z, u1, v1, color, luminosity);
putVertex(consumer, side, x2, y2, z, u2, v1, color, luminosity);
putVertex(consumer, side, x2, y1, z, u2, v2, color, luminosity);
}
return builder.build();
}
private static void putVertex(IVertexConsumer consumer, Direction side,
float x, float y, float z, float u, float v, int color)
float x, float y, float z, float u, float v, int color, int luminosity)
{
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);
switch (element.getUsage())
{
case POSITION:
consumer.put(e, x, y, z, 1f);
@ -285,11 +303,16 @@ public final class ItemTextureQuadConverter
consumer.put(e, offX, offY, offZ, 0f);
break;
case UV:
if (format.getElements().get(e).getIndex() == 0)
if (element.getIndex() == 0)
{
consumer.put(e, u, v, 0f, 1f);
break;
}
else if (element.getIndex() == 2)
{
consumer.put(e, (luminosity<<4)/32768.0f, (luminosity<<4)/32768.0f, 0f, 1f);
break;
}
// else fallthrough to default
default:
consumer.put(e);

View file

@ -24,7 +24,7 @@ import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.matrix.MatrixStack;
@ -34,11 +34,14 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraft.client.renderer.model.*;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.item.ItemStack;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.Direction;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.ForgeRenderTypes;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.EmptyModelData;
@ -48,9 +51,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.stream.Collectors;
/**
* A model that can be rendered in multiple {@link RenderType}.
@ -92,11 +93,12 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
IUnbakedModel missing = ModelLoader.instance().getMissingModel();
return new MultiLayerBakedModel(
owner.useSmoothLighting(), owner.isShadedInGui(),
owner.isSideLit(), spriteGetter.apply(owner.resolveTexture("particle")), overrides,
buildModels(models, modelTransform, bakery, spriteGetter, modelLocation),
owner.useSmoothLighting(), owner.isShadedInGui(), owner.isSideLit(),
spriteGetter.apply(owner.resolveTexture("particle")), overrides, true,
missing.bakeModel(bakery, spriteGetter, modelTransform, modelLocation),
PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform)));
buildModels(models, modelTransform, bakery, spriteGetter, modelLocation),
PerspectiveMapWrapper.getTransforms(new ModelTransformComposition(owner.getCombinedTransform(), modelTransform))
);
}
private static final class MultiLayerBakedModel implements IBakedModel
@ -109,10 +111,13 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
protected final TextureAtlasSprite particle;
protected final ItemOverrideList overrides;
private final IBakedModel missing;
private final boolean convertRenderTypes;
private final List<Pair<IBakedModel, RenderType>> itemLayers;
public MultiLayerBakedModel(
boolean ambientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemOverrideList overrides,
ImmutableMap<RenderType, IBakedModel> models, IBakedModel missing, ImmutableMap<TransformType, TransformationMatrix> cameraTransforms)
boolean convertRenderTypes, IBakedModel missing, ImmutableMap<RenderType, IBakedModel> models,
ImmutableMap<TransformType, TransformationMatrix> cameraTransforms)
{
this.isSideLit = isSideLit;
this.models = models;
@ -122,6 +127,12 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
this.gui3d = isGui3d;
this.particle = particle;
this.overrides = overrides;
this.convertRenderTypes = convertRenderTypes;
this.itemLayers = models.entrySet().stream().map(kv -> {
RenderType rt = kv.getKey();
if (convertRenderTypes) rt = ITEM_RENDER_TYPE_MAPPING.getOrDefault(rt, rt);
return Pair.of(kv.getValue(), rt);
}).collect(Collectors.toList());
}
@Override
@ -144,6 +155,9 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
}
return builder.build();
}
// support for item layer rendering
if (state == null && convertRenderTypes)
layer = ITEM_RENDER_TYPE_MAPPING.inverse().getOrDefault(layer, layer);
// assumes that child model will handle this state properly. FIXME?
return models.getOrDefault(layer, missing).getQuads(state, side, rand, extraData);
}
@ -201,10 +215,38 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
{
return ItemOverrideList.EMPTY;
}
@Override
public boolean isLayered()
{
return true;
}
@Override
public List<Pair<IBakedModel, RenderType>> getLayerModels(ItemStack itemStack, boolean fabulous)
{
return itemLayers;
}
public static BiMap<RenderType, RenderType> ITEM_RENDER_TYPE_MAPPING = HashBiMap.create();
static {
ITEM_RENDER_TYPE_MAPPING.put(RenderType.getSolid(), RenderType.getEntitySolid(AtlasTexture.LOCATION_BLOCKS_TEXTURE));
ITEM_RENDER_TYPE_MAPPING.put(RenderType.getCutout(), RenderType.getEntityCutout(AtlasTexture.LOCATION_BLOCKS_TEXTURE));
ITEM_RENDER_TYPE_MAPPING.put(RenderType.getCutoutMipped(), ForgeRenderTypes.getEntityCutoutMipped(AtlasTexture.LOCATION_BLOCKS_TEXTURE));
ITEM_RENDER_TYPE_MAPPING.put(RenderType.getTranslucent(), RenderType.getEntityTranslucent(AtlasTexture.LOCATION_BLOCKS_TEXTURE));
}
}
public static final class Loader implements IModelLoader<MultiLayerModel>
{
public static final Map<String, RenderType> BLOCK_LAYERS = ImmutableMap.<String,RenderType>builder()
.put("solid", RenderType.getSolid())
.put("cutout", RenderType.getCutout())
.put("cutout_mipped", RenderType.getCutoutMipped())
.put("translucent", RenderType.getTranslucent())
.put("tripwire", RenderType.func_241715_r_())
.build();
public static final Loader INSTANCE = new Loader();
private Loader() {}
@ -220,12 +262,12 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
{
ImmutableMap.Builder<RenderType, IUnbakedModel> builder = ImmutableMap.builder();
JsonObject layersObject = JSONUtils.getJsonObject(modelContents, "layers");
for(RenderType layer : RenderType.getBlockRenderTypes()) // block layers
for(Map.Entry<String, RenderType> layer : BLOCK_LAYERS.entrySet()) // block layers
{
String layerName = layer.toString(); // mc overrides toString to return the ID for the layer
String layerName = layer.getKey(); // mc overrides toString to return the ID for the layer
if(layersObject.has(layerName))
{
builder.put(layer, deserializationContext.deserialize(JSONUtils.getJsonObject(layersObject, layerName), BlockModel.class));
builder.put(layer.getValue(), deserializationContext.deserialize(JSONUtils.getJsonObject(layersObject, layerName), BlockModel.class));
}
}
ImmutableMap<RenderType, IUnbakedModel> models = builder.build();

View file

@ -31,7 +31,7 @@ import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.util.Direction;
/**
* Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.state.IBlockState, net.minecraft.util.EnumFacing, java.util.Random)}.
* Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.BlockState, net.minecraft.util.Direction, java.util.Random)}.
*/
public interface IDynamicBakedModel extends IBakedModel
{

View file

@ -33,6 +33,11 @@ public class ModelDataMap implements IModelData
this.backingMap = new IdentityHashMap<>(map);
}
protected ModelDataMap()
{
this.backingMap = new IdentityHashMap<>();
}
@Override
public boolean hasProperty(ModelProperty<?> prop)
{

View file

@ -150,6 +150,7 @@ public net.minecraft.client.particle.ParticleManager$IParticleMetaFactory
public net.minecraft.client.renderer.GameRenderer func_175069_a(Lnet/minecraft/util/ResourceLocation;)V #loadShader
private net.minecraft.client.renderer.ItemModelMesher field_199313_a #force public -> private
public net.minecraft.client.renderer.ItemRenderer func_229112_a_(Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;Ljava/util/List;Lnet/minecraft/item/ItemStack;II)V # renderQuads
public net.minecraft.client.renderer.ItemRenderer func_229114_a_(Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/item/ItemStack;IILcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;)V # renderModel
public net.minecraft.client.renderer.entity.EntityRendererManager field_78729_o #renderers
public net.minecraft.client.renderer.entity.EntityRendererManager func_229087_a_(Lnet/minecraft/entity/EntityType;Lnet/minecraft/client/renderer/entity/EntityRenderer;)V # addRenderer
protected net.minecraft.client.renderer.entity.ItemRenderer func_177078_a(Lnet/minecraft/item/ItemStack;)I # getMiniItemCount

View file

@ -5,7 +5,7 @@
"solid": {
"parent": "block/cube_all",
"textures": { "all": "block/slime_block" },
"transform": { "scale": 0.5, "translation": [-0.25,-0.25,-0.25] }
"transform": { "origin":"center", "scale": 0.625 }
},
"translucent": {
"parent": "block/cube_all",