diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..b26d69f
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c71b104
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.gradle/
+.settings/
+bin/
+build/
+libs/
diff --git a/.project b/.project
new file mode 100644
index 0000000..613bd57
--- /dev/null
+++ b/.project
@@ -0,0 +1,18 @@
+
+
+ BetterFoliage
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.springsource.ide.eclipse.gradle.core.nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..568eaa3
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,45 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ maven {
+ name = "forge"
+ url = "http://files.minecraftforge.net/maven"
+ }
+ maven {
+ name = "sonatype"
+ url = "https://oss.sonatype.org/content/repositories/snapshots/"
+ }
+ }
+ dependencies {
+ classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
+ }
+}
+apply plugin: 'forge'
+
+minecraft {
+ version = '1.7.2-10.12.1.1098'
+}
+
+jar.baseName = 'BetterFoliage-1.7.2'
+group = 'com.github.octarine-noise'
+version='0.9b'
+
+processResources {
+ inputs.property "version", project.version
+ inputs.property "mcversion", project.minecraft.version
+
+ from(sourceSets.main.resources.srcDirs) {
+ include 'mcmod.info'
+ expand 'version':project.version, 'mcversion':project.minecraft.version
+ }
+
+ from(sourceSets.main.resources.srcDirs) {
+ exclude 'mcmod.info'
+ }
+}
+
+jar {
+ manifest {
+ attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/mods/betterfoliage/BetterFoliage.java b/src/main/java/mods/betterfoliage/BetterFoliage.java
new file mode 100644
index 0000000..8f0bc9a
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/BetterFoliage.java
@@ -0,0 +1,51 @@
+package mods.betterfoliage;
+
+import java.io.File;
+import java.util.Map;
+
+import mods.betterfoliage.client.BetterFoliageClient;
+import mods.betterfoliage.common.config.Config;
+
+import org.apache.logging.log4j.Logger;
+
+import cpw.mods.fml.common.Mod;
+import cpw.mods.fml.common.event.FMLPostInitializationEvent;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import cpw.mods.fml.common.network.NetworkCheckHandler;
+import cpw.mods.fml.relauncher.Side;
+
+@Mod(name=BetterFoliage.MOD_NAME, modid=BetterFoliage.MOD_ID, acceptedMinecraftVersions="[1.7.2]", guiFactory="mods.betterfoliage.client.gui.ConfigGuiFactory")
+public class BetterFoliage {
+
+ public static final String MOD_ID = "BetterFoliage";
+ public static final String MOD_NAME = "Better Foliage";
+
+ @Mod.Instance
+ public static BetterFoliage instance;
+
+ public static Logger log;
+
+ public static File configDir;
+
+ @Mod.EventHandler
+ public void preInit(FMLPreInitializationEvent event) {
+ log = event.getModLog();
+ if (event.getSide() == Side.CLIENT) {
+ configDir = new File(event.getModConfigurationDirectory(), "betterfoliage");
+ configDir.mkdir();
+ Config.load();
+ BetterFoliageClient.preInit();
+ }
+ }
+
+ @Mod.EventHandler
+ public void postInit(FMLPostInitializationEvent event) {
+ if (event.getSide() == Side.CLIENT) {
+ }
+ }
+
+ @NetworkCheckHandler
+ public boolean checkVersion(Map mods, Side side) {
+ return true;
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java b/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java
new file mode 100644
index 0000000..cb9257b
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java
@@ -0,0 +1,24 @@
+package mods.betterfoliage;
+
+import net.minecraft.block.Block;
+
+/** Allows overriding block rendertype.
+ * @author octarine-noise
+ */
+public class BlockRenderTypeOverride {
+
+ public static IRenderTypeProvider provider = null;
+
+ public static interface IRenderTypeProvider {
+ public int getRenderType(Block block);
+ }
+
+ /** Entry point from transformed RenderBlocks class. If no provider is given,
+ * replicates default behaviour
+ * @param block block instance
+ * @return block render type
+ */
+ public static int getRenderType(Block block) {
+ return provider == null ? block.getRenderType() : provider.getRenderType(block);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
new file mode 100644
index 0000000..e481fbf
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
@@ -0,0 +1,76 @@
+package mods.betterfoliage.client;
+
+import java.io.File;
+import java.util.Set;
+
+import mods.betterfoliage.BetterFoliage;
+import mods.betterfoliage.BlockRenderTypeOverride;
+import mods.betterfoliage.BlockRenderTypeOverride.IRenderTypeProvider;
+import mods.betterfoliage.client.render.RenderBlockBetterGrass;
+import mods.betterfoliage.client.render.RenderBlockBetterLeaves;
+import mods.betterfoliage.client.resource.ILeafTextureRecognizer;
+import mods.betterfoliage.client.resource.LeafTextureGenerator;
+import mods.betterfoliage.common.config.Config;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockGrass;
+import net.minecraft.block.BlockLeavesBase;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraftforge.common.MinecraftForge;
+
+import com.google.common.collect.Sets;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class BetterFoliageClient implements IRenderTypeProvider, ILeafTextureRecognizer {
+
+ public static int leavesRenderId;
+ public static int grassRenderId;
+ public static LeafTextureGenerator leafGenerator;
+ public static Set> blockLeavesClasses = Sets.newHashSet();
+
+ public static void preInit() {
+ FMLCommonHandler.instance().bus().register(new KeyHandler());
+
+ BetterFoliage.log.info("Registering renderers");
+ leavesRenderId = RenderBlockBetterLeaves.register();
+ grassRenderId = RenderBlockBetterGrass.register();
+ BlockRenderTypeOverride.provider = new BetterFoliageClient();
+
+ blockLeavesClasses.add(BlockLeavesBase.class);
+ addLeafBlockClass("forestry.arboriculture.gadgets.BlockLeaves");
+ addLeafBlockClass("thaumcraft.common.blocks.BlockMagicalLeaves");
+
+ BetterFoliage.log.info("Registering leaf texture generator");
+ leafGenerator = new LeafTextureGenerator();
+ MinecraftForge.EVENT_BUS.register(leafGenerator);
+ leafGenerator.recognizers.add(new BetterFoliageClient());
+ leafGenerator.loadLeafMappings(new File(BetterFoliage.configDir, "leafMask.properties"));
+ }
+
+ protected static void addLeafBlockClass(String className) {
+ try {
+ blockLeavesClasses.add(Class.forName(className));
+ } catch(ClassNotFoundException e) {
+ }
+ }
+
+ public int getRenderType(Block block) {
+ if (Config.grassEnabled && block instanceof BlockGrass) return grassRenderId;
+
+ if (Config.leavesEnabled)
+ for (Class> clazz : blockLeavesClasses)
+ if (clazz.isAssignableFrom(block.getClass()))
+ return leavesRenderId;
+
+ return block.getRenderType();
+ }
+
+ public boolean isLeafTexture(TextureAtlasSprite icon) {
+ String resourceLocation = icon.getIconName();
+ if (resourceLocation.startsWith("forestry:leaves/")) return true;
+ return false;
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/client/KeyHandler.java b/src/main/java/mods/betterfoliage/client/KeyHandler.java
new file mode 100644
index 0000000..c3fff6e
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/KeyHandler.java
@@ -0,0 +1,27 @@
+package mods.betterfoliage.client;
+
+import cpw.mods.fml.client.FMLClientHandler;
+import cpw.mods.fml.client.registry.ClientRegistry;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.gameevent.InputEvent;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import mods.betterfoliage.BetterFoliage;
+import mods.betterfoliage.client.gui.ConfigGuiScreen;
+import net.minecraft.client.settings.KeyBinding;
+
+@SideOnly(Side.CLIENT)
+public class KeyHandler {
+
+ public static KeyBinding guiBinding;
+
+ public KeyHandler() {
+ guiBinding = new KeyBinding("key.betterfoliage.gui", 66, BetterFoliage.MOD_NAME);
+ ClientRegistry.registerKeyBinding(guiBinding);
+ }
+
+ @SubscribeEvent
+ public void handleKeyPress(InputEvent.KeyInputEvent event) {
+ if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiScreen(null));
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/client/gui/ConfigGuiFactory.java b/src/main/java/mods/betterfoliage/client/gui/ConfigGuiFactory.java
new file mode 100644
index 0000000..00b873a
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/gui/ConfigGuiFactory.java
@@ -0,0 +1,32 @@
+package mods.betterfoliage.client.gui;
+
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import cpw.mods.fml.client.IModGuiFactory;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class ConfigGuiFactory implements IModGuiFactory {
+
+ public void initialize(Minecraft minecraftInstance) {
+
+ }
+
+ public Class extends GuiScreen> mainConfigGuiClass() {
+ return ConfigGuiScreen.class;
+ }
+
+ public Set runtimeGuiCategories() {
+ return ImmutableSet.of();
+ }
+
+ public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/mods/betterfoliage/client/gui/ConfigGuiScreen.java b/src/main/java/mods/betterfoliage/client/gui/ConfigGuiScreen.java
new file mode 100644
index 0000000..2f797b2
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/gui/ConfigGuiScreen.java
@@ -0,0 +1,96 @@
+package mods.betterfoliage.client.gui;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import mods.betterfoliage.common.config.Config;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.util.EnumChatFormatting;
+import cpw.mods.fml.client.FMLClientHandler;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class ConfigGuiScreen extends GuiScreen {
+
+ public enum Button {CLOSE, TOGGLE_LEAVES, TOGGLE_GRASS}
+
+ private GuiScreen parent;
+ protected List widgets = Lists.newLinkedList();
+
+ public ConfigGuiScreen(GuiScreen parent) {
+ this.parent = parent;
+ int id = 3;
+ widgets.add(new OptionDoubleWidget(Config.leavesSize, -160, -65, 150, 40, id++, id++, "message.betterfoliage.size", "%.2f"));
+ widgets.add(new OptionDoubleWidget(Config.leavesHOffset, -160, -35, 150, 40, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
+ widgets.add(new OptionDoubleWidget(Config.leavesVOffset, -160, -5, 150, 40, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
+
+ widgets.add(new OptionDoubleWidget(Config.grassSize, 10, -65, 150, 40, id++, id++, "message.betterfoliage.size", "%.2f"));
+ widgets.add(new OptionDoubleWidget(Config.grassHOffset, 10, -35, 150, 40, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
+ widgets.add(new OptionDoubleWidget(Config.grassHeightMin, 10, -5, 150, 40, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
+ widgets.add(new OptionDoubleWidget(Config.grassHeightMax, 10, 25, 150, 40, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
+ }
+
+ @Override
+ public void drawScreen(int par1, int par2, float par3) {
+ this.drawDefaultBackground();
+ int x = width / 2;
+ int y = height / 2;
+ for (OptionDoubleWidget widget : widgets) widget.drawStrings(this, fontRendererObj, x, y, 14737632, 16777120);
+ super.drawScreen(par1, par2, par3);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void initGui() {
+ int x = width / 2;
+ int y = height / 2;
+ for (OptionDoubleWidget widget : widgets) widget.addButtons(buttonList, x, y);
+ buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 100, 100, 20, "Close"));
+ buttonList.add(new GuiButton(Button.TOGGLE_LEAVES.ordinal(), x - 160, y - 100, 150, 20, ""));
+ buttonList.add(new GuiButton(Button.TOGGLE_GRASS.ordinal(), x + 10, y - 100, 150, 20, ""));
+ updateButtons();
+ }
+
+ protected void updateButtons() {
+ setButtonOptionBoolean(Button.TOGGLE_LEAVES, "message.betterfoliage.betterLeaves", Config.leavesEnabled);
+ setButtonOptionBoolean(Button.TOGGLE_GRASS, "message.betterfoliage.betterGrass", Config.grassEnabled);
+ }
+
+ @Override
+ protected void actionPerformed(GuiButton button) {
+ super.actionPerformed(button);
+
+ if (button.id == Button.CLOSE.ordinal()) {
+ Config.save();
+ Minecraft.getMinecraft().renderGlobal.loadRenderers();
+ FMLClientHandler.instance().showGuiScreen(parent);
+ }
+ if (button.id == Button.TOGGLE_LEAVES.ordinal()) Config.leavesEnabled = !Config.leavesEnabled;
+ if (button.id == Button.TOGGLE_GRASS.ordinal()) Config.grassEnabled = !Config.grassEnabled;
+
+ for (OptionDoubleWidget widget : widgets) {
+ if (button.id == widget.idDecrement) widget.option.decrement();
+ if (button.id == widget.idIncrement) widget.option.increment();
+ if (widget.option == Config.grassHeightMin && Config.grassHeightMin.value > Config.grassHeightMax.value) Config.grassHeightMin.value = Config.grassHeightMax.value;
+ if (widget.option == Config.grassHeightMax && Config.grassHeightMin.value > Config.grassHeightMax.value) Config.grassHeightMax.value = Config.grassHeightMin.value;
+ }
+ updateButtons();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void setButtonOptionBoolean(Button enumButton, String msgKey, boolean option) {
+ for (GuiButton button : (List) buttonList) {
+ if (button.id == enumButton.ordinal()) {
+ String optionText = option ? (EnumChatFormatting.GREEN + I18n.format("message.betterfoliage.optionOn")) : (EnumChatFormatting.RED + I18n.format("message.betterfoliage.optionOff"));
+ button.displayString = I18n.format(msgKey, optionText);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/mods/betterfoliage/client/gui/OptionDoubleWidget.java b/src/main/java/mods/betterfoliage/client/gui/OptionDoubleWidget.java
new file mode 100644
index 0000000..51d1cc9
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/gui/OptionDoubleWidget.java
@@ -0,0 +1,48 @@
+package mods.betterfoliage.client.gui;
+
+import java.util.List;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import mods.betterfoliage.common.config.OptionDouble;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.resources.I18n;
+
+@SideOnly(Side.CLIENT)
+public class OptionDoubleWidget {
+
+ public OptionDouble option;
+ public int x;
+ public int y;
+ public int width;
+ public int numWidth;
+ public int idDecrement;
+ public int idIncrement;
+ public String keyLabel;
+ public String formatString;
+
+ public OptionDoubleWidget(OptionDouble option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel, String formatString) {
+ this.option = option;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.numWidth = numWidth;
+ this.idDecrement = idDecrement;
+ this.idIncrement = idIncrement;
+ this.keyLabel = keyLabel;
+ this.formatString = formatString;
+ }
+
+ public void addButtons(List buttonList, int xOffset, int yOffset) {
+ buttonList.add(new GuiButton(idDecrement, xOffset + x + width - numWidth - 40, yOffset + y, 20, 20, "-"));
+ buttonList.add(new GuiButton(idIncrement, xOffset + x + width - 20, yOffset + y, 20, 20, "+"));
+ }
+
+ public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor) {
+ screen.drawString(fontRenderer, I18n.format(keyLabel), xOffset + x, yOffset + y + 5, labelColor);
+ screen.drawCenteredString(fontRenderer, String.format(formatString, option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/client/render/RenderBlockAOBase.java b/src/main/java/mods/betterfoliage/client/render/RenderBlockAOBase.java
new file mode 100644
index 0000000..f537243
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/render/RenderBlockAOBase.java
@@ -0,0 +1,317 @@
+package mods.betterfoliage.client.render;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import mods.betterfoliage.common.util.Double3;
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.MathHelper;
+
+import org.lwjgl.opengl.GL11;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+/** Block renderer base class. Stores calculated ambient occlusion light and color values when rendering
+ * block sides for later use.
+ * @author octarine-noise
+ */
+@SideOnly(Side.CLIENT)
+public class RenderBlockAOBase extends RenderBlocks {
+
+ /** AO light and color values
+ * @author octarine-noise
+ */
+ @SideOnly(Side.CLIENT)
+ public static class ShadingValues {
+ public int passCounter = 0;
+ public int brightness;
+ public float red;
+ public float green;
+ public float blue;
+ }
+
+ protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
+ protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
+
+ /** Random vector pool. Unit rotation vectors in the XZ plane, Y coord goes between [-1.0, 1.0].
+ * Filled at init time */
+ public Double3[] pRot = new Double3[64];
+
+ /** Pool of random double values. Filled at init time. */
+ public double[] pRand = new double[64];
+
+ public ShadingValues aoXPYZPP = new ShadingValues();
+ public ShadingValues aoXPYZPN = new ShadingValues();
+ public ShadingValues aoXPYZNP = new ShadingValues();
+ public ShadingValues aoXPYZNN = new ShadingValues();
+ public ShadingValues aoXNYZPP = new ShadingValues();
+ public ShadingValues aoXNYZPN = new ShadingValues();
+ public ShadingValues aoXNYZNP = new ShadingValues();
+ public ShadingValues aoXNYZNN = new ShadingValues();
+ public ShadingValues aoYPXZPP = new ShadingValues();
+ public ShadingValues aoYPXZPN = new ShadingValues();
+ public ShadingValues aoYPXZNP = new ShadingValues();
+ public ShadingValues aoYPXZNN = new ShadingValues();
+ public ShadingValues aoYNXZPP = new ShadingValues();
+ public ShadingValues aoYNXZPN = new ShadingValues();
+ public ShadingValues aoYNXZNP = new ShadingValues();
+ public ShadingValues aoYNXZNN = new ShadingValues();
+ public ShadingValues aoZPXYPP = new ShadingValues();
+ public ShadingValues aoZPXYPN = new ShadingValues();
+ public ShadingValues aoZPXYNP = new ShadingValues();
+ public ShadingValues aoZPXYNN = new ShadingValues();
+ public ShadingValues aoZNXYPP = new ShadingValues();
+ public ShadingValues aoZNXYPN = new ShadingValues();
+ public ShadingValues aoZNXYNP = new ShadingValues();
+ public ShadingValues aoZNXYNN = new ShadingValues();
+
+ /** Initialize random values */
+ public void init() {
+ List perturbs = new ArrayList(64);
+ for (int idx = 0; idx < 64; idx++) {
+ double angle = (double) idx * Math.PI * 2.0 / 64.0;
+ perturbs.add(new Double3(Math.cos(angle), Math.random() * 2.0 - 1.0, Math.sin(angle)));
+ pRand[idx] = Math.random();
+ }
+ Collections.shuffle(perturbs);
+ Iterator iter = perturbs.iterator();
+ for (int idx = 0; idx < 64; idx++) pRot[idx] = iter.next();
+ }
+
+ /** Get a semi-random value depending on block position.
+ * @param x block X coord
+ * @param y block Y coord
+ * @param z block Z coord
+ * @param seed additional seed
+ * @return semirandom value
+ */
+ protected int getSemiRandomFromPos(double x, double y, double z, int seed) {
+ int sum = MathHelper.floor_double(x) * 3 + MathHelper.floor_double(y) * 5 + MathHelper.floor_double(z) * 7 + seed * 11;
+ return sum & 63;
+ }
+
+ protected void renderStandardBlockAsItem(RenderBlocks renderer, Block p_147800_1_, int p_147800_2_, float p_147800_3_) {
+ Tessellator tessellator = Tessellator.instance;
+ boolean flag = p_147800_1_ == Blocks.grass;
+
+ float f2;
+ float f3;
+ int k;
+
+ p_147800_1_.setBlockBoundsForItemRender();
+ renderer.setRenderBoundsFromBlock(p_147800_1_);
+ GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
+ GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0.0F, -1.0F, 0.0F);
+ renderer.renderFaceYNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 0, p_147800_2_));
+ tessellator.draw();
+
+ if (flag && renderer.useInventoryTint)
+ {
+ k = p_147800_1_.getRenderColor(p_147800_2_);
+ f2 = (float)(k >> 16 & 255) / 255.0F;
+ f3 = (float)(k >> 8 & 255) / 255.0F;
+ float f4 = (float)(k & 255) / 255.0F;
+ GL11.glColor4f(f2 * p_147800_3_, f3 * p_147800_3_, f4 * p_147800_3_, 1.0F);
+ }
+
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0.0F, 1.0F, 0.0F);
+ renderer.renderFaceYPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 1, p_147800_2_));
+ tessellator.draw();
+
+ if (flag && renderer.useInventoryTint)
+ {
+ GL11.glColor4f(p_147800_3_, p_147800_3_, p_147800_3_, 1.0F);
+ }
+
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0.0F, 0.0F, -1.0F);
+ renderer.renderFaceZNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 2, p_147800_2_));
+ tessellator.draw();
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0.0F, 0.0F, 1.0F);
+ renderer.renderFaceZPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 3, p_147800_2_));
+ tessellator.draw();
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(-1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 4, p_147800_2_));
+ tessellator.draw();
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 5, p_147800_2_));
+ tessellator.draw();
+ GL11.glTranslatef(0.5F, 0.5F, 0.5F);
+ }
+
+ @Override
+ public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceZNeg(block, x, y, z, icon);
+ saveShadingTopLeft(aoZNXYPP);
+ saveShadingTopRight(aoZNXYNP);
+ saveShadingBottomLeft(aoZNXYPN);
+ saveShadingBottomRight(aoZNXYNN);
+ }
+
+ @Override
+ public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceZPos(block, x, y, z, icon);
+ saveShadingTopLeft(aoZPXYNP);
+ saveShadingTopRight(aoZPXYPP);
+ saveShadingBottomLeft(aoZPXYNN);
+ saveShadingBottomRight(aoZPXYPN);
+ }
+
+ @Override
+ public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceXNeg(block, x, y, z, icon);
+ saveShadingTopLeft(aoXNYZPN);
+ saveShadingTopRight(aoXNYZPP);
+ saveShadingBottomLeft(aoXNYZNN);
+ saveShadingBottomRight(aoXNYZNP);
+ }
+
+ @Override
+ public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceXPos(block, x, y, z, icon);
+ saveShadingTopLeft(aoXPYZPP);
+ saveShadingTopRight(aoXPYZPN);
+ saveShadingBottomLeft(aoXPYZNP);
+ saveShadingBottomRight(aoXPYZNN);
+ }
+
+ @Override
+ public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceYNeg(block, x, y, z, icon);
+ saveShadingTopLeft(aoYNXZNP);
+ saveShadingTopRight(aoYNXZPP);
+ saveShadingBottomLeft(aoYNXZNN);
+ saveShadingBottomRight(aoYNXZPN);
+ }
+
+ @Override
+ public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
+ super.renderFaceYPos(block, x, y, z, icon);
+ saveShadingTopLeft(aoYPXZPP);
+ saveShadingTopRight(aoYPXZNP);
+ saveShadingBottomLeft(aoYPXZPN);
+ saveShadingBottomRight(aoYPXZNN);
+ }
+
+ /** Save AO values for top left vertex
+ * @param values {@link ShadingValues} to store values in
+ */
+ protected void saveShadingTopLeft(ShadingValues values) {
+ if (--values.passCounter != 0) return;
+ values.brightness = brightnessTopLeft;
+ values.red = colorRedTopLeft;
+ values.green = colorGreenTopLeft;
+ values.blue = colorBlueTopLeft;
+ }
+
+ protected void saveShadingTopRight(ShadingValues values) {
+ if (--values.passCounter != 0) return;
+ values.brightness = brightnessTopRight;
+ values.red = colorRedTopRight;
+ values.green = colorGreenTopRight;
+ values.blue = colorBlueTopRight;
+ }
+
+ protected void saveShadingBottomLeft(ShadingValues values) {
+ if (--values.passCounter != 0) return;
+ values.brightness = brightnessBottomLeft;
+ values.red = colorRedBottomLeft;
+ values.green = colorGreenBottomLeft;
+ values.blue = colorBlueBottomLeft;
+ }
+
+ protected void saveShadingBottomRight(ShadingValues values) {
+ if (--values.passCounter != 0) return;
+ values.brightness = brightnessBottomRight;
+ values.red = colorRedBottomRight;
+ values.green = colorGreenBottomRight;
+ values.blue = colorBlueBottomRight;
+ }
+
+ /** Set pass counter on all shading value objects.
+ * Used to collect AO values from a specific draw pass
+ * if the underlying renderer draws overlays
+ * @param value pass counter
+ */
+ protected void setPassCounters(int value) {
+ aoXPYZPP.passCounter = value;
+ aoXPYZPN.passCounter = value;
+ aoXPYZNP.passCounter = value;
+ aoXPYZNN.passCounter = value;
+ aoXNYZPP.passCounter = value;
+ aoXNYZPN.passCounter = value;
+ aoXNYZNP.passCounter = value;
+ aoXNYZNN.passCounter = value;
+ aoYPXZPP.passCounter = value;
+ aoYPXZPN.passCounter = value;
+ aoYPXZNP.passCounter = value;
+ aoYPXZNN.passCounter = value;
+ aoYNXZPP.passCounter = value;
+ aoYNXZPN.passCounter = value;
+ aoYNXZNP.passCounter = value;
+ aoYNXZNN.passCounter = value;
+ aoZPXYPP.passCounter = value;
+ aoZPXYPN.passCounter = value;
+ aoZPXYNP.passCounter = value;
+ aoZPXYNN.passCounter = value;
+ aoZNXYPP.passCounter = value;
+ aoZNXYPN.passCounter = value;
+ aoZNXYNP.passCounter = value;
+ aoZNXYNN.passCounter = value;
+ }
+
+ /** Render textured quad
+ * @param icon texture to use
+ * @param center center of quad
+ * @param vec1 vector to the half-point of one of the sides
+ * @param vec2 vector to half-point of side next to vec1
+ * @param uvRot number of increments to rotate UV coordinates by
+ */
+ protected void renderQuad(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot) {
+ Tessellator tessellator = Tessellator.instance;
+ tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
+ tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
+ tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
+ tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
+ }
+
+ /** Render textured quad using AO information
+ * @param icon texture to use
+ * @param center center of quad
+ * @param vec1 vector to the half-point of one of the sides
+ * @param vec2 vector to half-point of side next to vec1
+ * @param uvRot number of increments to rotate UV coordinates by
+ * @param aoPP AO values for vertex at (+vec1, +vec2)
+ * @param aoNP AO values for vertex at (-vec1, +vec2)
+ * @param aoNN AO values for vertex at (-vec1, -vec2)
+ * @param aoPN AO values for vertex at (+vec1, -vec2)
+ */
+ protected void renderQuadWithShading(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot, ShadingValues aoPP, ShadingValues aoNP, ShadingValues aoNN, ShadingValues aoPN) {
+ Tessellator tessellator = Tessellator.instance;
+ tessellator.setBrightness(aoPP.brightness);
+ tessellator.setColorOpaque_F(aoPP.red, aoPP.green, aoPP.blue);
+ tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
+ tessellator.setBrightness(aoNP.brightness);
+ tessellator.setColorOpaque_F(aoNP.red, aoNP.green, aoNP.blue);
+ tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
+ tessellator.setBrightness(aoNN.brightness);
+ tessellator.setColorOpaque_F(aoNN.red, aoNN.green, aoNN.blue);
+ tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
+ tessellator.setBrightness(aoPN.brightness);
+ tessellator.setColorOpaque_F(aoPN.red, aoPN.green, aoPN.blue);
+ tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterGrass.java b/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterGrass.java
new file mode 100644
index 0000000..a2e41da
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterGrass.java
@@ -0,0 +1,88 @@
+package mods.betterfoliage.client.render;
+
+import mods.betterfoliage.common.config.Config;
+import mods.betterfoliage.common.util.Double3;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.util.IIcon;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.client.event.TextureStitchEvent;
+import net.minecraftforge.common.MinecraftForge;
+import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
+import cpw.mods.fml.client.registry.RenderingRegistry;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class RenderBlockBetterGrass extends RenderBlockAOBase implements ISimpleBlockRenderingHandler {
+
+ public IIcon grassIcons[] = new IIcon[2];
+
+ public static int register() {
+ int result = RenderingRegistry.getNextAvailableRenderId();
+ RenderBlockBetterGrass renderGrass = new RenderBlockBetterGrass();
+ RenderingRegistry.registerBlockHandler(result, renderGrass);
+ MinecraftForge.EVENT_BUS.register(renderGrass);
+ renderGrass.init();
+ return result;
+ }
+
+ public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
+ renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
+ }
+
+ public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
+ // store world for later use
+ blockAccess = world;
+
+ // render grass block
+ setRenderBoundsFromBlock(block);
+ setPassCounters(1);
+ boolean result = renderStandardBlock(block, x, y, z);
+
+ if (y == 255 || !blockAccess.isAirBlock(x, y + 1, z)) return result;
+
+ int variation = getSemiRandomFromPos(x, y, z, 0);
+ int heightVariation = getSemiRandomFromPos(x, y, z, 1);
+ double halfSize = Config.grassSize.value * 0.5;
+ double halfHeight = 0.5 * (Config.grassHeightMin.value + pRand[heightVariation] * (Config.grassHeightMax.value - Config.grassHeightMin.value));
+ Double3 drawCenter = new Double3(x + 0.5, y + 1.0 + halfHeight, z + 0.5).add(pRot[variation].scaleAxes(Config.grassHOffset.value, 0.0, Config.grassHOffset.value));
+ Double3 horz1 = new Double3(halfSize, 0.0, halfSize);
+ Double3 horz2 = new Double3(halfSize, 0.0, -halfSize);
+ Double3 vert1 = new Double3(0.0, halfHeight, 0.0);
+ IIcon grassIcon = grassIcons[variation % 2];
+
+ if (Minecraft.isAmbientOcclusionEnabled()) {
+ renderQuadWithShading(grassIcon, drawCenter, horz1, vert1, 0, aoYPXZPP, aoYPXZNN, aoYPXZNN, aoYPXZPP);
+ renderQuadWithShading(grassIcon, drawCenter, horz1.inverse(), vert1, 0, aoYPXZNN, aoYPXZPP, aoYPXZPP, aoYPXZNN);
+ renderQuadWithShading(grassIcon, drawCenter, horz2, vert1, 0, aoYPXZPN, aoYPXZNP, aoYPXZNP, aoYPXZPN);
+ renderQuadWithShading(grassIcon, drawCenter, horz2.inverse(), vert1, 0, aoYPXZNP, aoYPXZPN, aoYPXZPN, aoYPXZNP);
+ } else {
+ Tessellator.instance.setBrightness(block.getMixedBrightnessForBlock(blockAccess, x, y + 1, z));
+ renderQuad(grassIcon, drawCenter, horz1, vert1, 0);
+ renderQuad(grassIcon, drawCenter, horz1.inverse(), vert1, 0);
+ renderQuad(grassIcon, drawCenter, horz2, vert1, 0);
+ renderQuad(grassIcon, drawCenter, horz2.inverse(), vert1, 0);
+ }
+ return result;
+ }
+
+ public boolean shouldRender3DInInventory(int modelId) {
+ return true;
+ }
+
+ public int getRenderId() {
+ return 0;
+ }
+
+ @SubscribeEvent
+ public void handleTextureReload(TextureStitchEvent.Pre event) {
+ if (event.map.getTextureType() != 0) return;
+ for (int idx = 0; idx < 2; idx++) {
+ grassIcons[idx] = event.map.registerIcon(String.format("betterfoliage:grass_%d", idx));
+ }
+ }
+}
diff --git a/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterLeaves.java b/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterLeaves.java
new file mode 100644
index 0000000..373c479
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/render/RenderBlockBetterLeaves.java
@@ -0,0 +1,111 @@
+package mods.betterfoliage.client.render;
+
+import mods.betterfoliage.client.BetterFoliageClient;
+import mods.betterfoliage.common.config.Config;
+import mods.betterfoliage.common.util.Double3;
+import mods.betterfoliage.common.util.ReflectionUtil;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.util.IIcon;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
+import cpw.mods.fml.client.registry.RenderingRegistry;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class RenderBlockBetterLeaves extends RenderBlockAOBase implements ISimpleBlockRenderingHandler {
+
+ public static int register() {
+ int result = RenderingRegistry.getNextAvailableRenderId();
+ RenderBlockBetterLeaves renderLeaves = new RenderBlockBetterLeaves();
+ RenderingRegistry.registerBlockHandler(result, renderLeaves);
+ renderLeaves.init();
+ return result;
+ }
+
+ public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
+ renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
+ }
+
+ public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
+ // store world for later use
+ blockAccess = world;
+
+ // render leaves center
+ setPassCounters(1);
+ int origRenderType = block.getRenderType();
+ boolean result;
+
+ setRenderBoundsFromBlock(block);
+ if (origRenderType == 0) {
+ result = renderStandardBlock(block, x, y, z);
+ } else {
+ ISimpleBlockRenderingHandler handler = ReflectionUtil.getRenderingHandler(origRenderType);
+ result = handler.renderWorldBlock(world, x, y, z, block, origRenderType, this);
+ }
+
+ if (isBlockSurrounded(x, y, z)) return result;
+
+ // find generated texture to render with, assume the
+ // "true" texture of the block is the one on the north size
+ TextureAtlasSprite blockLeafIcon = (TextureAtlasSprite) block.getIcon(world, x, y, z, ForgeDirection.NORTH.ordinal());
+ IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
+ if (crossLeafIcon == null) {
+ return result;
+ }
+
+ int variation = getSemiRandomFromPos(x, y, z, 0);
+ double halfSize = 0.5 * Config.leavesSize.value;
+ boolean isAirTop = y == 255 || blockAccess.isAirBlock(x, y + 1, z);
+ boolean isAirBottom = y == 0 || blockAccess.isAirBlock(x, y - 1, z);
+ Double3 drawCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
+ Double3 horz1 = new Double3(halfSize, 0.0, halfSize).add(pRot[variation].scaleAxes(Config.leavesHOffset.value, Config.leavesVOffset.value, Config.leavesHOffset.value));
+ Double3 horz2 = new Double3(halfSize, 0.0, -halfSize).add(pRot[(variation + 1) & 63].scaleAxes(Config.leavesHOffset.value, Config.leavesVOffset.value, Config.leavesHOffset.value));
+ Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
+
+ if (Minecraft.isAmbientOcclusionEnabled()) {
+ renderQuadWithShading(crossLeafIcon, drawCenter, horz1, vert1, variation,
+ isAirTop ? aoYPXZPP : aoZPXYPP, isAirTop ? aoYPXZNN : aoXNYZPN, isAirBottom ? aoYNXZNN : aoXNYZNN, isAirBottom ? aoYNXZPP : aoZPXYPN);
+ renderQuadWithShading(crossLeafIcon, drawCenter, horz1.inverse(), vert1, variation,
+ isAirTop ? aoYPXZNN : aoZNXYNP, isAirTop ? aoYPXZPP : aoXPYZPP, isAirBottom ? aoYNXZPP : aoXPYZNP, isAirBottom ? aoYNXZNN : aoZNXYNN);
+ renderQuadWithShading(crossLeafIcon, drawCenter, horz2, vert1, variation,
+ isAirTop ? aoYPXZPN : aoXPYZPN, isAirTop ? aoYPXZNP : aoZPXYNP, isAirBottom ? aoYNXZNP : aoZPXYNN, isAirBottom ? aoYNXZPN : aoXPYZNN);
+ renderQuadWithShading(crossLeafIcon, drawCenter, horz2.inverse(), vert1, variation,
+ isAirTop ? aoYPXZNP : aoXNYZPP, isAirTop ? aoYPXZPN : aoZNXYPP, isAirBottom ? aoYNXZPN : aoZNXYPN, isAirBottom ? aoYNXZNP : aoXNYZNP);
+ } else {
+ if (isAirTop) Tessellator.instance.setBrightness(block.getMixedBrightnessForBlock(blockAccess, x, y + 1, z));
+ else if (isAirBottom) Tessellator.instance.setBrightness(block.getMixedBrightnessForBlock(blockAccess, x, y - 1, z));
+ else Tessellator.instance.setBrightness(block.getMixedBrightnessForBlock(blockAccess, x, y, z));
+
+ renderQuad(crossLeafIcon, drawCenter, horz1, vert1, variation);
+ renderQuad(crossLeafIcon, drawCenter, horz1.inverse(), vert1, variation);
+ renderQuad(crossLeafIcon, drawCenter, horz2, vert1, variation);
+ renderQuad(crossLeafIcon, drawCenter, horz2.inverse(), vert1, variation);
+ }
+ return result;
+ }
+
+ public boolean shouldRender3DInInventory(int modelId) {
+ return true;
+ }
+
+ public int getRenderId() {
+ return 0;
+ }
+
+ protected boolean isBlockSurrounded(int x, int y, int z) {
+ if (blockAccess.isAirBlock(x + 1, y, z)) return false;
+ if (blockAccess.isAirBlock(x - 1, y, z)) return false;
+ if (blockAccess.isAirBlock(x, y, z + 1)) return false;
+ if (blockAccess.isAirBlock(x, y, z - 1)) return false;
+ if (y == 255 || blockAccess.isAirBlock(x, y + 1, z)) return false;
+ if (y == 0 || blockAccess.isAirBlock(x, y - 1, z)) return false;
+ return true;
+ }
+
+}
diff --git a/src/main/java/mods/betterfoliage/client/resource/ILeafTextureRecognizer.java b/src/main/java/mods/betterfoliage/client/resource/ILeafTextureRecognizer.java
new file mode 100644
index 0000000..cb347d8
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/resource/ILeafTextureRecognizer.java
@@ -0,0 +1,11 @@
+package mods.betterfoliage.client.resource;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+
+@SideOnly(Side.CLIENT)
+public interface ILeafTextureRecognizer {
+
+ public boolean isLeafTexture(TextureAtlasSprite icon);
+}
diff --git a/src/main/java/mods/betterfoliage/client/resource/LeafTextureGenerator.java b/src/main/java/mods/betterfoliage/client/resource/LeafTextureGenerator.java
new file mode 100644
index 0000000..365b80a
--- /dev/null
+++ b/src/main/java/mods/betterfoliage/client/resource/LeafTextureGenerator.java
@@ -0,0 +1,179 @@
+package mods.betterfoliage.client.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import mods.betterfoliage.BetterFoliage;
+import mods.betterfoliage.client.BetterFoliageClient;
+import mods.betterfoliage.common.util.DeobfNames;
+import mods.betterfoliage.common.util.ReflectionUtil;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.IResource;
+import net.minecraft.client.resources.IResourceManager;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.event.TextureStitchEvent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+/** Generates rounded crossleaf textures for all registered normal leaf textures at stitch time.
+ * @author octarine-noise
+ */
+@SideOnly(Side.CLIENT)
+public class LeafTextureGenerator implements IIconRegister, IResourceManager {
+
+ /** Resource domain name of autogenerated crossleaf textures */
+ public String domainName = "bf_leaves_autogen";
+
+ /** Resource location for fallback texture (if the generation process fails) */
+ public ResourceLocation missing_resource = new ResourceLocation("betterfoliage", "textures/blocks/missingleaf.png");
+
+ /** Texture atlas for block textures used in the current run */
+ public TextureMap blockTextures;
+
+ /** List of helpers which can identify leaf textures loaded by alternate means */
+ public List recognizers = Lists.newLinkedList();
+
+ /** Number of textures generated in the current run */
+ int counter = 0;
+
+ /** Map leaf types to alpha masks */
+ public Map maskMappings = Maps.newHashMap();
+
+ public Set getResourceDomains() {
+ return ImmutableSet.of(domainName);
+ }
+
+ public IResource getResource(ResourceLocation resourceLocation) throws IOException {
+ // remove "/blocks/textures/" from beginning
+ String origResPath = resourceLocation.getResourcePath().substring(16);
+ LeafTextureResource result = new LeafTextureResource(new ResourceLocation(origResPath), maskMappings);
+ if (result.data == null) {
+ return Minecraft.getMinecraft().getResourceManager().getResource(missing_resource);
+ } else {
+ counter++;
+ return result;
+ }
+ }
+
+ public List getAllResources(ResourceLocation resource) throws IOException {
+ return ImmutableList.of();
+ }
+
+ /** Leaf blocks register their textures here. An extra texture will be registered in the atlas
+ * for each, with the resource domain of this generator.
+ * @return the originally registered {@link IIcon} already in the atlas
+ */
+ public IIcon registerIcon(String resourceLocation) {
+ IIcon original = blockTextures.getTextureExtry(resourceLocation);
+ blockTextures.registerIcon(new ResourceLocation(domainName, resourceLocation).toString());
+ BetterFoliage.log.debug(String.format("Found leaf texture: %s", resourceLocation));
+ return original;
+ }
+
+ /** Iterates through all leaf blocks in the registry and makes them register
+ * their textures to "sniff out" all leaf textures.
+ * @param event
+ */
+ @SuppressWarnings("unchecked")
+ @SubscribeEvent
+ public void handleTextureReload(TextureStitchEvent.Pre event) {
+ if (event.map.getTextureType() != 0) return;
+
+ blockTextures = event.map;
+ counter = 0;
+ BetterFoliage.log.info("Reloading leaf textures");
+
+ Map domainManagers = ReflectionUtil.getDomainResourceManagers();
+ if (domainManagers == null) {
+ BetterFoliage.log.warn("Failed to inject leaf texture generator");
+ return;
+ }
+ domainManagers.put(domainName, this);
+
+ // register simple block textures
+ Iterator iter = Block.blockRegistry.iterator();
+ while(iter.hasNext()) {
+ Block block = iter.next();
+ for (Class> clazz : BetterFoliageClient.blockLeavesClasses) if (clazz.isAssignableFrom(block.getClass())) {
+ BetterFoliage.log.debug(String.format("Inspecting leaf block: %s", block.getClass().getName()));
+ block.registerBlockIcons(this);
+ }
+ }
+
+ // enumerate all registered textures, find leaf textures among them
+ Map mapAtlas = null;
+ mapAtlas = ReflectionUtil.getField(blockTextures, DeobfNames.TM_MRS_SRG, Map.class);
+ if (mapAtlas == null) mapAtlas = ReflectionUtil.getField(blockTextures, DeobfNames.TM_MRS_MCP, Map.class);
+ if (mapAtlas == null) {
+ BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
+ } else {
+ Set foundLeafTextures = Sets.newHashSet();
+ for (TextureAtlasSprite icon : mapAtlas.values())
+ for (ILeafTextureRecognizer recognizer : recognizers)
+ if (recognizer.isLeafTexture(icon))
+ foundLeafTextures.add(icon.getIconName());
+ for (String resourceLocation : foundLeafTextures) {
+ BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", resourceLocation));
+ blockTextures.registerIcon(new ResourceLocation(domainName, resourceLocation).toString());
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void endTextureReload(TextureStitchEvent.Post event) {
+ blockTextures = null;
+ if (event.map.getTextureType() == 0) BetterFoliage.log.info(String.format("Generated %d leaf textures", counter));
+ }
+
+ public void loadLeafMappings(File leafMaskFile) {
+ Properties props = new Properties();
+ try {
+ FileInputStream fis = new FileInputStream(leafMaskFile);
+ props.load(fis);
+ } catch (Exception e) {
+ maskMappings.put("spruce", "fine");
+ maskMappings.put("fir", "fine");
+ maskMappings.put("bamboo", "fine");
+ saveLeafMappings(leafMaskFile);
+ return;
+ }
+
+ for (Map.Entry