Hello, I'm trying to make a block that looks like a fluid but is solid. To that end, I've copied a bunch of code for making blocks with dynamic textures and stuck that in my mod. But when I give it a Fluid BlockState, it doesn't render anything. (I'm using just water for testing)
BoundFluidBlock:
package io.github.gingerindustries.cursedadditions.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
import net.minecraft.fluid.Fluids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockDisplayReader;
public class BoundFluidBlock extends Block {
public BoundFluidBlock() {
super(Block.Properties.of(Material.WATER));
}
public static BlockState getTargetFluid(IBlockDisplayReader world, BlockPos pos) {
return Fluids.WATER.getSource(true).createLegacyBlock();
}
}
BoundFluidBakedModel:
package io.github.gingerindustries.cursedadditions.client.blocks.model;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.logging.log4j.Logger;
import io.github.gingerindustries.cursedadditions.CursedAdditions;
import io.github.gingerindustries.cursedadditions.blocks.BoundFluidBlock;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockDisplayReader;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.data.ModelDataMap;
import net.minecraftforge.client.model.data.ModelProperty;
public class BoundFluidBakedModel implements IBakedModel {
private static final Logger LOGGER = CursedAdditions.LOGGER;
private static boolean loggedError = false; // no console spam
public BoundFluidBakedModel(IBakedModel unCamouflagedModel) {
defaultModel = unCamouflagedModel;
}
public static ModelProperty<BlockState> COPIED_BLOCK = new ModelProperty<>();
public static ModelDataMap getEmptyIModelData() {
ModelDataMap.Builder builder = new ModelDataMap.Builder();
builder.withProperty(COPIED_BLOCK);
ModelDataMap modelDataMap = builder.build();
return modelDataMap;
}
/**
* Forge's extension in place of IBakedModel::getQuads It allows us to pass in
* some extra information which we can use to choose the appropriate quads to
* render
*
* @param state
* @param side
* @param rand
* @param extraData
* @return
*/
@Override
@Nonnull
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand,
@Nonnull IModelData extraData) {
return getActualBakedModelFromIModelData(extraData).getQuads(state, side, rand, extraData);
}
@Override
@Nonnull
public IModelData getModelData(@Nonnull IBlockDisplayReader world, @Nonnull BlockPos pos, @Nonnull BlockState state,
@Nonnull IModelData tileData) {
BlockState fluid = BoundFluidBlock.getTargetFluid(world, pos);
ModelDataMap modelDataMap = getEmptyIModelData();
modelDataMap.setData(COPIED_BLOCK, fluid);
return modelDataMap;
}
@Override
public TextureAtlasSprite getParticleTexture(@Nonnull IModelData data) {
return getActualBakedModelFromIModelData(data).getParticleTexture(data);
}
private IBakedModel getActualBakedModelFromIModelData(@Nonnull IModelData data) {
IBakedModel retval = defaultModel; // default
if (!data.hasProperty(COPIED_BLOCK)) {
if (!loggedError) {
LOGGER.error("IModelData did not have expected property COPIED_BLOCK, falling back to default");
loggedError = true;
}
return retval;
}
BlockState copiedBlock = data.getData(COPIED_BLOCK);
Minecraft mc = Minecraft.getInstance();
BlockRendererDispatcher blockRendererDispatcher = mc.getBlockRenderer();
retval = blockRendererDispatcher.getBlockModel(copiedBlock);
return retval;
}
private IBakedModel defaultModel;
// ---- All these methods are required by the interface but we don't do anything
// special with them.
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
throw new AssertionError("IBakedModel::getQuads should never be called, only IForgeBakedModel::getQuads");
}
@Override
public boolean useAmbientOcclusion() {
// TODO Auto-generated method stub
return defaultModel.useAmbientOcclusion();
}
@Override
public boolean isGui3d() {
return defaultModel.isGui3d();
}
@Override
public boolean usesBlockLight() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCustomRenderer() {
// TODO Auto-generated method stub
return defaultModel.isCustomRenderer();
}
@Override
public TextureAtlasSprite getParticleIcon() {
// TODO Auto-generated method stub
return null;
}
@Override
public ItemOverrideList getOverrides() {
// TODO Auto-generated method stub
return null;
}
}
Registry code in BlockSetup:
public static void onModelBakeEvent(ModelBakeEvent event) {
CursedAdditions.LOGGER.debug("Setting up BoundFluid...");
for (BlockState blockState : BlockSetup.BOUND_FLUID.get().getStateDefinition().getPossibleStates()) {
ModelResourceLocation variantMRL = BlockModelShapes.stateToModelLocation(blockState);
IBakedModel existingModel = event.getModelRegistry().get(variantMRL);
if (existingModel == null) {
CursedAdditions.LOGGER
.warn("Did not find the expected vanilla baked model(s) for BoundFluid in registry");
} else if (existingModel instanceof BoundFluidBakedModel) {
CursedAdditions.LOGGER.warn("Tried to replace BoundFluidBakedModel twice");
} else {
BoundFluidBakedModel customModel = new BoundFluidBakedModel(existingModel);
event.getModelRegistry().put(variantMRL, customModel);
}
}
}