Skip to content

Commit

Permalink
Implement cauldron model
Browse files Browse the repository at this point in the history
  • Loading branch information
KnightMiner committed Aug 30, 2020
1 parent 0a81102 commit 41845d3
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 853 deletions.
@@ -0,0 +1,46 @@
package knightminer.inspirations.common.network;

import knightminer.inspirations.library.recipe.cauldron.CauldronContentTypes;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronContents;
import knightminer.inspirations.recipes.tileentity.CauldronTileEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.network.NetworkEvent.Context;
import slimeknights.mantle.network.packet.IThreadsafePacket;
import slimeknights.mantle.util.TileEntityHelper;

public class CauldronContentUpatePacket implements IThreadsafePacket {
private final BlockPos pos;
private final ICauldronContents contents;

public CauldronContentUpatePacket(BlockPos pos, ICauldronContents contents) {
this.pos = pos;
this.contents = contents;
}

public CauldronContentUpatePacket(PacketBuffer buffer) {
this.pos = buffer.readBlockPos();
this.contents = CauldronContentTypes.read(buffer);
}

@Override
public void encode(PacketBuffer buffer) {
buffer.writeBlockPos(pos);
CauldronContentTypes.write(contents, buffer);
}

@Override
public void handleThreadsafe(Context context) {
HandleClient.handle(this);
}

/** Once removed client class */
private static class HandleClient {
private static void handle(CauldronContentUpatePacket packet) {
TileEntityHelper.getTile(CauldronTileEntity.class, Minecraft.getInstance().world, packet.pos, true).ifPresent(te -> {
te.setContents(packet.contents);
});
}
}
}
Expand Up @@ -23,12 +23,9 @@ private InspirationsNetwork() {
* Called during mod construction to register all packets
*/
public void setup() {
// register all the packets

// bookshelf
registerPacket(InventorySlotSyncPacket.class, InventorySlotSyncPacket::new, NetworkDirection.PLAY_TO_CLIENT);
// milk cooldown
registerPacket(MilkablePacket.class, MilkablePacket::new, NetworkDirection.PLAY_TO_CLIENT);
registerPacket(CauldronContentUpatePacket.class, CauldronContentUpatePacket::new, NetworkDirection.PLAY_TO_CLIENT);
}

/**
Expand Down
Expand Up @@ -24,7 +24,6 @@
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* Resource pack that overrides resources based on config
Expand Down Expand Up @@ -65,7 +64,7 @@ public ConfigurableResourcePack(Class<?> resourceLoader, ResourceLocation packId
private ConfigurableResourcePack(Class<?> resourceLoader, String packId, String pathPrefix, String displayName, Set<String> namespaces) {
super(new File(pathPrefix));
this.resourceLoader = resourceLoader;
this.packId = packId.toString();
this.packId = packId;
this.displayName = displayName;
this.pathPrefix = pathPrefix;
this.namespaces = namespaces;
Expand Down Expand Up @@ -138,7 +137,7 @@ public <T extends ResourcePackInfo> void func_230230_a_(Consumer<T> consumer, IF
* @param originalPath Original resource path
* @param resource Path to the replacement resource relative to the pack root
*/
protected void addReplacement(BooleanSupplier condition, String originalPath, Supplier<String> resource) {
public void addReplacement(BooleanSupplier condition, String originalPath, String resource) {
if (replacements.containsKey(originalPath)) {
throw new IllegalArgumentException("Duplicate replacement '" + originalPath + "' for configurable pack " + packId);
}
Expand All @@ -162,7 +161,7 @@ private static String makePath(ResourceLocation id, String folder, String extens
* @param resource Name of blockstate replacement
*/
public void addBlockstateReplacement(BooleanSupplier condition, Block block, String resource) {
addReplacement(condition, makePath(Objects.requireNonNull(block.getRegistryName()), "blockstates", "json"), () -> "blockstates/" + resource + ".json");
addReplacement(condition, makePath(Objects.requireNonNull(block.getRegistryName()), "blockstates", "json"), "blockstates/" + resource + ".json");
}

/**
Expand All @@ -171,39 +170,29 @@ public void addBlockstateReplacement(BooleanSupplier condition, Block block, Str
* @param item Item to replace the model
* @param resource New name supplier
*/
public void addItemModelReplacement(BooleanSupplier condition, IItemProvider item, Supplier<String> resource) {
addReplacement(condition, makePath(Objects.requireNonNull(item.asItem().getRegistryName()), "models/item", "json"), () -> "item_models/" + resource.get() + ".json");
}

/**
* Adds a replacement for a blockstate JSON
* @param condition Condition for replacement
* @param item Item to replace the model
* @param resource New name supplier
*/
public void addItemModelReplacement(BooleanSupplier condition, IItemProvider item, String resource) {
addItemModelReplacement(condition, item, () -> resource);
addReplacement(condition, makePath(Objects.requireNonNull(item.asItem().getRegistryName()), "models/item", "json"), "item_models/" + resource + ".json");
}

/**
* Data class holding a single replacement pair
*/
private static class Replacement {
private final BooleanSupplier condition;
private final Supplier<String> name;
private final String name;

/**
* Creates a new replacement
* @param condition Condition for the replacement
* @param name New file name, relative to pack root
*/
public Replacement(BooleanSupplier condition, Supplier<String> name) {
public Replacement(BooleanSupplier condition, String name) {
this.name = name;
this.condition = condition;
}

public String getName() {
return name.get();
return name;
}

/**
Expand Down
@@ -0,0 +1,146 @@
package knightminer.inspirations.library.client.model;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair;
import knightminer.inspirations.recipes.tileentity.CauldronTileEntity;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.BlockPart;
import net.minecraft.client.renderer.model.BlockPartFace;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IModelTransform;
import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.model.RenderMaterial;
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.IModelConfiguration;
import net.minecraftforge.client.model.IModelLoader;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import slimeknights.mantle.client.model.RetexturedModel;
import slimeknights.mantle.client.model.RetexturedModel.RetexturedConfiguration;
import slimeknights.mantle.client.model.util.DynamicBakedWrapper;
import slimeknights.mantle.client.model.util.SimpleBlockModel;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;

/**
* Model to replace cauldron water texture with the relevant fluid texture
*/
public class CauldronModel implements IModelGeometry<CauldronModel> {
public static final Loader LOADER = new Loader();

private final SimpleBlockModel model;
private final Set<String> retextured;

/**
* Creates a new model instance
* @param model Model instance
* @param retextured Names of fluid textures to retexture
*/
protected CauldronModel(SimpleBlockModel model, Set<String> retextured) {
this.model = model;
this.retextured = retextured;
}

@Override
public Collection<RenderMaterial> getTextures(IModelConfiguration owner, Function<ResourceLocation,IUnbakedModel> modelGetter, Set<Pair<String,String>> missingTextureErrors) {
return model.getTextures(owner, modelGetter, missingTextureErrors);
}

@Override
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial,TextureAtlasSprite> spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation) {
List<BlockPart> elements = new ArrayList<>();
for (BlockPart part : model.getElements()) {
boolean updated = false;
Map<Direction, BlockPartFace> newFaces = new EnumMap<>(Direction.class);
for (Entry<Direction,BlockPartFace> entry : part.mapFaces.entrySet()) {
BlockPartFace face = entry.getValue();
if (face.tintIndex != 1 && retextured.contains(face.texture.substring(1))) {
updated = true;
newFaces.put(entry.getKey(), new BlockPartFace(face.cullFace, 1, face.texture, face.blockFaceUV));
} else {
newFaces.put(entry.getKey(), face);
}
}
if (updated) {
elements.add(new BlockPart(part.positionFrom, part.positionTo, newFaces, part.partRotation, part.shade));
} else {
elements.add(part);
}
}

IBakedModel baked = SimpleBlockModel.bakeModel(owner, elements, modelTransform, overrides, spriteGetter, modelLocation);
return new BakedModel(baked, owner, elements, modelTransform, RetexturedModel.getAllRetextured(owner, model, retextured));
}

/** Baked model, to swap out textures dynamically */
private static class BakedModel extends DynamicBakedWrapper<IBakedModel> {
private final Map<ResourceLocation,IBakedModel> fluidCache = new HashMap<>();
// data needed to rebake
private final IModelConfiguration owner;
private final List<BlockPart> elements;
private final IModelTransform transform;
private final Set<String> retextured;
protected BakedModel(IBakedModel originalModel, IModelConfiguration owner, List<BlockPart> elements, IModelTransform transform, Set<String> fluidNames) {
super(originalModel);
this.owner = owner;
this.elements = elements;
this.transform = transform;
this.retextured = fluidNames;

// for each part face using the fluid texture, set the tint index to 1. Saves having to recreate models
// the vanilla model does this using tint index 0, but that is problematic as that also tints the particle texture
// plus, ensures resource pack support if a resource pack does weird cauldron stuff

}

/**
* Bakes the baked model for the given fluid
* @param fluid Fluid texture
* @return Baked model
*/
private IBakedModel getFluidModel(ResourceLocation fluid) {
return SimpleBlockModel.bakeDynamic(new RetexturedConfiguration(owner, retextured, fluid), elements, transform);
}

@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, Random random, IModelData data) {
ResourceLocation texture = data.getData(CauldronTileEntity.TEXTURE);
if (texture == null) {
return originalModel.getQuads(state, direction, random, data);
}
return fluidCache.computeIfAbsent(texture, this::getFluidModel).getQuads(state, direction, random, data);
}
}

/** Loader class */
private static class Loader implements IModelLoader<CauldronModel> {
private Loader() {}

@Override
public void onResourceManagerReload(IResourceManager resourceManager) {}

@Override
public CauldronModel read(JsonDeserializationContext context, JsonObject json) {
SimpleBlockModel model = SimpleBlockModel.deserialize(context, json);
Set<String> retextured = RetexturedModel.Loader.getRetextured(json);
return new CauldronModel(model, retextured);
}
}
}
Expand Up @@ -2,16 +2,29 @@

import knightminer.inspirations.Inspirations;
import knightminer.inspirations.common.ClientEvents;
import knightminer.inspirations.common.Config;
import knightminer.inspirations.library.client.model.CauldronModel;
import knightminer.inspirations.library.recipe.cauldron.CauldronContentTypes;
import knightminer.inspirations.library.recipe.cauldron.contents.ICauldronContents;
import knightminer.inspirations.recipes.client.BoilingParticle;
import knightminer.inspirations.recipes.item.MixedDyedBottleItem;
import knightminer.inspirations.recipes.recipe.cauldron.contents.ColorContentType;
import knightminer.inspirations.recipes.recipe.cauldron.contents.PotionContentType;
import knightminer.inspirations.recipes.tileentity.CauldronTileEntity;
import knightminer.inspirations.shared.SharedClientEvents;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.client.renderer.color.ItemColors;
import net.minecraft.fluid.Fluids;
import net.minecraft.inventory.container.PlayerContainer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.biome.BiomeColors;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
Expand All @@ -20,29 +33,36 @@
@SuppressWarnings("unused")
@EventBusSubscriber(modid = Inspirations.modID, value = Dist.CLIENT, bus = Bus.MOD)
public class RecipesClientEvents extends ClientEvents {
/* TODO: reimplement
@SubscribeEvent
static void registerModels(ModelRegistryEvent event) {
setModelStateMapper(InspirationsRecipes.cauldron, new CauldronStateMapper(CAULDRON_MODEL));
static void registerModelLoaders(ModelRegistryEvent event) {
ModelLoaderRegistry.registerLoader(Inspirations.getResource("cauldron"), CauldronModel.LOADER);
SharedClientEvents.configPack.addBlockstateReplacement(Config.extendedCauldron, Blocks.CAULDRON, "cauldron");
}

@SubscribeEvent
static void registerBlockColors(ColorHandlerEvent.Block event) {
BlockColors blockColors = event.getBlockColors();

// coloring of liquid inside, either for potions or dyes
// coloring of liquid inside, for fluids, potions, and dyes
registerBlockColors(blockColors, (state, world, pos, tintIndex) -> {
if(tintIndex == 1) {
// skip tint index 0, that is particles
if (tintIndex > 0 && world != null && pos != null) {
// must be cauldron TE
TileEntity te = world.getTileEntity(pos);
if(te instanceof CauldronTileEntity) {
return ((CauldronTileEntity) te).getColor();
// if it contains water, run vanilla tinting
ICauldronContents contents = ((CauldronTileEntity) te).getContents();
if (!contents.matches(CauldronContentTypes.FLUID, Fluids.WATER)) {
return contents.getTintColor();
}
}
// water tinting if contains water or TE is missing
return BiomeColors.getWaterColor(world, pos);
}

return -1;
}, InspirationsRecipes.cauldron);
}, InspirationsRecipes.cauldron, InspirationsRecipes.boilingCauldron);
}
*/

@SubscribeEvent
static void clientSetup(FMLClientSetupEvent event) {
Expand Down

0 comments on commit 41845d3

Please sign in to comment.