package vazkii.botania.client.model; import java.util.ArrayList; import java.util.EnumMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.vecmath.Matrix4f; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; 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.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.pipeline.IVertexConsumer; import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.client.model.pipeline.VertexTransformer; import net.minecraftforge.common.model.TRSRTransformation; import vazkii.botania.common.item.ItemManaGun; public class GunModel implements IBakedModel { private final IBakedModel originalModel; public GunModel(IBakedModel originalModel) { this.originalModel = Preconditions.checkNotNull(originalModel); } private final ItemOverrideList itemHandler = new ItemOverrideList(ImmutableList.of()) { @Nonnull @Override public IBakedModel handleItemState(@Nonnull IBakedModel model, ItemStack stack, World world, EntityLivingBase entity) { ItemStack lens = ItemManaGun.getLens(stack); if(!lens.isEmpty()) { IBakedModel lensModel = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getItemModel(lens); return GunModel.this.getModel(lensModel); } else return GunModel.this; } }; @Nonnull @Override public ItemOverrideList getOverrides() { return itemHandler; } @Nonnull @Override public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) { return originalModel.getQuads(state, side, rand); } @Override public boolean isAmbientOcclusion() { return originalModel.isAmbientOcclusion(); } @Override public boolean isGui3d() { return originalModel.isGui3d(); } @Override public boolean isBuiltInRenderer() { return originalModel.isBuiltInRenderer(); } @Nonnull @Override public TextureAtlasSprite getParticleTexture() { return originalModel.getParticleTexture(); } @Nonnull @Override public ItemCameraTransforms getItemCameraTransforms() { return originalModel.getItemCameraTransforms(); } private final IdentityHashMap<IBakedModel, CompositeBakedModel> cache = new IdentityHashMap<>(); private CompositeBakedModel getModel(IBakedModel lens) { CompositeBakedModel model = cache.get(lens); if(model == null) { model = new CompositeBakedModel(lens, originalModel); cache.put(lens, model); } return model; } 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(); } private static class CompositeBakedModel implements IPerspectiveAwareModel { private final IBakedModel gun; private final List<BakedQuad> genQuads; private final Map<EnumFacing, List<BakedQuad>> faceQuads = new EnumMap<>(EnumFacing.class); CompositeBakedModel(IBakedModel lens, IBakedModel gun) { this.gun = gun; ImmutableList.Builder<BakedQuad> genBuilder = ImmutableList.builder(); final TRSRTransformation transform = TRSRTransformation.blockCenterToCorner(new TRSRTransformation(new Vector3f(-0.4F, 0.25F, 0), null, new Vector3f(0.625F, 0.625F, 0.625F), TRSRTransformation.quatFromXYZ(0, (float) Math.PI / 2, 0))); for(EnumFacing e : EnumFacing.VALUES) faceQuads.put(e, new ArrayList<>()); // Add lens quads, scaled and translated for(BakedQuad quad : lens.getQuads(null, null, 0)) { genBuilder.add(transform(quad, transform)); } for(EnumFacing e : EnumFacing.VALUES) { faceQuads.get(e).addAll(lens.getQuads(null, e, 0).stream().map(input -> transform(input, transform)).collect(Collectors.toList())); } // Add gun quads genBuilder.addAll(gun.getQuads(null, null, 0)); for(EnumFacing e : EnumFacing.VALUES) { faceQuads.get(e).addAll(gun.getQuads(null, e, 0)); } genQuads = genBuilder.build(); } @Nonnull @Override public List<BakedQuad> getQuads(IBlockState state, EnumFacing face, long rand) { return face == null ? genQuads : faceQuads.get(face); } // Forward all to gun model @Override public boolean isAmbientOcclusion() { return gun.isAmbientOcclusion(); } @Override public boolean isGui3d() { return gun.isGui3d(); } @Override public boolean isBuiltInRenderer() { return gun.isBuiltInRenderer(); } @Nonnull @Override public TextureAtlasSprite getParticleTexture() { return gun.getParticleTexture();} @Nonnull @Override public ItemCameraTransforms getItemCameraTransforms() { return gun.getItemCameraTransforms(); } @Nonnull @Override public ItemOverrideList getOverrides() { return ItemOverrideList.NONE; } @Override public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType) { if(gun instanceof IPerspectiveAwareModel) { Pair<? extends IBakedModel, Matrix4f> pair = ((IPerspectiveAwareModel) gun).handlePerspective(cameraTransformType); if(pair != null && pair.getRight() != null) return Pair.of(this, pair.getRight()); } return Pair.of(this, TRSRTransformation.identity().getMatrix()); } } }