Add new system for model data, replacing extended states (#5564)

This commit is contained in:
tterrag authored and GitHub committed 2019-05-09 02:10:38 -04:00
1 parent a7204b5cd3
commit 9209c3cbfe
29 files changed
+718 -572

No files matched your search

@@ -20,14 +20,28 @@
package net.minecraftforge.client;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.chunk.RenderChunkCache;
import net.minecraft.client.renderer.texture.NativeImage;
@@ -36,13 +50,10 @@ import net.minecraft.resources.IResourceManager;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.data.IModelData;
public class MinecraftForgeClient
{
@@ -19,17 +19,34 @@
package net.minecraftforge.client.extensions;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldReader;
import net.minecraftforge.client.model.data.IModelData;
public interface IForgeBakedModel
{
{
default IBakedModel getBakedModel()
{
return (IBakedModel) this;
}
@Nonnull
default List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand, @Nonnull IModelData extraData)
{
return getBakedModel().getQuads(state, side, rand);
}
default boolean isAmbientOcclusion(IBlockState state) { return getBakedModel().isAmbientOcclusion(); }
/*
@@ -40,4 +57,9 @@ public interface IForgeBakedModel
{
return net.minecraftforge.client.ForgeHooksClient.handlePerspective(getBakedModel(), cameraTransformType);
}
default @Nonnull IModelData getModelData(@Nonnull IWorldReader world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull IModelData tileData)
{
return tileData;
}
}
@@ -0,0 +1,101 @@
package net.minecraftforge.client.model;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.client.Minecraft;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraftforge.client.model.data.IModelData;
public class ModelDataManager
{
private static WeakReference<World> currentWorld = new WeakReference<>(null);
private static final Map<ChunkPos, Set<BlockPos>> needModelDataRefresh = new HashMap<>();
private static final LoadingCache<ChunkPos, Map<BlockPos, IModelData>> modelDataCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.concurrencyLevel(5)
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(new CacheLoader<ChunkPos, Map<BlockPos, IModelData>>(){
@Override
public Map<BlockPos, IModelData> load(@Nonnull ChunkPos key)
{
return new ConcurrentHashMap<>();
}
});
private static void cleanCaches(World world)
{
if (world != currentWorld.get())
{
currentWorld = new WeakReference<>(world);
needModelDataRefresh.clear();
modelDataCache.invalidateAll();
}
}
public static void requestModelDataRefresh(TileEntity te)
{
World world = te.getWorld();
Preconditions.checkNotNull(world, "Tile entity world must not be null");
Preconditions.checkArgument(world == Minecraft.getInstance().world, "Cannot request a model data refresh for a world other than the current client world");
synchronized (needModelDataRefresh)
{
cleanCaches(world);
needModelDataRefresh.computeIfAbsent(new ChunkPos(te.getPos()), $ -> Collections.synchronizedSet(new HashSet<>()))
.add(te.getPos());
}
}
private static void refreshModelData(World world, ChunkPos chunk)
{
Set<BlockPos> needUpdate;
synchronized (needModelDataRefresh)
{
cleanCaches(world);
needUpdate = needModelDataRefresh.remove(chunk);
}
if (needUpdate != null)
{
Map<BlockPos, IModelData> data = modelDataCache.getUnchecked(chunk);
for (BlockPos pos : needUpdate)
{
TileEntity toUpdate = world.getTileEntity(pos);
if (toUpdate != null)
{
data.put(pos, toUpdate.getModelData());
}
}
}
}
public static @Nullable IModelData getModelData(World world, BlockPos pos)
{
return getModelData(world, new ChunkPos(pos)).get(pos);
}
public static Map<BlockPos, IModelData> getModelData(World world, ChunkPos pos)
{
refreshModelData(world, pos);
return modelDataCache.getUnchecked(pos);
}
}
@@ -43,13 +43,13 @@ import net.minecraft.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.TRSRTransformer;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.fluids.Fluid;
import org.apache.commons.lang3.tuple.Pair;
@@ -103,7 +103,7 @@ public final class ModelFluid implements IUnbakedModel
bakedTextureGetter.apply(fluid.getFlowing()),
Optional.ofNullable(fluid.getOverlay()).map(bakedTextureGetter),
fluid.isLighterThanAir(),
Optional.empty()
null
);
}
@@ -157,7 +157,7 @@ public final class ModelFluid implements IUnbakedModel
}
});
public CachingBakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, Optional<IExtendedBlockState> stateOption)
public CachingBakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, Optional<IModelData> stateOption)
{
super(transformation, transforms, format, color, still, flowing, overlay, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption), getOverlay(stateOption));
}
@@ -171,12 +171,12 @@ public final class ModelFluid implements IUnbakedModel
* while also providing good use of the available value range.
* (For fluids with default quanta, this evenly divides the per-block intervals of 1/9 by 96)
*/
private static int[] getCorners(Optional<IExtendedBlockState> stateOption)
private static int[] getCorners(Optional<IModelData> stateOption)
{
int[] cornerRound = {0, 0, 0, 0};
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
IModelData state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Float level = null; // TODO fluids state.getValue(BlockFluidBase.LEVEL_CORNERS[i]);
@@ -193,7 +193,7 @@ public final class ModelFluid implements IUnbakedModel
* The value is currently stored as the angle rounded to the nearest degree.
* A value of -1000 is used to signify no flow.
*/
private static int getFlow(Optional<IExtendedBlockState> stateOption)
private static int getFlow(Optional<IModelData> stateOption)
{
Float flow = -1000f;
if (stateOption.isPresent())
@@ -213,12 +213,12 @@ public final class ModelFluid implements IUnbakedModel
* instead of the normal "flowing" texture (if applicable for that fluid).
* The sides are stored here by their regular horizontal index.
*/
private static boolean[] getOverlay(Optional<IExtendedBlockState> stateOption)
private static boolean[] getOverlay(Optional<IModelData> stateOption)
{
boolean[] overlaySides = new boolean[4];
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
IModelData state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Boolean overlay = null; // TODO fluids state.getValue(BlockFluidBase.SIDE_OVERLAYS[i]);
@@ -229,11 +229,11 @@ public final class ModelFluid implements IUnbakedModel
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand)
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData modelData)
{
if (side != null && state instanceof IExtendedBlockState)
if (side != null)
{
Optional<IExtendedBlockState> exState = Optional.of((IExtendedBlockState)state);
Optional<IModelData> exState = Optional.of(modelData);
int[] cornerRound = getCorners(exState);
int flowRound = getFlow(exState);
@@ -77,12 +77,12 @@ import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.IRegistry;
import net.minecraftforge.client.model.animation.AnimationItemOverrideList;
import net.minecraftforge.client.model.animation.ModelBlockAnimation;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.client.ClientModLoader;
@@ -510,20 +510,12 @@ public final class ModelLoader extends ModelBakery
private final ItemOverrideList overrides = new AnimationItemOverrideList(VanillaModelWrapper.this, modelState, format, bakedTextureGetter, super.getOverrides());
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand)
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData modelData)
{
if(state instanceof IExtendedBlockState)
IModelState newState = modelData.getData(Properties.AnimationProperty);
if(newState != null)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
IModelState newState = exState.getValue(Properties.AnimationProperty);
IExtendedBlockState newExState = (IExtendedBlockState) exState.withProperty(Properties.AnimationProperty, null);
if(newState != null)
{
return VanillaModelWrapper.this.bake(modelGetter, bakedTextureGetter, new ModelStateComposition(modelState, newState), uvlock, format).getQuads(newExState, side, rand);
}
}
return VanillaModelWrapper.this.bake(modelGetter, bakedTextureGetter, new ModelStateComposition(modelState, newState), uvlock, format).getQuads(state, side, rand, modelData);
}
return super.getQuads(state, side, rand);
}
@@ -19,26 +19,26 @@
package net.minecraftforge.client.model.animation;
import java.util.Random;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldReader;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.ModelDataManager;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.animation.IEventHandler;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.common.util.LazyOptional;
import java.util.Random;
/**
* Generic {@link TileGameRenderer} that works with the Forge model system and animations.
*/
@@ -58,30 +58,23 @@ public class TileEntityRendererAnimation<T extends TileEntity> extends TileEntit
BlockPos pos = te.getPos();
IWorldReader world = MinecraftForgeClient.getRegionRenderCache(te.getWorld(), pos);
IBlockState state = world.getBlockState(pos);
if(state.getBlock().getStateContainer().getProperties().contains(Properties.StaticProperty))
IBakedModel model = blockRenderer.getBlockModelShapes().getModel(state);
IModelData data = model.getModelData(world, pos, state, ModelDataManager.getModelData(te.getWorld(), pos));
if (data.hasProperty(Properties.AnimationProperty))
{
state = state.with(Properties.StaticProperty, false);
}
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
float time = Animation.getWorldTime(getWorld(), partialTick);
cap
.map(asm -> asm.apply(time))
.ifPresent(pair -> {
handleEvents(te, time, pair.getRight());
float time = Animation.getWorldTime(getWorld(), partialTick);
cap
.map(asm -> asm.apply(time))
.ifPresent(pair -> {
handleEvents(te, time, pair.getRight());
// TODO: caching?
IBakedModel model = blockRenderer.getBlockModelShapes().getModel(exState.getClean());
IExtendedBlockState animState = (IExtendedBlockState) exState.withProperty(Properties.AnimationProperty, pair.getLeft());
// TODO: caching?
data.setData(Properties.AnimationProperty, pair.getLeft());
renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ());
renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ());
blockRenderer.getBlockModelRenderer().renderModel(world, model, animState, pos, renderer, false, new Random(), 42);
});
}
blockRenderer.getBlockModelRenderer().renderModel(world, model, state, pos, renderer, false, new Random(), 42, data);
});
}
}
@@ -27,13 +27,33 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.base.Objects;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
@@ -59,6 +79,8 @@ import net.minecraftforge.client.model.b3d.B3DModel.Mesh;
import net.minecraftforge.client.model.b3d.B3DModel.Node;
import net.minecraftforge.client.model.b3d.B3DModel.Texture;
import net.minecraftforge.client.model.b3d.B3DModel.Vertex;
import net.minecraftforge.client.model.data.IDynamicBakedModel;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
@@ -66,30 +88,8 @@ import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.model.animation.IJoint;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.function.Function;
import com.google.common.base.Objects;
import java.util.Optional;
import java.util.Random;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/*
* Loader for Blitz3D models.
* To enable for your mod call instance.addDomain(modId).
@@ -599,7 +599,7 @@ public enum B3DLoader implements ICustomModelLoader
}
}
private static final class BakedWrapper implements IBakedModel
private static final class BakedWrapper implements IDynamicBakedModel
{
private final Node<?> node;
private final IModelState state;
@@ -647,34 +647,27 @@ public enum B3DLoader implements ICustomModelLoader
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand)
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, Random rand, IModelData data)
{
if(side != null) return ImmutableList.of();
IModelState modelState = this.state;
if(state instanceof IExtendedBlockState)
IModelState newState = data.getData(Properties.AnimationProperty);
if(newState != null)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
// FIXME: should animation state handle the parent state, or should it remain here?
IModelState parent = this.state;
if(parent instanceof B3DState)
{
// FIXME: should animation state handle the parent state, or should it remain here?
IModelState parent = this.state;
if(parent instanceof B3DState)
{
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
IModelState newState = exState.getValue(Properties.AnimationProperty);
if(newState != null)
{
if (parent == null)
{
modelState = newState;
}
else
{
modelState = new ModelStateComposition(parent, newState);
}
}
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
if (parent == null)
{
modelState = newState;
}
else
{
modelState = new ModelStateComposition(parent, newState);
}
}
if(quads == null)
@@ -0,0 +1,15 @@
package net.minecraftforge.client.model.data;
public enum EmptyModelData implements IModelData
{
INSTANCE;
@Override
public boolean hasProperty(ModelProperty<?> prop) { return false; }
@Override
public <T> T getData(ModelProperty<T> prop) { return null; }
@Override
public <T> T setData(ModelProperty<T> prop, T data) { return null; }
}
@@ -0,0 +1,29 @@
package net.minecraftforge.client.model.data;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.util.EnumFacing;
/**
* Convenience interface with default implementation of {@link IBakedModel#getQuads(net.minecraft.block.state.IBlockState, net.minecraft.util.EnumFacing, java.util.Random)}.
*/
public interface IDynamicBakedModel extends IBakedModel
{
@Override
default @Nonnull List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand)
{
return getQuads(state, side, rand, EmptyModelData.INSTANCE);
}
// Force this to be overriden otherwise this introduces a default cycle between the two overloads.
@Override
@Nonnull
List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, @Nonnull Random rand, @Nonnull IModelData extraData);
}
@@ -0,0 +1,25 @@
package net.minecraftforge.client.model.data;
import javax.annotation.Nullable;
public interface IModelData
{
/**
* Check if this data has a property, even if the value is {@code null}. Can be
* used by code that intends to fill in data for a render pipeline, such as the
* forge animation system.
* <p>
* IMPORTANT: {@link #getData(ModelProperty)} <em>can</em> return {@code null}
* even if this method returns {@code true}.
*
* @param prop The property to check for inclusion in this model data
* @return {@code true} if this data has the given property, even if no value is present
*/
boolean hasProperty(ModelProperty<?> prop);
@Nullable
<T> T getData(ModelProperty<T> prop);
@Nullable
<T> T setData(ModelProperty<T> prop, T data);
}
@@ -0,0 +1,58 @@
package net.minecraftforge.client.model.data;
import java.util.IdentityHashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
public class ModelDataMap implements IModelData
{
private final Map<ModelProperty<?>, Object> backingMap;
private ModelDataMap(Map<ModelProperty<?>, Object> map)
{
this.backingMap = new IdentityHashMap<>(map);
}
@Override
public boolean hasProperty(ModelProperty<?> prop)
{
return backingMap.containsKey(prop);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getData(ModelProperty<T> prop)
{
return (T) backingMap.get(prop);
}
@SuppressWarnings("unchecked")
@Override
public <T> T setData(ModelProperty<T> prop, T data)
{
Preconditions.checkArgument(prop.test(data), "Value is invalid for this property");
return (T) backingMap.put(prop, data);
}
public static class Builder
{
private final Map<ModelProperty<?>, Object> defaults = new IdentityHashMap<>();
public Builder withProperty(ModelProperty<?> prop)
{
return withInitial(prop, null);
}
public <T> Builder withInitial(ModelProperty<T> prop, T data)
{
this.defaults.put(prop, data);
return this;
}
public ModelDataMap build()
{
return new ModelDataMap(defaults);
}
}
}
@@ -0,0 +1,23 @@
package net.minecraftforge.client.model.data;
import java.util.function.Predicate;
import com.google.common.base.Predicates;
public class ModelProperty<T> implements Predicate<T> {
private final Predicate<T> pred;
public ModelProperty() {
this(Predicates.alwaysTrue());
}
public ModelProperty(Predicate<T> pred) {
this.pred = pred;
}
@Override
public boolean test(T t) {
return pred.test(t);
}
}
@@ -54,13 +54,13 @@ import net.minecraft.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.*;
import net.minecraftforge.client.model.data.IDynamicBakedModel;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.property.Properties;
import org.apache.commons.lang3.tuple.Pair;
@@ -1255,36 +1255,7 @@ public class OBJModel implements IUnbakedModel
}
}
@Deprecated
public enum OBJProperty implements IUnlistedProperty<OBJState>
{
INSTANCE;
@Override
public String getName()
{
return "OBJProperty";
}
@Override
public boolean isValid(OBJState value)
{
return value instanceof OBJState;
}
@Override
public Class<OBJState> getType()
{
return OBJState.class;
}
@Override
public String valueToString(OBJState value)
{
return value.toString();
}
}
public class OBJBakedModel implements IBakedModel
public class OBJBakedModel implements IDynamicBakedModel
{
private final OBJModel model;
private IModelState state;
@@ -1308,26 +1279,18 @@ public class OBJModel implements IUnbakedModel
// FIXME: merge with getQuads
@Override
public List<BakedQuad> getQuads(IBlockState blockState, EnumFacing side, Random rand)
public List<BakedQuad> getQuads(IBlockState blockState, EnumFacing side, Random rand, IModelData modelData)
{
if (side != null) return ImmutableList.of();
if (quads == null)
{
quads = buildQuads(this.state);
}
if (blockState instanceof IExtendedBlockState)
IModelState newState = modelData.getData(Properties.AnimationProperty);
if (newState != null)
{
IExtendedBlockState exState = (IExtendedBlockState) blockState;
if (exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
IModelState newState = exState.getValue(Properties.AnimationProperty);
if (newState != null)
{
newState = new ModelStateComposition(this.state, newState);
return buildQuads(newState);
}
}
newState = new ModelStateComposition(this.state, newState);
return buildQuads(newState);
}
return quads;
}
@@ -19,6 +19,8 @@
package net.minecraftforge.common.extensions;
import javax.annotation.Nonnull;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
@@ -29,6 +31,9 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.model.ModelDataManager;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
public interface IForgeTileEntity extends ICapabilitySerializable<NBTTagCompound>
@@ -86,7 +91,10 @@ public interface IForgeTileEntity extends ICapabilitySerializable<NBTTagCompound
* Called when this is first added to the world (by {@link World#addTileEntity(TileEntity)}).
* Override instead of adding {@code if (firstTick)} stuff in update.
*/
default void onLoad(){}
default void onLoad()
{
requestModelDataUpdate();
}
default boolean shouldRenderInPass(int pass)
{
@@ -168,4 +176,19 @@ public interface IForgeTileEntity extends ICapabilitySerializable<NBTTagCompound
{
return false;
}
default void requestModelDataUpdate()
{
TileEntity te = getTileEntity();
World world = te.getWorld();
if (world != null && world.isRemote)
{
ModelDataManager.requestModelDataRefresh(te);
}
}
default @Nonnull IModelData getModelData()
{
return EmptyModelData.INSTANCE;
}
}
@@ -1,178 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.property;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import net.minecraft.block.Block;
import net.minecraft.block.state.BlockState;
import net.minecraft.block.state.IBlockState;
import net.minecraft.state.AbstractStateHolder;
import net.minecraft.state.IProperty;
import net.minecraft.state.IStateHolder;
import net.minecraft.state.StateContainer;
// TODO Extended states gotta go, I doubt this works at all
public class ExtendedStateContainer<O, S extends IExtendedState<S>> extends StateContainer<O, S>
{
private final ImmutableSet<IUnlistedProperty<?>> unlistedProperties;
public <A extends AbstractStateHolder<O, S>> ExtendedStateContainer(O blockIn, StateContainer.IFactory<O, S, A> stateFactory, net.minecraft.state.IProperty<?>[] properties, IUnlistedProperty<?>[] unlistedProperties)
{
super(blockIn, getProxyFactory(unlistedProperties, stateFactory), buildListedMap(properties));// TODO Unlisted properties?, buildUnlistedMap(unlistedProperties));
ImmutableSet.Builder<IUnlistedProperty<?>> builder = ImmutableSet.builder();
for(IUnlistedProperty<?> property : unlistedProperties)
{
builder.add(property);
}
this.unlistedProperties = builder.build();
}
private static <O, S extends IExtendedState<S>, A extends AbstractStateHolder<O, S>>
StateContainer.IFactory<O, S, AbstractStateHolder<O,S>> getProxyFactory(IUnlistedProperty<?>[] unlistedProperties, StateContainer.IFactory<O, S, A> proxy)
{
return (o, props) -> {
if (unlistedProperties == null || unlistedProperties.length == 0) return proxy.create(o, props);
return new ExtendedStateHolder<O, S>(o, props, buildUnlistedMap(unlistedProperties), null);
};
}
public Collection<IUnlistedProperty<?>> getUnlistedProperties()
{
return unlistedProperties;
}
private static Map<String, IProperty<?>> buildListedMap(IProperty<?>[] properties)
{
return Arrays.stream(properties).collect(Collectors.toMap(IProperty::getName, Function.identity()));
}
private static ImmutableMap<IUnlistedProperty<?>, Optional<?>> buildUnlistedMap(IUnlistedProperty<?>[] unlistedProperties)
{
ImmutableMap.Builder<IUnlistedProperty<?>, Optional<?>> builder = ImmutableMap.builder();
for(IUnlistedProperty<?> p : unlistedProperties)
{
builder.put(p, Optional.empty());
}
return builder.build();
}
protected static class ExtendedStateHolder<O, S extends IExtendedState<S>> extends AbstractStateHolder<O, S> implements IExtendedState<S>
{
private final ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties;
private S cleanState;
protected ExtendedStateHolder(O block, ImmutableMap<IProperty<?>, Comparable<?>> properties, ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties, S clean)
{
super(block, properties);
this.unlistedProperties = unlistedProperties;
this.cleanState = clean == null ? (S) this : clean;
}
@Override
@Nonnull
public <T extends Comparable<T>, V extends T> S with(@Nonnull IProperty<T> property, @Nonnull V value)
{
S clean = super.with(property, value);
if (clean == this.cleanState) {
return (S) this;
}
if (this == this.cleanState)
{ // no dynamic properties present, looking up in the normal table
return clean;
}
return (S) new ExtendedStateHolder(object, ((IStateHolder)clean).getValues(), unlistedProperties, this.cleanState);
}
@Override
public <V> S withProperty(IUnlistedProperty<V> property, @Nullable V value)
{
Optional<?> oldValue = unlistedProperties.get(property);
if (oldValue == null)
{
throw new IllegalArgumentException("Cannot set unlisted property " + property + " as it does not exist in " + this);
}
if (Objects.equals(oldValue.orElse(null), value))
{
return (S) this;
}
if (!property.isValid(value))
{
throw new IllegalArgumentException("Cannot set unlisted property " + property + " to " + value + " on object " + object + ", it is not an allowed value");
}
boolean clean = true;
ImmutableMap.Builder<IUnlistedProperty<?>, Optional<?>> builder = ImmutableMap.builder();
for (Map.Entry<IUnlistedProperty<?>, Optional<?>> entry : unlistedProperties.entrySet())
{
IUnlistedProperty<?> key = entry.getKey();
Optional<?> newValue = key.equals(property) ? Optional.ofNullable(value) : entry.getValue();
if (newValue.isPresent()) clean = false;
builder.put(key, newValue);
}
if (clean)
{ // no dynamic properties, lookup normal state
return (S) cleanState;
}
return (S) new ExtendedStateHolder(object, getValues(), builder.build(), this.cleanState);
}
@Override
public Collection<IUnlistedProperty<?>> getUnlistedNames()
{
return unlistedProperties.keySet();
}
@Override
@Nullable
public <V> V getValue(IUnlistedProperty<V> property)
{
Optional<?> value = unlistedProperties.get(property);
if (value == null)
{
throw new IllegalArgumentException("Cannot get unlisted property " + property + " as it does not exist in " + this);
}
return property.getType().cast(value.orElse(null));
}
public ImmutableMap<IUnlistedProperty<?>, Optional<?>> getUnlistedProperties()
{
return unlistedProperties;
}
@Override
public S getClean()
{
return cleanState;
}
}
}
@@ -1,27 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.property;
import net.minecraft.block.state.IBlockState;
public interface IExtendedBlockState extends IExtendedState<IBlockState>, IBlockState
{
}
@@ -1,41 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.property;
import java.util.Collection;
import net.minecraft.block.state.IBlockState;
import net.minecraft.state.IStateHolder;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
public interface IExtendedState<C> extends IStateHolder<C>
{
Collection<IUnlistedProperty<?>> getUnlistedNames();
<V>V getValue(IUnlistedProperty<V> property);
<V>C withProperty(IUnlistedProperty<V> property, V value);
ImmutableMap<IUnlistedProperty<?>, Optional<?>> getUnlistedProperties();
C getClean();
}
@@ -1,31 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.property;
public interface IUnlistedProperty<V>
{
String getName();
boolean isValid(V value);
Class<V> getType();
String valueToString(V value);
}
@@ -20,7 +20,7 @@
package net.minecraftforge.common.property;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.IProperty;
import net.minecraftforge.client.model.data.ModelProperty;
import net.minecraftforge.common.model.IModelState;
public class Properties
@@ -33,54 +33,5 @@ public class Properties
/**
* Property holding the IModelState used for animating the model in the TESR.
*/
public static final IUnlistedProperty<IModelState> AnimationProperty = new IUnlistedProperty<IModelState>()
{
@Override
public String getName() { return "forge_animation"; }
@Override
public boolean isValid(IModelState state) { return true; }
@Override
public Class<IModelState> getType() { return IModelState.class; }
@Override
public String valueToString(IModelState state) { return state.toString(); }
};
public static <V extends Comparable<V>> IUnlistedProperty<V> toUnlisted(IProperty<V> property)
{
return new PropertyAdapter<V>(property);
}
public static class PropertyAdapter<V extends Comparable<V>> implements IUnlistedProperty<V>
{
private final IProperty<V> parent;
public PropertyAdapter(IProperty<V> parent)
{
this.parent = parent;
}
@Override
public String getName()
{
return parent.getName();
}
@Override
public boolean isValid(V value)
{
return parent.getAllowedValues().contains(value);
}
@Override
public Class<V> getType()
{
return parent.getValueClass();
}
@Override
public String valueToString(V value)
{
return parent.getName(value);
}
}
public static final ModelProperty<IModelState> AnimationProperty = new ModelProperty<IModelState>();
}
@@ -1,70 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.property;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Range;
public class PropertyFloat implements IUnlistedProperty<Float>
{
private final String name;
private final Predicate<Float> validator;
public PropertyFloat(String name)
{
this(name, Predicates.alwaysTrue());
}
public PropertyFloat(String name, float min, float max)
{
this(name, Range.closed(min, max));
}
public PropertyFloat(String name, Predicate<Float> validator)
{
this.name = name;
this.validator = validator;
}
@Override
public String getName()
{
return name;
}
@Override
public boolean isValid(Float value)
{
return validator.apply(value);
}
@Override
public Class<Float> getType()
{
return Float.class;
}
@Override
public String valueToString(Float value)
{
return value.toString();
}
}