first code commit

This commit is contained in:
octarine-noise
2014-06-25 23:27:17 +02:00
parent 554e06176b
commit 3bd402b964
33 changed files with 1591 additions and 0 deletions

9
.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
<classpathentry combineaccessrules="false" kind="src" path="/MinecraftForge-1.7.2"/>
<classpathentry kind="output" path="bin"/>
</classpath>

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.gradle/
.settings/
bin/
build/
libs/

18
.project Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>BetterFoliage</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

45
build.gradle Normal file
View File

@@ -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")
}
}

View File

@@ -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<String, String> mods, Side side) {
return true;
}
}

View File

@@ -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);
}
}

View File

@@ -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<Class<?>> 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;
}
}

View File

@@ -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));
}
}

View File

@@ -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<RuntimeOptionCategoryElement> runtimeGuiCategories() {
return ImmutableSet.<RuntimeOptionCategoryElement>of();
}
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
return null;
}
}

View File

@@ -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<OptionDoubleWidget> 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<GuiButton>) 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;
}
}
}
}

View File

@@ -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<GuiButton> 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);
}
}

View File

@@ -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<Double3> perturbs = new ArrayList<Double3>(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<Double3> 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]));
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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<ILeafTextureRecognizer> recognizers = Lists.newLinkedList();
/** Number of textures generated in the current run */
int counter = 0;
/** Map leaf types to alpha masks */
public Map<String, String> maskMappings = Maps.newHashMap();
public Set<String> getResourceDomains() {
return ImmutableSet.<String>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<IResource> getAllResources(ResourceLocation resource) throws IOException {
return ImmutableList.<IResource>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<String, IResourceManager> 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<Block> 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<String, TextureAtlasSprite> 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<String> 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<Object, Object> entry : props.entrySet()) {
maskMappings.put(entry.getKey().toString(), entry.getValue().toString());
}
BetterFoliage.log.info(String.format("Loaded %d leaf mask mappings", maskMappings.size()));
}
protected void saveLeafMappings(File leafMaskFile) {
try {
FileOutputStream fos = new FileOutputStream(leafMaskFile);
Properties props = new Properties();
props.putAll(maskMappings);
props.store(fos, "");
} catch (Exception e) {
BetterFoliage.log.info("Failed to save default leaf mask mappings");
return;
}
BetterFoliage.log.info("Created default leaf mask mappings");
}
}

View File

@@ -0,0 +1,114 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import javax.imageio.ImageIO;
import mods.betterfoliage.BetterFoliage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** {@link IResource} containing an autogenerated round crossleaf texture
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class LeafTextureResource implements IResource {
/** Raw PNG data*/
protected byte[] data = null;
/** Name of the default alpha mask to use */
public static String defaultMask = "rough";
public LeafTextureResource(ResourceLocation resLeaf, Map<String, String> maskMappings) {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
try {
// load normal leaf texture
ResourceLocation origResource = new ResourceLocation(resLeaf.getResourceDomain(), "textures/blocks/" + resLeaf.getResourcePath());
BufferedImage origImage = ImageIO.read(resourceManager.getResource(origResource).getInputStream());
if (origImage.getWidth() != origImage.getHeight()) return;
int size = origImage.getWidth();
// load alpha mask of appropriate size
String maskType = defaultMask;
for(Map.Entry<String, String> entry : maskMappings.entrySet()) if (resLeaf.getResourcePath().contains(entry.getKey())) {
maskType = entry.getValue();
break;
}
BufferedImage maskImage = loadLeafMaskImage(maskType, size * 2);
int scale = size * 2 / maskImage.getWidth();
// tile leaf texture 2x2
BufferedImage overlayIcon = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = overlayIcon.createGraphics();
graphics.drawImage(origImage, 0, 0, null);
graphics.drawImage(origImage, 0, size, null);
graphics.drawImage(origImage, size, 0, null);
graphics.drawImage(origImage, size, size, null);
// overlay mask alpha on texture
for (int x = 0; x < overlayIcon.getWidth(); x++) {
for (int y = 0; y < overlayIcon.getHeight(); y++) {
long origPixel = overlayIcon.getRGB(x, y) & 0xFFFFFFFFl;
long maskPixel = maskImage.getRGB(x / scale, y / scale) & 0xFF000000l | 0x00FFFFFF;
overlayIcon.setRGB(x, y, (int) (origPixel & maskPixel));
}
}
// create PNG image
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(overlayIcon, "PNG", baos);
data = baos.toByteArray();
} catch (Exception e) {
BetterFoliage.log.info(String.format("Could not create leaf texture: %s, exception: %s", resLeaf.toString(), e.getClass().getSimpleName()));
}
}
/** Loads the alpha mask of the given type and size. If a mask of the exact size can not be found,
* will try to load progressively smaller masks down to 16x16
* @param type mask type
* @param size texture size
* @return alpha mask
*/
protected BufferedImage loadLeafMaskImage(String type, int size) {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
IResource maskResource = null;
while (maskResource == null && size >= 16) {
try {
maskResource = resourceManager.getResource(new ResourceLocation(String.format("betterfoliage:textures/blocks/leafmask_%d_%s.png", size, type)));
} catch (Exception e) {}
size /= 2;
}
try {
return maskResource == null ? null : ImageIO.read(maskResource.getInputStream());
} catch (IOException e) {
return null;
}
}
public InputStream getInputStream() {
return data != null ? new ByteArrayInputStream(data) : null;
}
public boolean hasMetadata() {
return false;
}
public IMetadataSection getMetadata(String var1) {
return null;
}
}

View File

@@ -0,0 +1,74 @@
package mods.betterfoliage.common.config;
import java.io.File;
import mods.betterfoliage.BetterFoliage;
import net.minecraftforge.common.config.Configuration;
public class Config {
public static boolean leavesEnabled = true;
public static boolean grassEnabled = true;
public static OptionDouble leavesHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
public static OptionDouble leavesVOffset = new OptionDouble(0.0, 0.4, 0.025, 0.1);
public static OptionDouble leavesSize = new OptionDouble(0.75, 1.8, 0.05, 1.4);
public static OptionDouble grassHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
public static OptionDouble grassHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
public static OptionDouble grassHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
public static OptionDouble grassSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
private Config() {}
public static void load() {
Configuration config = new Configuration(new File(BetterFoliage.configDir, "betterfoliage.cfg"));
config.load();
leavesEnabled = config.get("render", "leavesEnabled", true).getBoolean(true);
loadValue(config, "render", "leavesHorizontalOffset", leavesHOffset);
loadValue(config, "render", "leavesVerticalOffset", leavesVOffset);
loadValue(config, "render", "leavesSize", leavesSize);
grassEnabled = config.get("render", "grassEnabled", true).getBoolean(true);
loadValue(config, "render", "grassHorizontalOffset", grassHOffset);
loadValue(config, "render", "grassHeightMin", grassHeightMin);
loadValue(config, "render", "grassHeightMax", grassHeightMax);
if (grassHeightMin.value > grassHeightMax.value) grassHeightMin.value = grassHeightMax.value;
if (config.hasChanged()) config.save();
}
public static void save() {
Configuration config = new Configuration(new File(BetterFoliage.configDir, "betterfoliage.cfg"));
config.load();
config.get("render", "leavesEnabled", true).set(leavesEnabled);
saveValue(config, "render", "leavesHorizontalOffset", leavesHOffset);
saveValue(config, "render", "leavesVerticalOffset", leavesVOffset);
saveValue(config, "render", "leavesSize", leavesSize);
config.get("render", "grassEnabled", true).set(grassEnabled);
saveValue(config, "render", "grassHorizontalOffset", grassHOffset);
saveValue(config, "render", "grassHeightMin", grassHeightMin);
saveValue(config, "render", "grassHeightMax", grassHeightMax);
if (config.hasChanged()) config.save();
}
protected static void saveValue(Configuration config, String category, String key, OptionDouble option) {
config.get(category, key, option.value).set(option.value);
}
protected static void loadValue(Configuration config, String category, String key, OptionDouble option) {
option.value = config.get(category, key, option.value).getDouble(option.value);
if (option.value > option.max) {
option.value = option.max;
saveValue(config, category, key, option);
}
if (option.value < option.min) {
option.value = option.min;
saveValue(config, category, key, option);
}
}
}

View File

@@ -0,0 +1,26 @@
package mods.betterfoliage.common.config;
public class OptionDouble {
public double min;
public double max;
public double step;
public double value;
public OptionDouble(double min, double max, double step, double value) {
this.min = min;
this.max = max;
this.step = step;
this.value = value;
}
public void increment() {
value += step;
if (value > max) value = max;
}
public void decrement() {
value -= step;
if (value < min) value = min;
}
}

View File

@@ -0,0 +1,39 @@
package mods.betterfoliage.common.util;
public class DeobfNames {
private DeobfNames() {}
/** MCP name of RenderBlocks.renderBlockByRenderType() */
public static final String RB_RBBRT_NAME_MCP = "renderBlockByRenderType";
/** Obfuscated name of RenderBlocks.renderBlockByRenderType() */
public static final String RB_RBBRT_NAME_OBF = "b";
/** MCP signature of RenderBlocks.renderBlockByRenderType() */
public static final String RB_RBBRT_SIG_MCP = "(Lnet/minecraft/block/Block;III)Z";
/** Obfuscated signature of RenderBlocks.renderBlockByRenderType() */
public static final String RB_RBBRT_SIG_OBF = "(Lahu;III)Z";
/** MCP signature of BlockRenderTypeOverride.getRenderType(Block) */
public static final String BRTO_GRT_SIG_MCP = "(Lnet/minecraft/block/Block;)I";
/** Obfuscated signature of BlockRenderTypeOverride.getRenderType(Block) */
public static final String BRTO_GRT_SIG_OBF = "(Lahu;)I";
/** MCP name of SimpleReloadableResourceManager.domainResourceManagers */
public static final String SRRM_DRM_MCP = "domainResourceManagers";
/** SRG name of SimpleReloadableResourceManager.domainResourceManagers */
public static final String SRRM_DRM_SRGNAME = "field_110548_a";
/** MCP name of TextureMap.mapRegisteredSprites */
public static final String TM_MRS_MCP = "mapRegisteredSprites";
/** Obfuscated name of TextureMap.mapRegisteredSprites */
public static final String TM_MRS_OBF = "bpr";
/** SRG name of TextureMap.mapRegisteredSprites */
public static final String TM_MRS_SRG = "field_110574_e";
}

View File

@@ -0,0 +1,30 @@
package mods.betterfoliage.common.util;
public class Double3 {
public final double x;
public final double y;
public final double z;
public Double3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Double3 add(Double3 other) {
return new Double3(x + other.x, y + other.y, z + other.z);
}
public Double3 scaleAxes(double sx, double sy, double sz) {
return new Double3(x * sx, y * sy, z * sz);
}
public Double3 scale(double s) {
return new Double3(x * s, y * s, z * s);
}
public Double3 inverse() {
return new Double3(-x, -y, -z);
}
}

View File

@@ -0,0 +1,58 @@
package mods.betterfoliage.common.util;
import java.lang.reflect.Field;
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourceManager;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
public class ReflectionUtil {
private ReflectionUtil() {}
@SuppressWarnings("unchecked")
public static Map<String, IResourceManager> getDomainResourceManagers() {
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
Map<String, IResourceManager> result = getField(manager, DeobfNames.SRRM_DRM_MCP, Map.class);
if (result == null) result = getField(manager, DeobfNames.SRRM_DRM_SRGNAME, Map.class);
return result;
}
@SuppressWarnings("unchecked")
public static <T> T getField(Object target, String fieldName, Class<T> resultClass) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(target);
} catch (Exception e) {
return null;
}
}
@SuppressWarnings("unchecked")
public static <T> T getStaticField(Class<?> clazz, String fieldName, Class<T> resultClass) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(null);
} catch (Exception e) {
return null;
}
}
@SuppressWarnings("unchecked")
public static ISimpleBlockRenderingHandler getRenderingHandler(int renderType) {
try {
Field field = RenderingRegistry.class.getDeclaredField("INSTANCE");
field.setAccessible(true);
RenderingRegistry inst = (RenderingRegistry) field.get(null);
field = RenderingRegistry.class.getDeclaredField("blockRenderers");
field.setAccessible(true);
return ((Map<Integer, ISimpleBlockRenderingHandler>) field.get(inst)).get(renderType);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,29 @@
package mods.betterfoliage.loader;
import java.util.Map;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.MCVersion("1.7.2")
public class BetterFoliageLoader implements IFMLLoadingPlugin {
public String[] getASMTransformerClass() {
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
}
public String getModContainerClass() {
return null;
}
public String getSetupClass() {
return null;
}
public void injectData(Map<String, Object> data) {
}
public String getAccessTransformerClass() {
return null;
}
}

View File

@@ -0,0 +1,63 @@
package mods.betterfoliage.loader;
import mods.betterfoliage.common.util.DeobfNames;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
/** Transformer overriding the first line of RenderBlocks.renderBlockByRenderType()
* with the following instruction:<br/><br/>
* int l = mods.betterfoliage.BlockRenderTypeOverride.getRenderType(block);<br/><br/>
*
* @author octarine-noise
*/
public class BetterFoliageTransformer implements IClassTransformer {
Logger log = LogManager.getLogger("BetterFoliageCore");
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
if (transformedName.equals("net.minecraft.client.renderer.RenderBlocks")) {
log.info(String.format("Found class %s", transformedName));
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
for (MethodNode mn : classNode.methods) {
boolean found = false;
boolean obf = false;
if (mn.desc.equals(DeobfNames.RB_RBBRT_SIG_MCP) && (mn.name.equals(DeobfNames.RB_RBBRT_NAME_MCP))) {
found = true;
} else if (mn.desc.equals(DeobfNames.RB_RBBRT_SIG_OBF) && (mn.name.equals(DeobfNames.RB_RBBRT_NAME_OBF))) {
found = true;
obf = true;
}
if (found) {
log.info("Overriding RenderBlocks.renderBlockByRenderType()");
int invokeNodeIdx = 0;
for (int idx = 0; idx < mn.instructions.size(); idx++) if (mn.instructions.get(idx) instanceof MethodInsnNode) {
invokeNodeIdx = idx;
break;
}
mn.instructions.remove(mn.instructions.get(invokeNodeIdx));
MethodInsnNode replacement = new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/BlockRenderTypeOverride", "getRenderType", obf ? DeobfNames.BRTO_GRT_SIG_OBF : DeobfNames.BRTO_GRT_SIG_MCP);
mn.instructions.insertBefore(mn.instructions.get(invokeNodeIdx), replacement);
break;
}
}
ClassWriter writer = new ClassWriter(0);
classNode.accept(writer);
return writer.toByteArray();
}
return basicClass;
}
}

View File

@@ -0,0 +1,13 @@
key.betterfoliage.gui=Open Settings
message.betterfoliage.optionOn=ON
message.betterfoliage.optionOff=OFF
message.betterfoliage.close=Close
message.betterfoliage.betterLeaves=Better Leaves: %s
message.betterfoliage.betterGrass=Better Grass: %s
message.betterfoliage.size=Size
message.betterfoliage.hOffset=H.Offset
message.betterfoliage.vOffset=V.Offset
message.betterfoliage.minHeight=Min.Height
message.betterfoliage.maxHeight=Max.Height

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1,8 @@
[{
"modid": "BetterFoliage",
"name": "Better Foliage",
"version": "$version",
"mcversion": "$mcversion",
"description": "Leafier leaves and grassier grass",
"credits": "Made by octarine-noise"
}]