mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 16:27:47 +00:00
Merge pull request #2282 from bonii-xx/dynbucket
Add a dynamic bucket model that displays the animated liquid contained
This commit is contained in:
commit
c7790f7b35
22 files changed
+958
No files matched your search
@@ -104,6 +104,7 @@ public class ForgeGuiFactory implements IModGuiFactory
|
||||
{
|
||||
List<IConfigElement> list = new ArrayList<IConfigElement>();
|
||||
list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class));
|
||||
list.add(new DummyCategoryElement("forgeClientCfg", "forge.configgui.ctgy.forgeClientConfig", ClientEntry.class));
|
||||
list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.class));
|
||||
list.add(new DummyCategoryElement("forgeVersionCheckCfg", "forge.configgui.ctgy.VersionCheckConfig", VersionCheckEntry.class));
|
||||
return list;
|
||||
@@ -133,6 +134,30 @@ public class ForgeGuiFactory implements IModGuiFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This custom list entry provides the Client only Settings entry on the Minecraft Forge Configuration screen.
|
||||
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
|
||||
*/
|
||||
public static class ClientEntry extends CategoryEntry
|
||||
{
|
||||
public ClientEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
|
||||
{
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiScreen buildChildScreen()
|
||||
{
|
||||
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
|
||||
// GuiConfig object's entryList will also be refreshed to reflect the changes.
|
||||
return new GuiConfig(this.owningScreen,
|
||||
(new ConfigElement(ForgeModContainer.getConfig().getCategory(Configuration.CATEGORY_CLIENT))).getChildElements(),
|
||||
this.owningScreen.modID, Configuration.CATEGORY_CLIENT, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
|
||||
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart,
|
||||
GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This custom list entry provides the Forge Chunk Manager Config entry on the Minecraft Forge Configuration screen.
|
||||
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
|
||||
|
||||
import javax.vecmath.Vector4f;
|
||||
import java.util.List;
|
||||
|
||||
public final class ItemTextureQuadConverter
|
||||
{
|
||||
private ItemTextureQuadConverter()
|
||||
{
|
||||
// non-instantiable
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a texture and converts it into BakedQuads.
|
||||
* The conversion is done by scanning the texture horizontally and vertically and creating "strips" of the texture.
|
||||
* Strips that are of the same size and follow each other are converted into one bigger quad.
|
||||
* </br>
|
||||
* 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<UnpackedBakedQuad> convertTexture(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
|
||||
{
|
||||
List<UnpackedBakedQuad> horizontal = convertTextureHorizontal(format, transform, template, sprite, z, facing, color);
|
||||
List<UnpackedBakedQuad> vertical = convertTextureVertical(format, transform, template, sprite, z, facing, color);
|
||||
|
||||
return horizontal.size() >= vertical.size() ? horizontal : vertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a texture and converts it into a list of horizontal strips stacked on top of each other.
|
||||
* The height of the strips is as big as possible.
|
||||
*/
|
||||
public static List<UnpackedBakedQuad> convertTextureHorizontal(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
|
||||
{
|
||||
int w = template.getIconWidth();
|
||||
int h = template.getIconHeight();
|
||||
int[] data = template.getFrameTextureData(0)[0];
|
||||
List<UnpackedBakedQuad> quads = Lists.newArrayList();
|
||||
|
||||
// the upper left x-position of the current quad
|
||||
int start = -1;
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
// current pixel
|
||||
int pixel = data[y * w + x];
|
||||
|
||||
// no current quad but found a new one
|
||||
if (start < 0 && isVisible(pixel))
|
||||
{
|
||||
start = x;
|
||||
}
|
||||
// got a current quad, but it ends here
|
||||
if (start >= 0 && !isVisible(pixel))
|
||||
{
|
||||
// we now check if the visibility of the next row matches the one fo the current row
|
||||
// if they are, we can extend the quad downwards
|
||||
int endY = y + 1;
|
||||
boolean sameRow = true;
|
||||
while (sameRow)
|
||||
{
|
||||
for (int i = 0; i < w; i++)
|
||||
{
|
||||
int px1 = data[y * w + i];
|
||||
int px2 = data[endY * w + i];
|
||||
if (isVisible(px1) != isVisible(px2))
|
||||
{
|
||||
sameRow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sameRow)
|
||||
{
|
||||
endY++;
|
||||
}
|
||||
}
|
||||
|
||||
// create the quad
|
||||
quads.add(genQuad(format, transform, start, y, x, endY, z, sprite, facing, color));
|
||||
|
||||
// update Y if all the rows match. no need to rescan
|
||||
if (endY - y > 1)
|
||||
{
|
||||
y = endY - 1;
|
||||
}
|
||||
// clear current quad
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a texture and converts it into a list of vertical strips stacked next to each other from left to right.
|
||||
* The width of the strips is as big as possible.
|
||||
*/
|
||||
public static List<UnpackedBakedQuad> convertTextureVertical(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
|
||||
{
|
||||
int w = template.getIconWidth();
|
||||
int h = template.getIconHeight();
|
||||
int[] data = template.getFrameTextureData(0)[0];
|
||||
List<UnpackedBakedQuad> quads = Lists.newArrayList();
|
||||
|
||||
// the upper left y-position of the current quad
|
||||
int start = -1;
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
// current pixel
|
||||
int pixel = data[y * w + x];
|
||||
|
||||
// no current quad but found a new one
|
||||
if (start < 0 && isVisible(pixel))
|
||||
{
|
||||
start = y;
|
||||
}
|
||||
// got a current quad, but it ends here
|
||||
if (start >= 0 && !isVisible(pixel))
|
||||
{
|
||||
// we now check if the visibility of the next column matches the one fo the current row
|
||||
// if they are, we can extend the quad downwards
|
||||
int endX = x + 1;
|
||||
boolean sameColumn = true;
|
||||
while (sameColumn)
|
||||
{
|
||||
for (int i = 0; i < h; i++)
|
||||
{
|
||||
int px1 = data[i * w + x];
|
||||
int px2 = data[i * w + endX];
|
||||
if (isVisible(px1) != isVisible(px2))
|
||||
{
|
||||
sameColumn = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sameColumn)
|
||||
{
|
||||
endX++;
|
||||
}
|
||||
}
|
||||
|
||||
// create the quad
|
||||
quads.add(genQuad(format, transform, x, start, endX, y, z, sprite, facing, color));
|
||||
|
||||
// update X if all the columns match. no need to rescan
|
||||
if (endX - x > 1)
|
||||
{
|
||||
x = endX - 1;
|
||||
}
|
||||
// clear current quad
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
// true if alpha != 0
|
||||
private static boolean isVisible(int color)
|
||||
{
|
||||
return (color >> 24 & 255) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Front/Back quad for an itemmodel. Therefore only supports facing NORTH and SOUTH.
|
||||
*/
|
||||
public static UnpackedBakedQuad genQuad(VertexFormat format, TRSRTransformation transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, EnumFacing facing, int color)
|
||||
{
|
||||
float u1 = sprite.getInterpolatedU(x1);
|
||||
float v1 = sprite.getInterpolatedV(y1);
|
||||
float u2 = sprite.getInterpolatedU(x2);
|
||||
float v2 = sprite.getInterpolatedV(y2);
|
||||
|
||||
x1 /= 16f;
|
||||
y1 /= 16f;
|
||||
x2 /= 16f;
|
||||
y2 /= 16f;
|
||||
|
||||
float tmp = y1;
|
||||
y1 = 1f - y2;
|
||||
y2 = 1f - tmp;
|
||||
|
||||
return putQuad(format, transform, facing, color, x1, y1, x2, y2, z, u1, v1, u2, v2);
|
||||
}
|
||||
|
||||
private static UnpackedBakedQuad putQuad(VertexFormat format, TRSRTransformation transform, EnumFacing side, int color,
|
||||
float x1, float y1, float x2, float y2, float z,
|
||||
float u1, float v1, float u2, float v2)
|
||||
{
|
||||
side = side.getOpposite();
|
||||
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
|
||||
builder.setQuadTint(-1);
|
||||
builder.setQuadOrientation(side);
|
||||
builder.setQuadColored();
|
||||
|
||||
if (side == EnumFacing.NORTH)
|
||||
{
|
||||
putVertex(builder, format, transform, side, x1, y1, z, u1, v2, color);
|
||||
putVertex(builder, format, transform, side, x2, y1, z, u2, v2, color);
|
||||
putVertex(builder, format, transform, side, x2, y2, z, u2, v1, color);
|
||||
putVertex(builder, format, transform, side, x1, y2, z, u1, v1, color);
|
||||
} else
|
||||
{
|
||||
putVertex(builder, format, transform, side, x1, y1, z, u1, v2, color);
|
||||
putVertex(builder, format, transform, side, x1, y2, z, u1, v1, color);
|
||||
putVertex(builder, format, transform, side, x2, y2, z, u2, v1, color);
|
||||
putVertex(builder, format, transform, side, x2, y1, z, u2, v2, color);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static void putVertex(UnpackedBakedQuad.Builder builder, VertexFormat format, TRSRTransformation transform, EnumFacing side,
|
||||
float x, float y, float z, float u, float v, int color)
|
||||
{
|
||||
Vector4f vec = new Vector4f();
|
||||
for (int e = 0; e < format.getElementCount(); e++)
|
||||
{
|
||||
switch (format.getElement(e).getUsage())
|
||||
{
|
||||
case POSITION:
|
||||
if (transform == TRSRTransformation.identity())
|
||||
{
|
||||
builder.put(e, x, y, z, 1);
|
||||
}
|
||||
// only apply the transform if it's not identity
|
||||
else
|
||||
{
|
||||
vec.x = x;
|
||||
vec.y = y;
|
||||
vec.z = z;
|
||||
vec.w = 1;
|
||||
transform.getMatrix().transform(vec);
|
||||
builder.put(e, vec.x, vec.y, vec.z, vec.w);
|
||||
}
|
||||
break;
|
||||
case COLOR:
|
||||
float r = ((color >> 16) & 0xFF) / 255f; // red
|
||||
float g = ((color >> 8) & 0xFF) / 255f; // green
|
||||
float b = ((color >> 0) & 0xFF) / 255f; // blue
|
||||
float a = ((color >> 24) & 0xFF) / 255f; // alpha
|
||||
builder.put(e, r, g, b, a);
|
||||
break;
|
||||
case UV:
|
||||
if (format.getElement(e).getIndex() == 0)
|
||||
{
|
||||
builder.put(e, u, v, 0f, 1f);
|
||||
break;
|
||||
}
|
||||
case NORMAL:
|
||||
builder.put(e, (float) side.getFrontOffsetX(), (float) side.getFrontOffsetY(), (float) side.getFrontOffsetZ(), 0f);
|
||||
break;
|
||||
default:
|
||||
builder.put(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.model.IBakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fluids.*;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Quat4f;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class ModelDynBucket implements IModel, IModelCustomData, IRetexturableModel
|
||||
{
|
||||
public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation("forge", "dynbucket"), "inventory");
|
||||
|
||||
// minimal Z offset to prevent depth-fighting
|
||||
private static final float NORTH_Z_BASE = 7.496f / 16f;
|
||||
private static final float SOUTH_Z_BASE = 8.504f / 16f;
|
||||
private static final float NORTH_Z_FLUID = 7.498f / 16f;
|
||||
private static final float SOUTH_Z_FLUID = 8.502f / 16f;
|
||||
|
||||
public static final IModel MODEL = new ModelDynBucket();
|
||||
|
||||
protected final ResourceLocation baseLocation;
|
||||
protected final ResourceLocation liquidLocation;
|
||||
protected final ResourceLocation coverLocation;
|
||||
|
||||
protected final Fluid fluid;
|
||||
protected final boolean flipGas;
|
||||
|
||||
public ModelDynBucket()
|
||||
{
|
||||
this(null, null, null, FluidRegistry.WATER, false);
|
||||
}
|
||||
|
||||
public ModelDynBucket(ResourceLocation baseLocation, ResourceLocation liquidLocation, ResourceLocation coverLocation, Fluid fluid, boolean flipGas)
|
||||
{
|
||||
this.baseLocation = baseLocation;
|
||||
this.liquidLocation = liquidLocation;
|
||||
this.coverLocation = coverLocation;
|
||||
this.fluid = fluid;
|
||||
this.flipGas = flipGas;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies()
|
||||
{
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextures()
|
||||
{
|
||||
ImmutableSet.Builder<ResourceLocation> builder = ImmutableSet.builder();
|
||||
if (baseLocation != null)
|
||||
builder.add(baseLocation);
|
||||
if (liquidLocation != null)
|
||||
builder.add(liquidLocation);
|
||||
if (coverLocation != null)
|
||||
builder.add(coverLocation);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFlexibleBakedModel bake(IModelState state, VertexFormat format,
|
||||
Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
|
||||
{
|
||||
|
||||
ImmutableMap<TransformType, TRSRTransformation> transformMap = IPerspectiveAwareModel.MapWrapper.getTransforms(state);
|
||||
|
||||
// if the fluid is a gas wi manipulate the initial state to be rotated 180° to turn it upside down
|
||||
if (flipGas && fluid.isGaseous())
|
||||
{
|
||||
state = new ModelStateComposition(state, TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, new Quat4f(0, 0, 1, 0), null, null)));
|
||||
}
|
||||
|
||||
TRSRTransformation transform = state.apply(Optional.<IModelPart>absent()).or(TRSRTransformation.identity());
|
||||
TextureAtlasSprite fluidSprite = bakedTextureGetter.apply(fluid.getStill());
|
||||
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
|
||||
|
||||
if (baseLocation != null)
|
||||
{
|
||||
// build base (insidest)
|
||||
IFlexibleBakedModel model = (new ItemLayerModel(ImmutableList.of(baseLocation))).bake(state, format, bakedTextureGetter);
|
||||
builder.addAll(model.getGeneralQuads());
|
||||
}
|
||||
if (liquidLocation != null)
|
||||
{
|
||||
TextureAtlasSprite liquid = bakedTextureGetter.apply(liquidLocation);
|
||||
// build liquid layer (inside)
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, NORTH_Z_FLUID, EnumFacing.NORTH, fluid.getColor()));
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, SOUTH_Z_FLUID, EnumFacing.SOUTH, fluid.getColor()));
|
||||
}
|
||||
if (coverLocation != null)
|
||||
{
|
||||
// cover (the actual item around the other two)
|
||||
TextureAtlasSprite base = bakedTextureGetter.apply(coverLocation);
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_BASE, base, EnumFacing.NORTH, 0xffffffff));
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_BASE, base, EnumFacing.SOUTH, 0xffffffff));
|
||||
}
|
||||
|
||||
|
||||
return new BakedDynBucket(this, builder.build(), fluidSprite, format, Maps.immutableEnumMap(transformMap), Maps.<String, IFlexibleBakedModel>newHashMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModelState getDefaultState()
|
||||
{
|
||||
return TRSRTransformation.identity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the liquid in the model.
|
||||
* fluid - Name of the fluid in the FluidRegistry
|
||||
* flipGas - If "true" the model will be flipped upside down if the liquid is a gas. If "false" it wont
|
||||
* <p/>
|
||||
* If the fluid can't be found, water is used
|
||||
*/
|
||||
@Override
|
||||
public IModel process(ImmutableMap<String, String> customData)
|
||||
{
|
||||
String fluidName = customData.get("fluid");
|
||||
Fluid fluid = FluidRegistry.getFluid(fluidName);
|
||||
|
||||
if (fluid == null) fluid = this.fluid;
|
||||
|
||||
boolean flip = flipGas;
|
||||
if (customData.containsKey("flipGas"))
|
||||
{
|
||||
String flipStr = customData.get("flipGas");
|
||||
if (flipStr.equals("true")) flip = true;
|
||||
else if (flipStr.equals("false")) flip = false;
|
||||
else
|
||||
throw new IllegalArgumentException(String.format("DynBucket custom data \"flipGas\" must have value \'true\' or \'false\' (was \'%s\')", flipStr));
|
||||
}
|
||||
|
||||
// create new model with correct liquid
|
||||
return new ModelDynBucket(baseLocation, liquidLocation, coverLocation, fluid, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to use different textures for the model.
|
||||
* There are 3 layers:
|
||||
* base - The empty bucket/container
|
||||
* fluid - A texture representing the liquid portion. Non-transparent = liquid
|
||||
* cover - An overlay that's put over the liquid (optional)
|
||||
* <p/>
|
||||
* If no liquid is given a hardcoded variant for the bucket is used.
|
||||
*/
|
||||
@Override
|
||||
public IModel retexture(ImmutableMap<String, String> textures)
|
||||
{
|
||||
|
||||
ResourceLocation base = baseLocation;
|
||||
ResourceLocation liquid = liquidLocation;
|
||||
ResourceLocation cover = coverLocation;
|
||||
|
||||
if (textures.containsKey("base"))
|
||||
base = new ResourceLocation(textures.get("base"));
|
||||
if (textures.containsKey("fluid"))
|
||||
liquid = new ResourceLocation(textures.get("fluid"));
|
||||
if (textures.containsKey("cover"))
|
||||
cover = new ResourceLocation(textures.get("cover"));
|
||||
|
||||
return new ModelDynBucket(base, liquid, cover, fluid, flipGas);
|
||||
}
|
||||
|
||||
public enum LoaderDynBucket implements ICustomModelLoader
|
||||
{
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean accepts(ResourceLocation modelLocation)
|
||||
{
|
||||
return modelLocation.getResourceDomain().equals("forge") && modelLocation.getResourcePath().contains("forgebucket");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModel loadModel(ResourceLocation modelLocation) throws IOException
|
||||
{
|
||||
return MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
// no need to clear cache since we create a new model instance
|
||||
}
|
||||
}
|
||||
|
||||
// the dynamic bucket is based on the empty bucket
|
||||
protected static class BakedDynBucket extends ItemLayerModel.BakedModel implements ISmartItemModel, IPerspectiveAwareModel
|
||||
{
|
||||
|
||||
private final ModelDynBucket parent;
|
||||
private final Map<String, IFlexibleBakedModel> cache; // contains all the baked models since they'll never change
|
||||
private final ImmutableMap<TransformType, TRSRTransformation> transforms;
|
||||
|
||||
public BakedDynBucket(ModelDynBucket parent,
|
||||
ImmutableList<BakedQuad> quads, TextureAtlasSprite particle, VertexFormat format, ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms,
|
||||
Map<String, IFlexibleBakedModel> cache)
|
||||
{
|
||||
super(quads, particle, format);
|
||||
this.parent = parent;
|
||||
this.transforms = transforms;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel handleItemState(ItemStack stack)
|
||||
{
|
||||
FluidStack fluidStack = FluidContainerRegistry.getFluidForFilledItem(stack);
|
||||
if (fluidStack == null)
|
||||
{
|
||||
if (stack.getItem() instanceof IFluidContainerItem)
|
||||
{
|
||||
fluidStack = ((IFluidContainerItem) stack.getItem()).getFluid(stack);
|
||||
}
|
||||
}
|
||||
|
||||
// not a fluid item apparently
|
||||
if (fluidStack == null)
|
||||
return this;
|
||||
|
||||
|
||||
Fluid fluid = fluidStack.getFluid();
|
||||
String name = fluid.getName();
|
||||
|
||||
if (!cache.containsKey(name))
|
||||
{
|
||||
IModel model = parent.process(ImmutableMap.of("fluid", name));
|
||||
Function<ResourceLocation, TextureAtlasSprite> textureGetter;
|
||||
textureGetter = new Function<ResourceLocation, TextureAtlasSprite>()
|
||||
{
|
||||
public TextureAtlasSprite apply(ResourceLocation location)
|
||||
{
|
||||
return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString());
|
||||
}
|
||||
};
|
||||
|
||||
IFlexibleBakedModel bakedModel = model.bake(new SimpleModelState(transforms), this.getFormat(), textureGetter);
|
||||
cache.put(name, bakedModel);
|
||||
return bakedModel;
|
||||
}
|
||||
|
||||
return cache.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IFlexibleBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
|
||||
{
|
||||
return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, transforms, cameraTransformType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,10 +45,17 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.ModelRotation;
|
||||
import net.minecraft.client.resources.model.SimpleBakedModel;
|
||||
import net.minecraft.client.resources.model.WeightedBakedModel;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.IRegistry;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidContainerRegistry;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.registry.GameData;
|
||||
import net.minecraftforge.fml.common.registry.RegistryDelegate;
|
||||
@@ -177,6 +184,7 @@ public class ModelLoader extends ModelBakery
|
||||
registerVariantNames();
|
||||
for(Item item : GameData.getItemRegistry().typeSafeIterable())
|
||||
{
|
||||
// default loading
|
||||
for(String s : (List<String>)getVariantNames(item))
|
||||
{
|
||||
ResourceLocation file = getItemLocation(s);
|
||||
@@ -201,6 +209,68 @@ public class ModelLoader extends ModelBakery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replace vanilla bucket models if desired. done afterwards for performance reasons
|
||||
if(ForgeModContainer.replaceVanillaBucketModel)
|
||||
{
|
||||
// empty bucket
|
||||
for(String s : getVariantNames(Items.bucket))
|
||||
{
|
||||
ModelResourceLocation memory = new ModelResourceLocation(s, "inventory");
|
||||
try
|
||||
{
|
||||
IModel model = getModel(new ResourceLocation("forge", "item/bucket"));
|
||||
// only on successful load, otherwise continue using the old model
|
||||
stateModels.put(memory, model);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
// use the original vanilla model
|
||||
}
|
||||
}
|
||||
|
||||
setBucketModel(Items.water_bucket);
|
||||
setBucketModel(Items.lava_bucket);
|
||||
// milk bucket only replaced if some mod adds milk
|
||||
if(FluidRegistry.isFluidRegistered("milk"))
|
||||
{
|
||||
// can the milk be put into a bucket?
|
||||
Fluid milk = FluidRegistry.getFluid("milk");
|
||||
FluidStack milkStack = new FluidStack(milk, FluidContainerRegistry.BUCKET_VOLUME);
|
||||
if(FluidContainerRegistry.getContainerCapacity(milkStack, new ItemStack(Items.bucket)) == FluidContainerRegistry.BUCKET_VOLUME)
|
||||
{
|
||||
setBucketModel(Items.milk_bucket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// milk bucket if no milk fluid is present
|
||||
for(String s : getVariantNames(Items.milk_bucket))
|
||||
{
|
||||
ModelResourceLocation memory = new ModelResourceLocation(s, "inventory");
|
||||
try
|
||||
{
|
||||
IModel model = getModel(new ResourceLocation("forge", "item/bucket_milk"));
|
||||
// only on successful load, otherwise continue using the old model
|
||||
stateModels.put(memory, model);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
// use the original vanilla model
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setBucketModel(Item item)
|
||||
{
|
||||
for(String s : getVariantNames(item))
|
||||
{
|
||||
ModelResourceLocation memory = new ModelResourceLocation(s, "inventory");
|
||||
IModel model = stateModels.get(ModelDynBucket.LOCATION);
|
||||
stateModels.put(memory, model);
|
||||
}
|
||||
}
|
||||
|
||||
public IModel getModel(ResourceLocation location) throws IOException
|
||||
|
||||
@@ -34,6 +34,7 @@ public class ModelLoaderRegistry
|
||||
registerLoader(ModelFluid.FluidLoader.instance);
|
||||
registerLoader(ItemLayerModel.Loader.instance);
|
||||
registerLoader(MultiLayerModel.Loader.instance);
|
||||
registerLoader(ModelDynBucket.LoaderDynBucket.instance);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import static net.minecraftforge.common.config.Configuration.CATEGORY_CLIENT;
|
||||
import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL;
|
||||
|
||||
import java.io.File;
|
||||
@@ -68,6 +69,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
|
||||
public static int defaultSpawnFuzz = 20;
|
||||
public static boolean defaultHasSpawnFuzz = true;
|
||||
public static boolean forgeLightPipelineEnabled = true;
|
||||
public static boolean replaceVanillaBucketModel = true;
|
||||
|
||||
private static Configuration config;
|
||||
private static ForgeModContainer INSTANCE;
|
||||
@@ -244,6 +246,16 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
|
||||
|
||||
config.setCategoryPropertyOrder(VERSION_CHECK_CAT, propOrder);
|
||||
|
||||
// Client-Side only properties
|
||||
propOrder = new ArrayList<String>();
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "replaceVanillaBucketModel", Boolean.FALSE,
|
||||
"Replace the vanilla bucket models with Forges own dynamic bucket model. Unifies bucket visuals if a mod uses the Forge bucket model.");
|
||||
prop.setLanguageKey("forge.configgui.replaceBuckets").setRequiresMcRestart(true);
|
||||
replaceVanillaBucketModel = prop.getBoolean(Boolean.FALSE);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
config.setCategoryPropertyOrder(CATEGORY_CLIENT, propOrder);
|
||||
|
||||
if (config.hasChanged())
|
||||
{
|
||||
config.save();
|
||||
|
||||
@@ -48,6 +48,7 @@ import net.minecraftforge.fml.relauncher.FMLInjectionData;
|
||||
public class Configuration
|
||||
{
|
||||
public static final String CATEGORY_GENERAL = "general";
|
||||
public static final String CATEGORY_CLIENT = "client";
|
||||
public static final String ALLOWED_CHARS = "._-";
|
||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||
public static final String CATEGORY_SPLITTER = ".";
|
||||
|
||||
Reference in new issue
Block a user