ForgePatch/src/main/java/net/minecraftforge/client/model/ModelFluid.java

431 lines
16 KiB
Java
Raw Normal View History

/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* 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
*/
2015-06-18 11:14:46 +00:00
package net.minecraftforge.client.model;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector4f;
2015-06-18 11:14:46 +00:00
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelRotation;
2015-06-18 11:14:46 +00:00
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
2015-06-18 11:14:46 +00:00
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.fluids.BlockFluidBase;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Function;
import java.util.Optional;
2015-12-30 10:31:36 +00:00
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
2015-06-18 11:14:46 +00:00
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 com.google.gson.JsonElement;
import com.google.gson.JsonParser;
public final class ModelFluid implements IModel
2015-06-18 11:14:46 +00:00
{
2016-03-21 07:23:27 +00:00
public static final ModelFluid WATER = new ModelFluid(FluidRegistry.WATER);
public static final ModelFluid LAVA = new ModelFluid(FluidRegistry.LAVA);
2015-06-18 11:14:46 +00:00
private final Fluid fluid;
public ModelFluid(Fluid fluid)
{
this.fluid = fluid;
}
public Collection<ResourceLocation> getTextures()
{
return ImmutableSet.of(fluid.getStill(), fluid.getFlowing());
}
2017-06-18 01:24:17 +00:00
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
2015-06-18 11:14:46 +00:00
{
ImmutableMap<TransformType, TRSRTransformation> map = PerspectiveMapWrapper.getTransforms(state);
return new BakedFluid(state.apply(Optional.empty()), map, format, fluid.getColor(), bakedTextureGetter.apply(fluid.getStill()), bakedTextureGetter.apply(fluid.getFlowing()), fluid.isGaseous(), Optional.empty());
2015-06-18 11:14:46 +00:00
}
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public IModelState getDefaultState()
{
return ModelRotation.X0_Y0;
}
public static enum FluidLoader implements ICustomModelLoader
{
2016-03-21 07:23:27 +00:00
INSTANCE;
2015-06-18 11:14:46 +00:00
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public void onResourceManagerReload(IResourceManager resourceManager) {}
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public boolean accepts(ResourceLocation modelLocation)
{
return modelLocation.getResourceDomain().equals(ForgeVersion.MOD_ID) && (
2015-06-18 11:14:46 +00:00
modelLocation.getResourcePath().equals("fluid") ||
modelLocation.getResourcePath().equals("models/block/fluid") ||
modelLocation.getResourcePath().equals("models/item/fluid"));
}
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public IModel loadModel(ResourceLocation modelLocation)
{
2016-03-21 07:23:27 +00:00
return WATER;
2015-06-18 11:14:46 +00:00
}
}
private static final class BakedFluid implements IBakedModel
2015-06-18 11:14:46 +00:00
{
private static final int x[] = { 0, 0, 1, 1 };
private static final int z[] = { 0, 1, 1, 0 };
private static final float eps = 1e-3f;
2015-12-30 10:31:36 +00:00
private final LoadingCache<Long, BakedFluid> modelCache = CacheBuilder.newBuilder().maximumSize(200).build(new CacheLoader<Long, BakedFluid>()
{
2017-06-18 01:24:17 +00:00
@Override
2015-12-30 10:31:36 +00:00
public BakedFluid load(Long key) throws Exception
{
boolean statePresent = (key & 1) != 0;
key >>>= 1;
int[] cornerRound = new int[4];
for(int i = 0; i < 4; i++)
{
cornerRound[i] = (int)(key & 0x3FF);
key >>>= 10;
}
int flowRound = (int)(key & 0x7FF) - 1024;
return new BakedFluid(transformation, transforms, format, color, still, flowing, gas, statePresent, cornerRound, flowRound);
2015-12-30 10:31:36 +00:00
}
});
private final Optional<TRSRTransformation> transformation;
private final ImmutableMap<TransformType, TRSRTransformation> transforms;
2015-06-18 11:14:46 +00:00
private final VertexFormat format;
private final int color;
private final TextureAtlasSprite still, flowing;
private final boolean gas;
private final EnumMap<EnumFacing, List<BakedQuad>> faceQuads;
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, boolean gas, Optional<IExtendedBlockState> stateOption)
{
this(transformation, transforms, format, color, still, flowing, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption));
2015-12-30 10:31:36 +00:00
}
private static int[] getCorners(Optional<IExtendedBlockState> stateOption)
{
int[] cornerRound = new int[]{0, 0, 0, 0};
if(stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
for(int i = 0; i < 4; i++)
{
Float level = state.getValue(BlockFluidBase.LEVEL_CORNERS[i]);
cornerRound[i] = Math.round((level == null ? 7f / 8 : level) * 768);
2015-12-30 10:31:36 +00:00
}
}
return cornerRound;
}
private static int getFlow(Optional<IExtendedBlockState> stateOption)
{
Float flow = -1000f;
2015-12-30 10:31:36 +00:00
if(stateOption.isPresent())
{
flow = stateOption.get().getValue(BlockFluidBase.FLOW_DIRECTION);
if(flow == null) flow = -1000f;
2015-12-30 10:31:36 +00:00
}
int flowRound = (int)Math.round(Math.toDegrees(flow));
flowRound = MathHelper.clamp(flowRound, -1000, 1000);
2015-12-30 10:31:36 +00:00
return flowRound;
}
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, boolean gas, boolean statePresent, int[] cornerRound, int flowRound)
2015-06-18 11:14:46 +00:00
{
this.transformation = transformation;
this.transforms = transforms;
2015-06-18 11:14:46 +00:00
this.format = format;
this.color = color;
this.still = still;
this.flowing = flowing;
this.gas = gas;
faceQuads = Maps.newEnumMap(EnumFacing.class);
for(EnumFacing side : EnumFacing.values())
{
faceQuads.put(side, ImmutableList.of());
2015-06-18 11:14:46 +00:00
}
2015-12-30 10:31:36 +00:00
if(statePresent)
2015-06-18 11:14:46 +00:00
{
float[] y = new float[4];
for(int i = 0; i < 4; i++)
{
if(gas)
{
2015-12-30 10:31:36 +00:00
y[i] = 1 - cornerRound[i] / 768f;
2015-06-18 11:14:46 +00:00
}
else
{
2015-12-30 10:31:36 +00:00
y[i] = cornerRound[i] / 768f;
2015-06-18 11:14:46 +00:00
}
}
2015-12-30 10:31:36 +00:00
float flow = (float)Math.toRadians(flowRound);
2015-06-18 11:14:46 +00:00
// top
TextureAtlasSprite topSprite = flowing;
float scale = 4;
if(flow < -17F)
2015-06-18 11:14:46 +00:00
{
flow = 0;
scale = 8;
topSprite = still;
}
float c = MathHelper.cos(flow) * scale;
float s = MathHelper.sin(flow) * scale;
EnumFacing side = gas ? EnumFacing.DOWN : EnumFacing.UP;
UnpackedBakedQuad.Builder builder;
ImmutableList.Builder<BakedQuad> topFaceBuilder = ImmutableList.builder();
for(int k = 0; k < 2; k++)
2015-06-18 11:14:46 +00:00
{
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(topSprite);
builder.setQuadTint(0);
for (int i = gas ? 3 : 0; i != (gas ? -1 : 4); i += (gas ? -1 : 1))
{
int l = (k * 3) + (1 - 2 * k) * i;
putVertex(
builder, side,
x[l], y[l], z[l],
topSprite.getInterpolatedU(8 + c * (x[l] * 2 - 1) + s * (z[l] * 2 - 1)),
topSprite.getInterpolatedV(8 + c * (x[(l + 1) % 4] * 2 - 1) + s * (z[(l + 1) % 4] * 2 - 1)));
}
topFaceBuilder.add(builder.build());
2015-06-18 11:14:46 +00:00
}
faceQuads.put(side, topFaceBuilder.build());
2015-06-18 11:14:46 +00:00
// bottom
side = side.getOpposite();
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(still);
builder.setQuadTint(0);
2015-06-18 11:14:46 +00:00
for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1))
{
putVertex(
builder, side,
2015-06-18 11:14:46 +00:00
z[i], gas ? 1 : 0, x[i],
still.getInterpolatedU(z[i] * 16),
still.getInterpolatedV(x[i] * 16));
}
faceQuads.put(side, ImmutableList.of(builder.build()));
2015-06-18 11:14:46 +00:00
// sides
for(int i = 0; i < 4; i++)
{
side = EnumFacing.getHorizontal((5 - i) % 4);
BakedQuad q[] = new BakedQuad[2];
for(int k = 0; k < 2; k++)
{
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(flowing);
builder.setQuadTint(0);
2015-06-18 11:14:46 +00:00
for(int j = 0; j < 4; j++)
{
int l = (k * 3) + (1 - 2 * k) * j;
float yl = z[l] * y[(i + x[l]) % 4];
if(gas && z[l] == 0) yl = 1;
putVertex(
builder, side,
2015-06-18 11:14:46 +00:00
x[(i + x[l]) % 4], yl, z[(i + x[l]) % 4],
flowing.getInterpolatedU(x[l] * 8),
flowing.getInterpolatedV((gas ? yl : 1 - yl) * 8));
}
q[k] = builder.build();
2015-06-18 11:14:46 +00:00
}
faceQuads.put(side, ImmutableList.of(q[0], q[1]));
}
}
else
{
// 1 quad for inventory
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(EnumFacing.UP);
builder.setTexture(still);
builder.setQuadTint(0); //I dont know if we also need this in inventory, but now it should be possible to color it here as well
2015-06-18 11:14:46 +00:00
for(int i = 0; i < 4; i++)
{
putVertex(
builder, EnumFacing.UP,
2015-06-18 11:14:46 +00:00
z[i], x[i], 0,
still.getInterpolatedU(z[i] * 16),
still.getInterpolatedV(x[i] * 16));
2015-06-18 11:14:46 +00:00
}
faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(builder.build()));
2015-06-18 11:14:46 +00:00
}
}
private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, float x, float y, float z, float u, float v)
2015-06-18 11:14:46 +00:00
{
for(int e = 0; e < format.getElementCount(); e++)
2015-06-18 11:14:46 +00:00
{
switch(format.getElement(e).getUsage())
2015-06-18 11:14:46 +00:00
{
case POSITION:
float[] data = new float[]{ x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1 };
if(transformation.isPresent() && !transformation.get().isIdentity())
{
Vector4f vec = new Vector4f(data);
transformation.get().getMatrix().transform(vec);
vec.get(data);
}
builder.put(e, data);
2015-06-18 11:14:46 +00:00
break;
case COLOR:
builder.put(e,
((color >> 16) & 0xFF) / 255f,
((color >> 8) & 0xFF) / 255f,
(color & 0xFF) / 255f,
2015-06-18 11:14:46 +00:00
((color >> 24) & 0xFF) / 255f);
break;
case UV: if(format.getElement(e).getIndex() == 0)
{
builder.put(e, u, v, 0f, 1f);
2015-06-18 11:14:46 +00:00
break;
}
2015-06-18 11:14:46 +00:00
case NORMAL:
builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f);
2015-06-18 11:14:46 +00:00
break;
default:
builder.put(e);
2015-06-18 11:14:46 +00:00
break;
}
}
}
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public boolean isAmbientOcclusion()
{
2016-04-06 13:13:51 +00:00
return true;
2015-06-18 11:14:46 +00:00
}
2017-06-18 01:24:17 +00:00
@Override
2015-06-18 11:14:46 +00:00
public boolean isGui3d()
{
return false;
}
2017-06-18 01:24:17 +00:00
@Override
public boolean isBuiltInRenderer()
2015-06-18 11:14:46 +00:00
{
return false;
}
2017-06-18 01:24:17 +00:00
@Override
2015-12-30 04:15:03 +00:00
public TextureAtlasSprite getParticleTexture()
2015-06-18 11:14:46 +00:00
{
return still;
}
2017-06-18 01:24:17 +00:00
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
2015-06-18 11:14:46 +00:00
{
BakedFluid model = this;
2015-12-30 10:31:36 +00:00
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
int[] cornerRound = getCorners(Optional.of(exState));
int flowRound = getFlow(Optional.of(exState));
long key = flowRound + 1024;
for(int i = 3; i >= 0; i--)
{
key <<= 10;
key |= cornerRound[i];
}
key <<= 1;
key |= 1;
model = modelCache.getUnchecked(key);
2015-12-30 10:31:36 +00:00
}
if(side == null) return ImmutableList.of();
return model.faceQuads.get(side);
}
2017-06-18 01:24:17 +00:00
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
2015-06-18 11:14:46 +00:00
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
{
return PerspectiveMapWrapper.handlePerspective(this, transforms, type);
}
2015-06-18 11:14:46 +00:00
}
@Override
public ModelFluid process(ImmutableMap<String, String> customData)
2015-06-18 11:14:46 +00:00
{
if(!customData.containsKey("fluid")) return this;
String fluidStr = customData.get("fluid");
JsonElement e = new JsonParser().parse(fluidStr);
String fluid = e.getAsString();
if(!FluidRegistry.isFluidRegistered(fluid))
{
FMLLog.log.fatal("fluid '{}' not found", fluid);
2016-03-21 07:23:27 +00:00
return WATER;
2015-06-18 11:14:46 +00:00
}
return new ModelFluid(FluidRegistry.getFluid(fluid));
}
}