diff --git a/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java index 91473fa..90be547 100644 --- a/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java +++ b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java @@ -5,6 +5,7 @@ import java.util.Map; import mods.betterfoliage.BetterFoliage; import mods.betterfoliage.client.render.IRenderBlockDecorator; +import mods.betterfoliage.client.render.impl.EntityFXFallingLeaves; import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae; import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus; import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral; @@ -13,13 +14,17 @@ import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves; import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad; import mods.betterfoliage.client.render.impl.RenderBlockBetterReed; import mods.betterfoliage.client.resource.LeafGenerator; +import mods.betterfoliage.client.resource.LeafParticleTextures; import mods.betterfoliage.client.resource.LeafTextureEnumerator; import mods.betterfoliage.client.resource.ReedGenerator; import mods.betterfoliage.client.resource.ShortGrassGenerator; import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.ResourceLocation; import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import com.google.common.collect.Maps; @@ -31,6 +36,7 @@ public class BetterFoliageClient { public static Map decorators = Maps.newHashMap(); public static LeafGenerator leafGenerator; + public static LeafParticleTextures leafParticles; public static BlockMatcher leaves = new BlockMatcher(); public static BlockMatcher crops = new BlockMatcher(); @@ -66,6 +72,8 @@ public class BetterFoliageClient { BetterFoliage.log.info("Registering texture generators"); leafGenerator = new LeafGenerator(); MinecraftForge.EVENT_BUS.register(leafGenerator); + leafParticles = new LeafParticleTextures(0); + MinecraftForge.EVENT_BUS.register(leafParticles); MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator()); MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true)); @@ -93,6 +101,12 @@ public class BetterFoliageClient { return original; } + public static void onRandomDisplayTick(Block block, World world, int x, int y, int z) { + if (!leaves.matchesID(block)) return; + if (world.getBlock(x, y - 1, z).getMaterial() != Material.air) return; + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z)); + } + public static void registerRenderer(IRenderBlockDecorator decorator) { int renderId = RenderingRegistry.getNextAvailableRenderId(); decorators.put(renderId, decorator); diff --git a/src/main/java/mods/betterfoliage/client/render/impl/EntityFXFallingLeaves.java b/src/main/java/mods/betterfoliage/client/render/impl/EntityFXFallingLeaves.java new file mode 100644 index 0000000..4217dd1 --- /dev/null +++ b/src/main/java/mods/betterfoliage/client/render/impl/EntityFXFallingLeaves.java @@ -0,0 +1,72 @@ +package mods.betterfoliage.client.render.impl; + +import java.awt.Color; + +import mods.betterfoliage.client.BetterFoliageClient; +import net.minecraft.block.Block; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class EntityFXFallingLeaves extends EntityFX { + + public static float biomeBrightnessMultiplier = 0.5f; + + public EntityFXFallingLeaves(World world, int x, int y, int z) { + super(world, x + 0.5, y, z + 0.5); + motionY = -0.1d; + particleScale = 0.75f; + particleIcon = BetterFoliageClient.leafParticles.icon; + + Block block = world.getBlock(x, y, z); + IIcon blockIcon = block.getIcon(world, x, y, z, ForgeDirection.DOWN.ordinal()); + calculateParticleColor(BetterFoliageClient.leafParticles.getColor(blockIcon), block.colorMultiplier(world, x, y, z)); + + } + + @Override + public void onUpdate() { + super.onUpdate(); + motionY = -0.1d; + } + + @Override + public void renderParticle(Tessellator tessellator, float partialTickTime, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) { + super.renderParticle(tessellator, partialTickTime, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); + } + + /** Calculates and sets the color of the particle by blending the average color of the block texture with the current biome color + * Blending is done in HSB color space, weighted by the relative saturation of the colors + * @param textureAvgColor average color of the block texture + * @param blockColor biome color at the spawning block + */ + public void calculateParticleColor(int textureAvgColor, int blockColor) { + float[] hsbTexture = Color.RGBtoHSB((textureAvgColor >> 16) & 0xFF, (textureAvgColor >> 8) & 0xFF, textureAvgColor & 0xFF, null); + float[] hsbBlock = Color.RGBtoHSB((blockColor >> 16) & 0xFF, (blockColor >> 8) & 0xFF, blockColor & 0xFF, null); + + float weightTex = hsbTexture[1] / (hsbTexture[1] + hsbBlock[1]); + float weightBlock = 1.0f - weightTex; + + // avoid circular average for hue for performance reasons + // one of the color components should dominate anyway + float h = weightTex * hsbTexture[0] + weightBlock * hsbBlock[0]; + float s = weightTex * hsbTexture[1] + weightBlock * hsbBlock[1]; + float b = weightTex * hsbTexture[2] + weightBlock * hsbBlock[2] * biomeBrightnessMultiplier; + int particleColor = Color.HSBtoRGB(h, s, b); + + particleBlue = (particleColor & 0xFF) / 256.0f; + particleGreen = ((particleColor >> 8) & 0xFF) / 256.0f; + particleRed = ((particleColor >> 16) & 0xFF) / 256.0f; + } + + @Override + public int getFXLayer() { + return 1; + } + +} diff --git a/src/main/java/mods/betterfoliage/client/resource/LeafParticleTextures.java b/src/main/java/mods/betterfoliage/client/resource/LeafParticleTextures.java new file mode 100644 index 0000000..a4c02bd --- /dev/null +++ b/src/main/java/mods/betterfoliage/client/resource/LeafParticleTextures.java @@ -0,0 +1,92 @@ +package mods.betterfoliage.client.resource; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Map; + +import javax.imageio.ImageIO; + +import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.TextureStitchEvent; + +import com.google.common.collect.Maps; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; + +/** Holds the texture for the falling leaf particles, and stores average texture color values for leaf textures + * @author octarine-noise + * + */ +public class LeafParticleTextures { + + /** Icon for leaf particles */ + public IIcon icon; + + /** Map of average color values */ + public Map colors = Maps.newHashMap(); + + /** Default color value */ + public int defaultColor = 0x208040; + + public LeafParticleTextures(int defaultColor) { + this.defaultColor = defaultColor; + } + + public int getColor(IIcon icon) { + Integer result = colors.get(icon); + return result == null ? defaultColor : result; + } + + /** Calculate average color value (in HSB color space) for a texture and store it in the map. + * @param icon texture + */ + protected void addAtlasTexture(TextureAtlasSprite icon) { + ResourceLocation locationNoDirs = new ResourceLocation(icon.getIconName()); + ResourceLocation locationWithDirs = new ResourceLocation(locationNoDirs.getResourceDomain(), String.format("textures/blocks/%s.png", locationNoDirs.getResourcePath())); + try { + BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(locationWithDirs).getInputStream()); + + int numOpaque = 0; + float sumHueX = 0.0f; + float sumHueY = 0.0f; + float sumSaturation = 0.0f; + float sumBrightness = 0.0f; + for (int x = 0; x < image.getWidth(); x++) { + for (int y = 0; y < image.getHeight(); y++) { + int pixel = image.getRGB(x, y); + int alpha = (pixel >> 24) & 0xFF; + float[] hsbVals = Color.RGBtoHSB((pixel >> 16) & 0xFF, (pixel >> 8) & 0xFF, pixel & 0xFF, null); + if (alpha == 255) { + numOpaque++; + sumHueX += Math.cos((hsbVals[0] - 0.5) * 2.0 * Math.PI); + sumHueY += Math.sin((hsbVals[0] - 0.5) * 2.0 * Math.PI); + sumSaturation += hsbVals[1]; + sumBrightness += hsbVals[2]; + } + } + } + + // average hue as usual for circular values - transform average unit vector back to polar angle + float avgHue = (float) (Math.atan2(sumHueY, sumHueX) / (2.0 * Math.PI) + 0.5); + colors.put(icon, Color.HSBtoRGB(avgHue, sumSaturation / numOpaque, sumBrightness / numOpaque)); + } catch (IOException e) { + } + } + + @SubscribeEvent + public void handleTextureReload(TextureStitchEvent.Pre event) { + if (event.map.getTextureType() != 0) return; + colors.clear(); + icon = event.map.registerIcon("betterfoliage:falling_leaf"); + } + + @SubscribeEvent + public void handleRegisterTexture(LeafTextureFoundEvent event) { + addAtlasTexture(event.icon); + } +} diff --git a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java index 7c91a57..8a24274 100644 --- a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java +++ b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java @@ -17,7 +17,8 @@ public class BetterFoliageTransformer implements IClassTransformer { protected Iterable transformers = ImmutableList.of( new TransformRenderBlockOverride(), - new TransformShaderModBlockOverride() + new TransformShaderModBlockOverride(), + new TransformRandomDisplayTick() ); protected Logger logger = LogManager.getLogger(getClass().getSimpleName()); diff --git a/src/main/java/mods/betterfoliage/loader/DeobfHelper.java b/src/main/java/mods/betterfoliage/loader/DeobfHelper.java index 3a855ab..e924a45 100644 --- a/src/main/java/mods/betterfoliage/loader/DeobfHelper.java +++ b/src/main/java/mods/betterfoliage/loader/DeobfHelper.java @@ -20,18 +20,24 @@ public class DeobfHelper { obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "ble"); obfClasses.put("net/minecraft/world/IBlockAccess", "afx"); obfClasses.put("net/minecraft/block/Block", "ahu"); + obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "biz"); + obfClasses.put("net/minecraft/world/World", "afn"); obfElements.put("blockAccess", "a"); obfElements.put("renderBlockByRenderType", "b"); obfElements.put("mapRegisteredSprites", "bpr"); + obfElements.put("doVoidFogParticles", "C"); } else if ("1.7.10".equals(mcVersion)) { obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "blm"); obfClasses.put("net/minecraft/world/IBlockAccess", "ahl"); obfClasses.put("net/minecraft/block/Block", "aji"); + obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "bjf"); + obfClasses.put("net/minecraft/world/World", "ahb"); obfElements.put("blockAccess", "a"); obfElements.put("renderBlockByRenderType", "b"); obfElements.put("mapRegisteredSprites", "bpr"); + obfElements.put("doVoidFogParticles", "C"); } } diff --git a/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java b/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java index 61fa8d3..5b73942 100644 --- a/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java +++ b/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java @@ -69,4 +69,10 @@ public abstract class MethodTransformerBase { for (AbstractInsnNode inst : added) listAdd.add(inst); insnList.insert(node, listAdd); } + + protected void insertBefore(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) { + InsnList listAdd = new InsnList(); + for (AbstractInsnNode inst : added) listAdd.add(inst); + insnList.insertBefore(node, listAdd); + } } diff --git a/src/main/java/mods/betterfoliage/loader/TransformRandomDisplayTick.java b/src/main/java/mods/betterfoliage/loader/TransformRandomDisplayTick.java new file mode 100644 index 0000000..1af9051 --- /dev/null +++ b/src/main/java/mods/betterfoliage/loader/TransformRandomDisplayTick.java @@ -0,0 +1,43 @@ +package mods.betterfoliage.loader; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class TransformRandomDisplayTick extends MethodTransformerBase { + + @Override + public String getClassName() { + return "net.minecraft.client.multiplayer.WorldClient"; + } + + @Override + public String getMethodName() { + return "doVoidFogParticles"; + } + + @Override + public String getSignature() { + return "(III)V"; + } + + @Override + public String getLogMessage() { + return "Applying random display tick call hook"; + } + + @Override + public void transform(MethodNode method, boolean obf) { + AbstractInsnNode endLoop = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IINC)); + insertBefore(method.instructions, endLoop, + new VarInsnNode(Opcodes.ALOAD, 10), + new VarInsnNode(Opcodes.ALOAD, 0), + new VarInsnNode(Opcodes.ILOAD, 7), + new VarInsnNode(Opcodes.ILOAD, 8), + new VarInsnNode(Opcodes.ILOAD, 9), + new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "onRandomDisplayTick", signature("(Lnet/minecraft/block/Block;Lnet/minecraft/world/World;III)V", obf)) + ); + } +} diff --git a/src/main/resources/assets/betterfoliage/textures/blocks/falling_leaf.png b/src/main/resources/assets/betterfoliage/textures/blocks/falling_leaf.png new file mode 100644 index 0000000..94df28f Binary files /dev/null and b/src/main/resources/assets/betterfoliage/textures/blocks/falling_leaf.png differ