/**
* This class was created by <williewillus>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
* <p/>
* Botania is Open Source and distributed under the
* Botania License: http://botaniamod.net/license.php
*/
package vazkii.botania.client.model;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelManager;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.client.model.IPerspectiveAwareModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.client.model.pipeline.VertexTransformer;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.property.IExtendedBlockState;
import vazkii.botania.api.BotaniaAPIClient;
import vazkii.botania.api.item.IFloatingFlower;
import vazkii.botania.api.state.BotaniaStateProps;
import vazkii.botania.common.Botania;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.item.block.ItemBlockSpecialFlower;
public class FloatingFlowerModel implements IBakedModel {
private static final String MUNDANE_PREFIX = "botania:shimmeringFlower_";
private final Table<IFloatingFlower.IslandType, String, CompositeBakedModel> CACHE = HashBasedTable.create();
protected static BakedQuad transform(BakedQuad quad, final TRSRTransformation transform) {
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(DefaultVertexFormats.ITEM);
final IVertexConsumer consumer = new VertexTransformer(builder) {
@Override
public void put(int element, float... data) {
VertexFormatElement formatElement = DefaultVertexFormats.ITEM.getElement(element);
switch(formatElement.getUsage()) {
case POSITION: {
float[] newData = new float[4];
Vector4f vec = new Vector4f(data);
transform.getMatrix().transform(vec);
vec.get(newData);
parent.put(element, newData);
break;
}
default: {
parent.put(element, data);
break;
}
}
}
};
quad.pipe(consumer);
return builder.build();
}
@Nonnull
@Override
public List<BakedQuad> getQuads(IBlockState state, EnumFacing face, long rand) {
if(state.getBlock() != ModBlocks.floatingSpecialFlower && state.getBlock() != ModBlocks.floatingFlower)
return Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelManager().getMissingModel().getQuads(state, face, rand);
IExtendedBlockState realState = (IExtendedBlockState) state;
IFloatingFlower.IslandType islandType = realState.getValue(BotaniaStateProps.ISLAND_TYPE);
String identifier;
if(state.getBlock() == ModBlocks.floatingSpecialFlower) {
// Magic flower
identifier = realState.getValue(BotaniaStateProps.SUBTILE_ID);
} else {
// Mundane flower
identifier = MUNDANE_PREFIX + state.getValue(BotaniaStateProps.COLOR).getMetadata();
}
return getModel(islandType, identifier).getQuads(state, face, rand);
}
// Get the model for this islandtype + flower type combination. If it's not cached already, generate it.
private CompositeBakedModel getModel(IFloatingFlower.IslandType islandType, String identifier) {
ModelManager modelManager = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getModelManager();
if(CACHE.contains(islandType, identifier)) {
return CACHE.get(islandType, identifier);
} else {
IBakedModel islandModel;
try {
islandModel = ModelLoaderRegistry.getModel(BotaniaAPIClient.getRegisteredIslandTypeModels().get(islandType)).bake(TRSRTransformation.identity(), DefaultVertexFormats.ITEM, ModelLoader.defaultTextureGetter());
} catch (Exception e) {
islandModel = modelManager.getMissingModel();
}
IBakedModel flowerModel;
if(identifier.startsWith(MUNDANE_PREFIX)) {
int meta = Integer.parseInt(identifier.substring(identifier.indexOf(MUNDANE_PREFIX) + MUNDANE_PREFIX.length()));
flowerModel = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getItemModel(new ItemStack(ModBlocks.shinyFlower, 1, meta));
} else {
ItemStack stack = ItemBlockSpecialFlower.ofType(identifier);
IBakedModel specialFlowerModel = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getItemModel(stack);
flowerModel = specialFlowerModel.getOverrides().handleItemState(specialFlowerModel, stack, null, null);
}
// Enhance!
CompositeBakedModel model = new CompositeBakedModel(flowerModel, islandModel);
Botania.LOGGER.debug("Cached floating flower model for islandtype {} and flowertype {}", islandType, identifier);
CACHE.put(islandType, identifier, model);
return model;
}
}
@Nonnull
@Override
public ItemOverrideList getOverrides() {
return itemHandler;
}
@Override public boolean isAmbientOcclusion() { return false; }
@Override public boolean isGui3d() { return true; }
@Override public boolean isBuiltInRenderer() { return false; }
@Nonnull @Override public TextureAtlasSprite getParticleTexture() { return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("minecraft:blocks/dirt"); }
@Nonnull @Override public ItemCameraTransforms getItemCameraTransforms() { return ItemCameraTransforms.DEFAULT; }
private static class CompositeBakedModel implements IPerspectiveAwareModel {
private final IBakedModel flower;
private final IBakedModel island;
private final List<BakedQuad> genQuads;
private final Map<EnumFacing, List<BakedQuad>> faceQuads = new EnumMap<>(EnumFacing.class);
public CompositeBakedModel(IBakedModel flower, IBakedModel island) {
this.flower = flower;
this.island = island;
ImmutableList.Builder<BakedQuad> genBuilder = ImmutableList.builder();
final TRSRTransformation transform = TRSRTransformation.blockCenterToCorner(new TRSRTransformation(new Vector3f(0F, 0.2F, 0F), null, new Vector3f(0.5F, 0.5F, 0.5F), null));
for(EnumFacing e : EnumFacing.VALUES)
faceQuads.put(e, new ArrayList<>());
// Add flower quads, scaled and translated
flower.getQuads(null, null, 0).stream().map(q -> transform(q, transform)).forEach(genBuilder::add);
for(EnumFacing e : EnumFacing.VALUES) {
List<BakedQuad> faceQ = faceQuads.get(e);
flower.getQuads(null, e, 0).stream().map(input -> transform(input, transform)).forEach(faceQ::add);
}
// Add island quads
genBuilder.addAll(island.getQuads(null, null, 0));
for(EnumFacing e : EnumFacing.VALUES) {
faceQuads.get(e).addAll(island.getQuads(null, e, 0));
}
genQuads = genBuilder.build();
}
// Forward all to flower model
@Nonnull @Override public List<BakedQuad> getQuads(IBlockState state, EnumFacing face, long rand) {
return face == null ? genQuads : faceQuads.get(face);
}
@Override public boolean isAmbientOcclusion() {
return flower.isAmbientOcclusion();
}
@Override public boolean isGui3d() {
return flower.isGui3d();
}
@Override public boolean isBuiltInRenderer() {
return flower.isBuiltInRenderer();
}
@Nonnull @Override public TextureAtlasSprite getParticleTexture() {
return flower.getParticleTexture();
}
@Nonnull @Override public ItemCameraTransforms getItemCameraTransforms() {
return flower.getItemCameraTransforms();
}
@Nonnull @Override public ItemOverrideList getOverrides() { return ItemOverrideList.NONE; }
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType) {
if(island instanceof IPerspectiveAwareModel) {
Pair<? extends IBakedModel, Matrix4f> pair = ((IPerspectiveAwareModel) island).handlePerspective(cameraTransformType);
if(pair != null && pair.getRight() != null)
return Pair.of(this, pair.getRight());
}
return Pair.of(this, TRSRTransformation.identity().getMatrix());
}
}
private final ItemOverrideList itemHandler = new ItemOverrideList(ImmutableList.of()) {
@Nonnull
@Override
public IBakedModel handleItemState(@Nonnull IBakedModel model, ItemStack stack, World world, EntityLivingBase entity) {
// Items always have GRASS island
IFloatingFlower.IslandType islandType = IFloatingFlower.IslandType.GRASS;
String identifier;
if(Block.getBlockFromItem(stack.getItem()) == ModBlocks.floatingSpecialFlower) {
// Magic flower
identifier = ItemBlockSpecialFlower.getType(stack);
} else {
// Mundane flower
identifier = MUNDANE_PREFIX + stack.getItemDamage();
}
return FloatingFlowerModel.this.getModel(islandType, identifier);
}
};
}