80 Commits

Author SHA1 Message Date
octarine-noise
5e5b1e136e bump version 2014-09-06 12:16:32 +02:00
octarine-noise
e8d3a38942 fix connected grass appearing on the wrong blocks 2014-09-06 12:16:21 +02:00
octarine-noise
5bdb316161 correct config GUI order 2014-09-06 12:15:47 +02:00
octarine-noise
da7ff77320 fix wrong defaults in config GUI biome lists 2014-09-06 11:57:13 +02:00
octarine-noise
81fbced1c8 misc cleanup 2014-09-06 11:49:16 +02:00
octarine-noise
e32f63ee3a added rising soul particles 2014-09-06 11:25:59 +02:00
octarine-noise
77b1e672c4 fix coral default biomes bug
tweak config order
2014-09-06 11:07:15 +02:00
octarine-noise
755348ad18 added snow on leaves 2014-09-06 11:05:09 +02:00
octarine-noise
47aa5df3c4 tweak default particle mapping 2014-08-28 23:00:31 +02:00
octarine-noise
3f49f6e09b Merge branch 'master' of https://github.com/octarine-noise/BetterFoliage
bump version
2014-08-28 22:49:06 +02:00
octarine-noise
4f7ede5d9e add spruce and jungle particles
fix additional leaf types not loading
2014-08-28 22:47:52 +02:00
octarine-noise
5ad3bbe342 add multiple leaf particle types 2014-08-26 21:22:33 +02:00
octarine-noise
5a5daf6750 move text resource reading to util class 2014-08-26 21:21:45 +02:00
octarine-noise
4bba2b06c2 add hanging vines below netherrack 2014-08-26 21:21:10 +02:00
octarine-noise
adfc14bc62 Update README.md 2014-08-21 22:47:23 +02:00
octarine-noise
85b4880391 store only biome IDs not instances
reorganized utility methods
2014-08-21 15:23:28 +02:00
octarine-noise
4bc6a4a2f9 don't do connected grass textures on snow-covered grass 2014-08-21 15:22:12 +02:00
octarine-noise
98ca2ae284 added beach to default coral biomes 2014-08-21 01:51:28 +02:00
octarine-noise
abb1c5cf22 bump version 2014-08-21 01:46:27 +02:00
octarine-noise
c366e89266 make reeds, algae, coral biomes configurable 2014-08-21 01:39:00 +02:00
octarine-noise
03766727fd removed temperature dependence of algae 2014-08-20 13:02:31 +02:00
octarine-noise
856daacd58 updated strings 2014-08-20 12:49:24 +02:00
octarine-noise
f8092685a8 moved BlockMatcher configuration into main config file 2014-08-20 11:59:25 +02:00
octarine-noise
8b4967584e migrated to Forge built-in GuiConfig 2014-08-20 00:50:25 +02:00
octarine-noise
ce37687a1b add connected grass 2014-08-18 22:57:39 +02:00
octarine-noise
187b29ac41 log stacktrace when default config fails to copy 2014-08-18 22:28:04 +02:00
octarine-noise
7a38ec7c3b Update README.md 2014-08-16 09:29:54 +02:00
octarine-noise
772b509a10 fix texture atlas stitch event handlers firing in wrong order 2014-08-16 09:21:23 +02:00
octarine-noise
ee50ce1dc5 Update README.md 2014-08-16 00:54:02 +02:00
octarine-noise
d2f0d9c0f6 Merge branch 'master' of https://github.com/octarine-noise/BetterFoliage
bump version
2014-08-15 23:25:49 +02:00
octarine-noise
4706b11f98 added texture set by Meringue 2014-08-15 23:24:00 +02:00
octarine-noise
bc3e598b1e fix particles not turning off 2014-08-15 23:10:52 +02:00
octarine-noise
b407e51d19 rotating leaf particles, wind perturbation 2014-08-15 20:24:54 +02:00
octarine-noise
dcbb636902 config GUI for leaf particles 2014-08-15 20:22:52 +02:00
octarine-noise
e88a1ba5ae shift-click in GUI to jump 5 increments 2014-08-15 20:21:34 +02:00
octarine-noise
f107fe0e1a missing class from previous commits (derp) 2014-08-12 23:11:28 +02:00
octarine-noise
97f528d426 add compatibility for custom grass block renderers 2014-08-12 19:04:10 +02:00
octarine-noise
f2aeb870ce fix missing texture NPE 2014-08-12 19:03:37 +02:00
octarine-noise
b365c1cf06 more work on leaf particles 2014-08-12 19:02:48 +02:00
octarine-noise
2b1cab8e62 tons of javadoc and cleanup 2014-08-11 01:38:35 +02:00
octarine-noise
bae6d9b598 add some more SideOnly annotations 2014-08-11 00:14:28 +02:00
octarine-noise
6472353e4a correct mixed up RGB values 2014-08-11 00:10:15 +02:00
octarine-noise
929553692f started work on falling leaf particles 2014-08-10 20:37:43 +02:00
octarine-noise
7babab8385 add some missing SideOnly annotations 2014-08-10 17:04:16 +02:00
octarine-noise
51589bbad4 make leaf texture generator event based and more modular 2014-08-10 17:02:15 +02:00
octarine-noise
6b57840b8f fix possible ClassCastException when leaf block uses non-atlas texture 2014-08-10 16:02:13 +02:00
octarine-noise
c176713f20 use biome colors for short grass without AO 2014-08-10 15:27:49 +02:00
octarine-noise
23abde8000 Merge pull request #5 from Adaptivity/patch-1
Create ru_RU.lang
2014-08-09 15:38:07 +02:00
Anton
21f6569e1b Create ru_RU.lang 2014-08-09 17:28:27 +04:00
octarine-noise
e92e89f86c Update README.md 2014-08-09 00:10:55 +02:00
octarine-noise
728e723d37 render extra leaves even when surrounded by snow 2014-08-08 21:59:51 +02:00
octarine-noise
ab5d1c0f3a add support for arbitrary grass and dirt blocks 2014-08-08 21:54:07 +02:00
octarine-noise
9d9d32b32a bump version 2014-08-06 21:39:36 +02:00
octarine-noise
6623bee39f fix graphical glitch when breaking blocks 2014-08-06 15:50:26 +02:00
octarine-noise
ffa8dd724e made texture generation slightly saner 2014-07-30 18:29:01 +02:00
octarine-noise
bd4a4885fe bump version 2014-07-28 22:04:14 +02:00
octarine-noise
9d59105af1 don't crash coral rendering if textures are missing 2014-07-28 22:03:58 +02:00
octarine-noise
7cdddef1bf new feature: snowed grass 2014-07-28 21:52:23 +02:00
octarine-noise
168fad883d new feature: coral 2014-07-28 20:32:40 +02:00
octarine-noise
572d517828 tweak short grass texture offset 2014-07-28 18:12:20 +02:00
octarine-noise
d4963ec173 new feature: generate short grass texture 2014-07-28 18:06:09 +02:00
octarine-noise
ad83a511fb slightly more robust leaf texture generation 2014-07-27 23:42:22 +02:00
octarine-noise
e351af2369 get rid of annotations in class transformer 2014-07-27 23:38:40 +02:00
octarine-noise
32a88fa824 Update README.md 2014-07-26 10:49:51 +02:00
octarine-noise
ec723113d3 bump version 2014-07-26 10:00:58 +02:00
octarine-noise
4e42f63c36 don't alter ShaderMod special textures 2014-07-25 20:59:21 +02:00
octarine-noise
0daf61583a fixed leaves and grass block data for ShadersMod integration 2014-07-25 15:57:27 +02:00
octarine-noise
504f033c2e only rebuild block ID cache on client world load 2014-07-24 17:59:06 +02:00
octarine-noise
fa099b1b97 fix deadlock issue during class transformation 2014-07-24 17:50:06 +02:00
octarine-noise
b0c0cd0d1b avoid Class.forName() in world loading event 2014-07-24 01:03:35 +02:00
octarine-noise
df69605521 Update README.md 2014-07-23 02:19:18 +02:00
octarine-noise
d3b1d138ba bump version 2014-07-23 01:23:41 +02:00
octarine-noise
07369159b8 hack block ID seen by ShadersMod mid-render
move block ID override to dedicated class
2014-07-23 01:14:47 +02:00
octarine-noise
6700e724a5 emit warning message for unsupported Minecraft versions 2014-07-17 18:44:39 +02:00
octarine-noise
5b34da3e61 move default class lists to resource files 2014-07-17 18:29:19 +02:00
octarine-noise
fc7c9a5381 push down debug log to correct level 2014-07-16 23:28:35 +02:00
octarine-noise
cf7ae0efa1 removed usused internal feature 2014-07-16 23:26:46 +02:00
octarine-noise
04bb240d36 minor comment correction 2014-07-16 23:25:44 +02:00
octarine-noise
91fda1522c Unified 1.7.2 and 1.7.10 versions 2014-07-16 23:25:06 +02:00
octarine-noise
7ca25c0da7 null check for leaf icon 2014-07-14 17:52:33 +02:00
147 changed files with 3328 additions and 1639 deletions

View File

@@ -4,8 +4,6 @@ Minecraft mod that alters the appearance of leaves & grass
More info: http://www.minecraftforum.net/topic/2776217-better-foliage/
Download
Latest Download
========
[BetterFoliage 0.9.4-beta] (http://goo.gl/pNBE23) (MC 1.7.2)
[BetterFoliage 0.9.4-beta] (http://goo.gl/ywT6Xg) (MC 1.7.10)
[BetterFoliage 0.9.11-beta] (http://goo.gl/cTCFLo) (MC 1.7.2 & 1.7.10)

View File

@@ -20,9 +20,9 @@ minecraft {
version = '1.7.2-10.12.2.1147'
}
jar.baseName = 'BetterFoliage-1.7.2'
jar.baseName = 'BetterFoliage'
group = 'com.github.octarine-noise'
version='0.9.5b'
version='1.0.0'
processResources {
inputs.property "version", project.version

View File

@@ -4,11 +4,12 @@ import java.io.File;
import java.util.Map;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.config.BetterFoliageConfig;
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;
@@ -18,14 +19,12 @@ public class BetterFoliage {
public static final String MOD_ID = "BetterFoliage";
public static final String MOD_NAME = "Better Foliage";
public static final String MC_VERSIONS = "[1.7.2]";
public static final String MC_VERSIONS = "[1.7.2,1.7.10]";
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
@Mod.Instance
public static BetterFoliage instance;
public static BetterFoliageConfig config = new BetterFoliageConfig();
public static Logger log;
public static File configDir;
@@ -33,12 +32,17 @@ public class BetterFoliage {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
log = event.getModLog();
if (event.getSide() == Side.CLIENT) {
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
configDir.mkdir();
config.load(new File(configDir, "betterfoliage.cfg"));
BetterFoliageClient.preInit();
}
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
configDir.mkdir();
}
@Mod.EventHandler
public void postInit(FMLPostInitializationEvent event) {
if (event.getSide() == Side.CLIENT) {
Config.getDefaultBiomes();
Config.readConfig(new File(configDir, "betterfoliage.cfg"));
BetterFoliageClient.postInit();
}
}
@NetworkCheckHandler

View File

@@ -1,34 +1,35 @@
package mods.betterfoliage.client;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.impl.EntityFXFallingLeaves;
import mods.betterfoliage.client.render.impl.EntityFXRisingSoul;
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral;
import mods.betterfoliage.client.render.impl.RenderBlockBetterGrass;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad;
import mods.betterfoliage.client.render.impl.RenderBlockBetterMycelium;
import mods.betterfoliage.client.render.impl.RenderBlockBetterNetherrack;
import mods.betterfoliage.client.render.impl.RenderBlockBetterReed;
import mods.betterfoliage.client.resource.BlockTextureGenerator;
import mods.betterfoliage.client.resource.HalfTextureResource;
import mods.betterfoliage.client.resource.ILeafTextureRecognizer;
import mods.betterfoliage.client.resource.LeafTextureGenerator;
import mods.betterfoliage.client.render.impl.RenderBlocksBetterGrassSide;
import mods.betterfoliage.client.resource.LeafGenerator;
import mods.betterfoliage.client.resource.LeafParticleTextures;
import mods.betterfoliage.client.resource.LeafTextureEnumerator;
import mods.betterfoliage.client.resource.ReedGenerator;
import mods.betterfoliage.client.resource.ShortGrassGenerator;
import mods.betterfoliage.client.resource.SoulParticleTextures;
import mods.betterfoliage.common.config.Config;
import net.minecraft.block.Block;
import net.minecraft.block.BlockCarrot;
import net.minecraft.block.BlockCrops;
import net.minecraft.block.BlockDoublePlant;
import net.minecraft.block.BlockLeavesBase;
import net.minecraft.block.BlockPotato;
import net.minecraft.block.BlockReed;
import net.minecraft.block.BlockTallGrass;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.IResource;
import net.minecraft.init.Blocks;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.collect.Maps;
@@ -36,63 +37,55 @@ import com.google.common.collect.Maps;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
public class BetterFoliageClient implements ILeafTextureRecognizer {
public class BetterFoliageClient {
public static ResourceLocation missingTexture = new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png");
public static Map<Integer, IRenderBlockDecorator> decorators = Maps.newHashMap();
public static LeafTextureGenerator leafGenerator;
public static LeafGenerator leafGenerator = new LeafGenerator();
public static LeafParticleTextures leafParticles = new LeafParticleTextures(0);
public static SoulParticleTextures soulParticles = new SoulParticleTextures();
public static WindTracker wind = new WindTracker();
public static BlockMatcher leaves;
public static BlockMatcher crops;
public static void preInit() {
public static void postInit() {
FMLCommonHandler.instance().bus().register(new KeyHandler());
FMLCommonHandler.instance().bus().register(new Config());
BetterFoliage.log.info("Registering renderers");
registerRenderer(new RenderBlockBetterCactus());
registerRenderer(new RenderBlockBetterNetherrack());
registerRenderer(new RenderBlockBetterLilypad());
registerRenderer(new RenderBlockBetterMycelium());
registerRenderer(new RenderBlockBetterLeaves());
registerRenderer(new RenderBlockBetterGrass());
registerRenderer(new RenderBlockBetterCactus());
registerRenderer(new RenderBlockBetterLilypad());
registerRenderer(new RenderBlockBetterReed());
registerRenderer(new RenderBlockBetterAlgae());
leaves = new BlockMatcher(BlockLeavesBase.class.getName(),
"forestry.arboriculture.gadgets.BlockLeaves",
"thaumcraft.common.blocks.BlockMagicalLeaves");
leaves.load(new File(BetterFoliage.configDir, "classesLeaves.cfg"));
crops = new BlockMatcher(BlockCrops.class.getName(),
"-" + BlockCarrot.class.getName(),
"-" + BlockPotato.class.getName(),
BlockTallGrass.class.getName(),
BlockDoublePlant.class.getName(),
BlockReed.class.getName(),
"biomesoplenty.common.blocks.BlockBOPFlower",
"biomesoplenty.common.blocks.BlockBOPFlower2",
"tconstruct.blocks.slime.SlimeTallGrass");
crops.load(new File(BetterFoliage.configDir, "classesCrops.cfg"));
BetterFoliage.log.info("Registering leaf texture generator");
leafGenerator = new LeafTextureGenerator();
MinecraftForge.EVENT_BUS.register(leafGenerator);
leafGenerator.recognizers.add(new BetterFoliageClient());
MinecraftForge.EVENT_BUS.register(new BlockTextureGenerator("bf_reed_bottom", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png")) {
@Override
public IResource getResource(ResourceLocation var1) throws IOException {
return new HalfTextureResource(unwrapResource(var1), true, getMissingResource());
}
});
MinecraftForge.EVENT_BUS.register(new BlockTextureGenerator("bf_reed_top", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png")) {
@Override
public IResource getResource(ResourceLocation var1) throws IOException {
return new HalfTextureResource(unwrapResource(var1), false, getMissingResource());
}
});
registerRenderer(new RenderBlockBetterCoral());
registerRenderer(new RenderBlocksBetterGrassSide());
MinecraftForge.EVENT_BUS.register(new BetterFoliageClient());
MinecraftForge.EVENT_BUS.register(wind);
FMLCommonHandler.instance().bus().register(wind);
MinecraftForge.EVENT_BUS.register(Config.leaves);
MinecraftForge.EVENT_BUS.register(Config.crops);
MinecraftForge.EVENT_BUS.register(Config.dirt);
MinecraftForge.EVENT_BUS.register(Config.grass);
BetterFoliage.log.info("Registering texture generators");
MinecraftForge.EVENT_BUS.register(soulParticles);
MinecraftForge.EVENT_BUS.register(leafGenerator);
MinecraftForge.EVENT_BUS.register(leafParticles);
MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator());
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true));
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_top", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass_snow", missingTexture, true));
ShadersModIntegration.init();
}
public boolean isLeafTexture(TextureAtlasSprite icon) {
public static boolean isLeafTexture(TextureAtlasSprite icon) {
String resourceLocation = icon.getIconName();
if (resourceLocation.startsWith("forestry:leaves/")) return true;
return false;
@@ -109,12 +102,19 @@ public class BetterFoliageClient implements ILeafTextureRecognizer {
return original;
}
public static int getGLSLBlockIdOverride(int original, Block block) {
if (leaves.matchesID(original & 0xFFFF))
return Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | block.getRenderType() << 16;
if (crops.matchesID(original & 0xFFFF))
return Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | block.getRenderType() << 16;
return original;
public static void onRandomDisplayTick(Block block, World world, int x, int y, int z) {
if (Config.soulFXEnabled) {
if (world.getBlock(x, y, z) == Blocks.soul_sand && Math.random() < Config.soulFXChance) {
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXRisingSoul(world, x, y, z));
return;
}
}
if (Config.leafFXEnabled) {
if (Config.leaves.matchesID(block) && world.isAirBlock(x, y - 1, z) && Math.random() < Config.leafFXChance) {
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z));
return;
}
}
}
public static void registerRenderer(IRenderBlockDecorator decorator) {

View File

@@ -1,17 +1,13 @@
package mods.betterfoliage.client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.block.Block;
import net.minecraftforge.common.MinecraftForge;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.world.WorldEvent;
import com.google.common.collect.Sets;
@@ -20,35 +16,13 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class BlockMatcher {
public Set<String> whiteList = Sets.newHashSet();
public Set<String> blackList = Sets.newHashSet();
public Set<Class<?>> whiteList = Sets.newHashSet();
public Set<Class<?>> blackList = Sets.newHashSet();
public Set<Integer> blockIDs = Sets.newHashSet();
public BlockMatcher(String... defaults) {
for (String clazz : defaults) addClass(clazz);
MinecraftForge.EVENT_BUS.register(this);
}
public void addClass(String className) {
if (className.startsWith("-"))
blackList.add(className.substring(1));
else
whiteList.add(className);
}
public boolean matchesClass(Block block) {
for (String className : blackList) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAssignableFrom(block.getClass())) return false;
} catch(ClassNotFoundException e) {}
}
for (String className : whiteList) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAssignableFrom(block.getClass())) return true;
} catch(ClassNotFoundException e) {}
}
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
return false;
}
@@ -60,57 +34,44 @@ public class BlockMatcher {
return blockIDs.contains(Block.blockRegistry.getIDForObject(block));
}
public void load(File file) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
whiteList.clear();
blackList.clear();
String line = reader.readLine();
while(line != null) {
addClass(line.trim());
line = reader.readLine();
}
reader.close();
} catch (FileNotFoundException e) {
saveDefaults(file);
} catch (IOException e) {
BetterFoliage.log.warn(String.format("Error reading configuration: %s", file.getName()));
}
public void updateClassLists(String[] newWhitelist, String[] newBlacklist) {
whiteList.clear();
for(String className : newWhitelist) try {
whiteList.add(Class.forName(className));
} catch(ClassNotFoundException e) {}
blackList.clear();
for(String className : newBlacklist) try {
blackList.add(Class.forName(className));
} catch(ClassNotFoundException e) {}
updateBlockIDs();
}
public void saveDefaults(File file) {
FileWriter writer = null;
try {
writer = new FileWriter(file);
for (String className : whiteList) {
writer.write(className);
writer.write("\n");
}
for (String className : blackList) {
writer.write("-");
writer.write(className);
writer.write("\n");
}
writer.close();
} catch (FileNotFoundException e) {
saveDefaults(file);
} catch (IOException e) {
BetterFoliage.log.warn(String.format("Error writing default configuration: %s", file.getName()));
}
@SuppressWarnings("unchecked")
public void updateBlockIDs() {
blockIDs.clear();
Iterator<Block> iter = Block.blockRegistry.iterator();
while (iter.hasNext()) {
Block block = iter.next();
if (matchesClass(block)) blockIDs.add(Block.blockRegistry.getIDForObject(block));
}
}
public static void loadDefaultLists(ResourceLocation defaults, Collection<String> blacklist, Collection<String> whitelist) {
for (String line : ResourceUtils.getLines(defaults)) {
if (line.startsWith("-"))
blacklist.add(line.substring(1));
else
whitelist.add(line);
}
}
/** Caches block IDs on world load for fast lookup
* @param event
*/
@SuppressWarnings("unchecked")
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
blockIDs.clear();
Iterator<Block> iter = Block.blockRegistry.iterator();
while (iter.hasNext()) {
Block block = iter.next();
if (matchesClass(block)) blockIDs.add(Block.blockRegistry.getIDForObject(block));
}
if (event.world instanceof WorldClient) updateBlockIDs();
}
}

View File

@@ -1,14 +1,14 @@
package mods.betterfoliage.client;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.ConfigGuiFactory;
import net.minecraft.client.settings.KeyBinding;
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.ConfigGuiMain;
import net.minecraft.client.settings.KeyBinding;
@SideOnly(Side.CLIENT)
public class KeyHandler {
@@ -22,6 +22,6 @@ public class KeyHandler {
@SubscribeEvent
public void handleKeyPress(InputEvent.KeyInputEvent event) {
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiMain(null));
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiFactory.ConfigGuiBetterFoliage(null));
}
}

View File

@@ -0,0 +1,86 @@
package mods.betterfoliage.client;
import java.lang.reflect.Field;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.betterfoliage.common.config.Config;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.util.ResourceLocation;
/** Call hooks and helper methods for dealing with Shaders Mod.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class ShadersModIntegration {
private static boolean hasShadersMod = false;
private static int tallGrassEntityData;
private static int leavesEntityData;
private static Field shadersEntityData;
private static Field shadersEntityDataIndex;
/** Hide constructor */
private ShadersModIntegration() {}
public static void init() {
tallGrassEntityData = Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | Blocks.tallgrass.getRenderType() << 16;
leavesEntityData = Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | Blocks.leaves.getRenderType() << 16;
try {
Class<?> classShaders = Class.forName("shadersmodcore.client.Shaders");
shadersEntityData = classShaders.getDeclaredField("entityData");
shadersEntityDataIndex = classShaders.getDeclaredField("entityDataIndex");
hasShadersMod = true;
} catch(Exception e) {
}
}
/** Signal start of grass-type quads
*/
public static void startGrassQuads() {
if (!hasShadersMod) return;
setShadersEntityData(tallGrassEntityData);
}
/** Signal start of leaf-type quads
*/
public static void startLeavesQuads() {
if (!hasShadersMod) return;
setShadersEntityData(leavesEntityData);
}
/** Change the entity data (containing block ID) for the currently rendered block.
* Quads drawn afterwards will have the altered data.
* @param data
*/
private static void setShadersEntityData(int data) {
try {
int[] entityData = (int[]) shadersEntityData.get(null);
int entityDataIndex = shadersEntityDataIndex.getInt(null);
entityData[(entityDataIndex * 2)] = data;
} catch (Exception e) {
}
}
/** Call hook from transformed ShadersMod class
* @param original entity data of currently rendered block
* @param block the block
* @return entity data to use
*/
public static int getBlockIdOverride(int original, Block block) {
if (Config.leaves.matchesID(original & 0xFFFF)) return leavesEntityData;
if (Config.crops.matchesID(original & 0xFFFF)) return tallGrassEntityData;
return original;
}
/**
* @param resource texture resource
* @return true if texture is a normal or specular map
*/
public static boolean isSpecialTexture(ResourceLocation resource) {
return resource.getResourcePath().toLowerCase().endsWith("_n.png") || resource.getResourcePath().toLowerCase().endsWith("_s.png");
}
}

View File

@@ -0,0 +1,67 @@
package mods.betterfoliage.client;
import java.util.List;
import java.util.Map;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class TextureMatcher {
public static class TextureMapping {
public String matchDomain;
public String matchName;
public String textureType;
public TextureMapping(String matchDomain, String matchName, String textureType) {
this.matchDomain = matchDomain;
this.matchName = matchName;
this.textureType = textureType;
}
public boolean matches(TextureAtlasSprite icon) {
ResourceLocation iconLocation = new ResourceLocation(icon.getIconName());
if (matchDomain != null && !matchDomain.equals(iconLocation.getResourceDomain())) return false;
return iconLocation.getResourcePath().contains(matchName);
}
}
public List<TextureMapping> mappings = Lists.newLinkedList();
public Map<IIcon, String> types = Maps.newHashMap();
public String put(TextureAtlasSprite icon) {
if (types.keySet().contains(icon)) return types.get(icon);
for (TextureMapping mapping : mappings) if (mapping.matches(icon)) {
types.put(icon, mapping.textureType);
return mapping.textureType;
}
return null;
}
public String get(IIcon icon) {
return types.get(icon);
}
public void loadMappings(ResourceLocation resource) {
mappings = Lists.newLinkedList();
for (String line : ResourceUtils.getLines(resource)) {
String[] lineSplit = line.split("=");
if (lineSplit.length != 2) continue;
String[] match = lineSplit[0].split(":");
if (match.length == 2) {
mappings.add(new TextureMapping(match[0], match[1], lineSplit[1]));
} else if (match.length == 1) {
mappings.add(new TextureMapping(null, match[0], lineSplit[1]));
}
}
}
}

View File

@@ -0,0 +1,64 @@
package mods.betterfoliage.client;
import java.util.Random;
import mods.betterfoliage.common.config.Config;
import net.minecraft.client.Minecraft;
import net.minecraft.world.World;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
public class WindTracker {
public Random random = new Random();
public double targetX;
public double targetZ;
public double currentX;
public double currentZ;
public long nextChange = 0;
public void changeWind(World world) {
long changeTime = 120;
nextChange = world.getWorldInfo().getWorldTime() + changeTime;
double direction = 2.0 * Math.PI * random.nextDouble();
double speed = Math.abs(random.nextGaussian()) * Config.leafFXWindStrength;
if (world.isRaining()) speed += Math.abs(random.nextGaussian()) * Config.leafFXStormStrength;
targetX = Math.cos(direction) * speed;
targetZ = Math.sin(direction) * speed;
}
@SubscribeEvent
public void handleWorldTick(ClientTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
World world = Minecraft.getMinecraft().theWorld;
if (world == null) return;
// change target wind speed
if (world.getWorldInfo().getWorldTime() >= nextChange) changeWind(world);
// change current wind speed
double changeRate = world.isRaining() ? 0.015 : 0.005;
double deltaX = targetX - currentX;
if (deltaX < -changeRate) deltaX = -changeRate;
if (deltaX > changeRate) deltaX = changeRate;
double deltaZ = targetZ - currentZ;
if (deltaZ < -changeRate) deltaZ = -changeRate;
if (deltaZ > changeRate) deltaZ = changeRate;
currentX += deltaX;
currentZ += deltaZ;
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
if (event.world.isRemote) changeWind(event.world);
}
}

View File

@@ -0,0 +1,91 @@
package mods.betterfoliage.client.gui;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.IConfigElement;
public class AlternateTextBooleanEntry extends GuiConfigEntries.ButtonEntry {
protected final boolean beforeValue;
protected boolean currentValue;
public AlternateTextBooleanEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<Boolean> configElement)
{
super(owningScreen, owningEntryList, configElement);
this.beforeValue = Boolean.valueOf(configElement.get().toString());
this.currentValue = beforeValue;
this.btnValue.enabled = enabled();
updateValueButtonText();
}
@Override
public void updateValueButtonText()
{
this.btnValue.displayString = I18n.format(configElement.getLanguageKey() + "." + String.valueOf(currentValue));
}
@Override
public void valueButtonPressed(int slotIndex)
{
if (enabled())
currentValue = !currentValue;
}
@Override
public boolean isDefault()
{
return currentValue == Boolean.valueOf(configElement.getDefault().toString());
}
@Override
public void setToDefault()
{
if (enabled())
{
currentValue = Boolean.valueOf(configElement.getDefault().toString());
updateValueButtonText();
}
}
@Override
public boolean isChanged()
{
return currentValue != beforeValue;
}
@Override
public void undoChanges()
{
if (enabled())
{
currentValue = beforeValue;
updateValueButtonText();
}
}
@SuppressWarnings("unchecked")
@Override
public boolean saveConfigElement()
{
if (enabled() && isChanged())
{
configElement.set(currentValue);
return configElement.requiresMcRestart();
}
return false;
}
@Override
public Boolean getCurrentValue()
{
return currentValue;
}
@Override
public Boolean[] getCurrentValues()
{
return new Boolean[] { getCurrentValue() };
}
}

View File

@@ -0,0 +1,54 @@
package mods.betterfoliage.client.gui;
import java.util.List;
import mods.betterfoliage.common.util.BiomeUtils;
import net.minecraft.world.biome.BiomeGenBase;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.IConfigElement;
public class BiomeListConfigEntry extends SelectListConfigEntry<BiomeGenBase> {
public BiomeListConfigEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
super(owningScreen, owningEntryList, configElement);
}
@Override
protected List<BiomeGenBase> getBaseSet(String name) {
return BiomeUtils.getAllBiomes();
}
@Override
protected List<BiomeGenBase> getDefaultSelected(String name) {
if (name.equals("reedBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeTempRainFilter(0.4f, null, 0.4f, null)));
if (name.equals("algaeBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeClassNameFilter("river", "ocean")));
if (name.equals("coralBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeClassNameFilter("river", "ocean", "beach")));
return ImmutableList.<BiomeGenBase>of();
}
@Override
protected int getItemId(BiomeGenBase item) {
return item.biomeID;
}
@Override
protected String getItemName(BiomeGenBase item) {
return item.biomeName;
}
@Override
protected String getTooltipLangKey(String name) {
if (name.equals("reedBiomeList")) return "betterfoliage.reeds.biomeSelectTooltip";
if (name.equals("algaeBiomeList")) return "betterfoliage.algae.biomeSelectTooltip";
if (name.equals("coralBiomeList")) return "betterfoliage.coral.biomeSelectTooltip";
return "";
}
}

View File

@@ -1,36 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiAlgae extends ConfigGuiScreenBase {
public ConfigGuiAlgae(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeSize, -100, -40, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMin, -100, -10, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMax, -100, 20, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.algaeChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.algaeChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 100, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
if (BetterFoliage.config.algaeHeightMin.value > BetterFoliage.config.algaeHeightMax.value) BetterFoliage.config.algaeHeightMin.value = BetterFoliage.config.algaeHeightMax.value;
}
}

View File

@@ -2,11 +2,15 @@ package mods.betterfoliage.client.gui;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.config.Config;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import com.google.common.collect.ImmutableSet;
import cpw.mods.fml.client.IModGuiFactory;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -18,7 +22,7 @@ public class ConfigGuiFactory implements IModGuiFactory {
}
public Class<? extends GuiScreen> mainConfigGuiClass() {
return ConfigGuiMain.class;
return ConfigGuiBetterFoliage.class;
}
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
@@ -29,4 +33,9 @@ public class ConfigGuiFactory implements IModGuiFactory {
return null;
}
public static class ConfigGuiBetterFoliage extends GuiConfig {
public ConfigGuiBetterFoliage(GuiScreen parentScreen) {
super(parentScreen, Config.getConfigRootElements(), BetterFoliage.MOD_ID, null, false, false, BetterFoliage.MOD_NAME);
}
}
}

View File

@@ -1,34 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiGrass extends ConfigGuiScreenBase {
public ConfigGuiGrass(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHOffset, -100, -40, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMin, -100, -10, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMax, -100, 20, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
if (BetterFoliage.config.grassHeightMin.value > BetterFoliage.config.grassHeightMax.value) BetterFoliage.config.grassHeightMin.value = BetterFoliage.config.grassHeightMax.value;
}
}

View File

@@ -1,39 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiLeaves extends ConfigGuiScreenBase {
public enum Button {CLOSE, LEAVES_OFFSET_MODE}
public ConfigGuiLeaves(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesVOffset, -100, 20, 200, 50, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
buttonList.add(new GuiButton(Button.LEAVES_OFFSET_MODE.ordinal(), x - 100, y - 40, 200, 20, ""));
}
protected void updateButtons() {
setButtonOptionBoolean(Button.LEAVES_OFFSET_MODE.ordinal(), "message.betterfoliage.leavesMode", BetterFoliage.config.leavesSkew ? "message.betterfoliage.leavesSkew" : "message.betterfoliage.leavesTranslate");
}
@Override
protected void onButtonPress(int id) {
if (id == Button.CLOSE.ordinal()) FMLClientHandler.instance().showGuiScreen(parent);
if (id == Button.LEAVES_OFFSET_MODE.ordinal()) BetterFoliage.config.leavesSkew = !BetterFoliage.config.leavesSkew;
}
}

View File

@@ -1,31 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiLilypad extends ConfigGuiScreenBase {
public ConfigGuiLilypad(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.lilypadHOffset, -100, -40, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.lilypadChance, -100, -10, 200, 50, id++, id++, "message.betterfoliage.flowerChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
}
}

View File

@@ -1,83 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiMain extends ConfigGuiScreenBase {
public enum Button {CLOSE,
TOGGLE_LEAVES, CONFIG_LEAVES,
TOGGLE_GRASS, CONFIG_GRASS,
TOGGLE_CACTUS, CONFIG_CACTUS,
TOGGLE_LILYPAD, CONFIG_LILYPAD,
TOGGLE_REED, CONFIG_REED,
TOGGLE_ALGAE, CONFIG_ALGAE}
public ConfigGuiMain(GuiScreen parent) {
super(parent);
}
@SuppressWarnings("unchecked")
@Override
protected void addButtons(int x, int y) {
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.close")));
buttonList.add(new GuiButton(Button.TOGGLE_LEAVES.ordinal(), x - 100, y - 100, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_LEAVES.ordinal(), x + 60, y - 100, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_GRASS.ordinal(), x - 100, y - 70, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_GRASS.ordinal(), x + 60, y - 70, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_CACTUS.ordinal(), x - 100, y - 40, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_CACTUS.ordinal(), x + 60, y - 40, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_LILYPAD.ordinal(), x - 100, y - 10, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_LILYPAD.ordinal(), x + 60, y - 10, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_REED.ordinal(), x - 100, y + 20, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_REED.ordinal(), x + 60, y + 20, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_ALGAE.ordinal(), x - 100, y + 50, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_ALGAE.ordinal(), x + 60, y + 50, 40, 20, I18n.format("message.betterfoliage.config")));
}
protected void updateButtons() {
setButtonOptionBoolean(Button.TOGGLE_LEAVES.ordinal(), "message.betterfoliage.betterLeaves", BetterFoliage.config.leavesEnabled);
setButtonOptionBoolean(Button.TOGGLE_GRASS.ordinal(), "message.betterfoliage.betterGrass", BetterFoliage.config.grassEnabled);
setButtonOptionBoolean(Button.TOGGLE_CACTUS.ordinal(), "message.betterfoliage.betterCactus", BetterFoliage.config.cactusEnabled);
setButtonOptionBoolean(Button.TOGGLE_LILYPAD.ordinal(), "message.betterfoliage.betterLilypad", BetterFoliage.config.lilypadEnabled);
setButtonOptionBoolean(Button.TOGGLE_REED.ordinal(), "message.betterfoliage.betterReed", BetterFoliage.config.reedEnabled);
setButtonOptionBoolean(Button.TOGGLE_ALGAE.ordinal(), "message.betterfoliage.betterAlgae", BetterFoliage.config.algaeEnabled);
((GuiButton) buttonList.get(Button.CONFIG_CACTUS.ordinal())).enabled = false;
}
@Override
protected void onButtonPress(int id) {
if (id == Button.CLOSE.ordinal()) {
BetterFoliage.config.save();
Minecraft.getMinecraft().renderGlobal.loadRenderers();
FMLClientHandler.instance().showGuiScreen(parent);
}
if (id == Button.TOGGLE_LEAVES.ordinal()) BetterFoliage.config.leavesEnabled = !BetterFoliage.config.leavesEnabled;
if (id == Button.TOGGLE_GRASS.ordinal()) BetterFoliage.config.grassEnabled = !BetterFoliage.config.grassEnabled;
if (id == Button.TOGGLE_CACTUS.ordinal()) BetterFoliage.config.cactusEnabled = !BetterFoliage.config.cactusEnabled;
if (id == Button.TOGGLE_LILYPAD.ordinal()) BetterFoliage.config.lilypadEnabled = !BetterFoliage.config.lilypadEnabled;
if (id == Button.TOGGLE_REED.ordinal()) BetterFoliage.config.reedEnabled = !BetterFoliage.config.reedEnabled;
if (id == Button.TOGGLE_ALGAE.ordinal()) BetterFoliage.config.algaeEnabled = !BetterFoliage.config.algaeEnabled;
if (id== Button.CONFIG_LEAVES.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLeaves(this));
if (id== Button.CONFIG_GRASS.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiGrass(this));
if (id== Button.CONFIG_LILYPAD.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLilypad(this));
if (id== Button.CONFIG_REED.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiReed(this));
if (id== Button.CONFIG_ALGAE.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiAlgae(this));
}
}

View File

@@ -1,35 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiReed extends ConfigGuiScreenBase {
public ConfigGuiReed(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMin, -100, -40, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMax, -100, -10, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.reedChance, -100, 20, 200, 50, id++, id++, "message.betterfoliage.reedChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
if (BetterFoliage.config.reedHeightMin.value > BetterFoliage.config.reedHeightMax.value) BetterFoliage.config.reedHeightMin.value = BetterFoliage.config.reedHeightMax.value;
}
}

View File

@@ -1,75 +0,0 @@
package mods.betterfoliage.client.gui;
import java.util.List;
import mods.betterfoliage.client.gui.widget.IOptionWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import com.google.common.collect.Lists;
public class ConfigGuiScreenBase extends GuiScreen {
protected GuiScreen parent;
protected List<IOptionWidget> widgets = Lists.newLinkedList();
public ConfigGuiScreenBase(GuiScreen parent) {
this.parent = parent;
}
@Override
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
int x = width / 2;
int y = height / 2;
for (IOptionWidget 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 (IOptionWidget widget : widgets) widget.addButtons(buttonList, x, y);
addButtons(x, y);
updateButtons();
}
protected void addButtons(int x, int y) {}
protected void updateButtons() {}
protected void onButtonPress(int id) {}
@Override
protected void actionPerformed(GuiButton button) {
super.actionPerformed(button);
for (IOptionWidget widget : widgets) widget.onAction(button.id);
onButtonPress(button.id);
updateButtons();
}
@SuppressWarnings("unchecked")
protected void setButtonOptionBoolean(int id, String msgKey, boolean option) {
for (GuiButton button : (List<GuiButton>) buttonList) {
if (button.id == id) {
String optionText = option ? (EnumChatFormatting.GREEN + I18n.format("message.betterfoliage.optionOn")) : (EnumChatFormatting.RED + I18n.format("message.betterfoliage.optionOff"));
button.displayString = I18n.format(msgKey, optionText);
break;
}
}
}
@SuppressWarnings("unchecked")
protected void setButtonOptionBoolean(int id, String msgKey, String optionKey) {
for (GuiButton button : (List<GuiButton>) buttonList) {
if (button.id == id) {
button.displayString = I18n.format(msgKey, I18n.format(optionKey));
break;
}
}
}
}

View File

@@ -0,0 +1,27 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.common.util.RenderUtils;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.IConfigElement;
public class NonVerboseArrayEntry extends GuiConfigEntries.ArrayEntry {
@SuppressWarnings("unchecked")
public NonVerboseArrayEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
super(owningScreen, owningEntryList, configElement);
RenderUtils.stripTooltipDefaultText(toolTip);
String shortDefaults = I18n.format("betterfoliage.arrayEntryDisplay", configElement.getDefaults().length);
toolTip.addAll(this.mc.fontRenderer.listFormattedStringToWidth(EnumChatFormatting.AQUA + I18n.format("fml.configgui.tooltip.default", shortDefaults),300));
}
@Override
public void updateValueButtonText() {
this.btnValue.displayString = I18n.format("betterfoliage.arrayEntryDisplay", currentValues.length);
}
}

View File

@@ -0,0 +1,114 @@
package mods.betterfoliage.client.gui;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import mods.betterfoliage.common.util.RenderUtils;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import cpw.mods.fml.client.config.ConfigGuiType;
import cpw.mods.fml.client.config.DummyConfigElement;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.GuiConfigEntries.CategoryEntry;
import cpw.mods.fml.client.config.IConfigElement;
public abstract class SelectListConfigEntry<T> extends CategoryEntry {
List<ItemWrapperElement> children;
List<Integer> notFoundIdList;
@SuppressWarnings("unchecked")
public SelectListConfigEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
super(owningScreen, owningEntryList, configElement);
RenderUtils.stripTooltipDefaultText(toolTip);
}
@Override
protected GuiScreen buildChildScreen()
{
return new GuiConfig(this.owningScreen, createChildElements(), this.owningScreen.modID,
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(), this.owningScreen.title,
((this.owningScreen.titleLine2 == null ? "" : this.owningScreen.titleLine2) + " > " + this.name));
}
protected abstract List<T> getBaseSet(String qualifiedName);
protected abstract List<T> getDefaultSelected(String qualifiedName);
protected abstract int getItemId(T item);
protected abstract String getItemName(T item);
protected abstract String getTooltipLangKey(String qualifiedName);
@SuppressWarnings("rawtypes")
protected List<IConfigElement> createChildElements() {
children = Lists.newArrayList();
List<Integer> idList = Lists.newArrayList();
for (Object id : configElement.getList()) idList.add((Integer) id);
List<T> defaults = getDefaultSelected(configElement.getName());
for(T item : getBaseSet(configElement.getQualifiedName())) {
children.add(new ItemWrapperElement(item, defaults.contains(item), idList.contains(getItemId(item))));
idList.remove(new Integer(getItemId(item)));
}
notFoundIdList = idList;
List<IConfigElement> result = Lists.newArrayList();
result.addAll(children);
return result;
}
@SuppressWarnings("unchecked")
@Override
public boolean saveConfigElement() {
boolean requiresRestart = ((GuiConfig) childScreen).entryList.saveConfigElements();
Set<Integer> idSet = Sets.newHashSet();
for (ItemWrapperElement child : children)
if (Boolean.TRUE.equals(child.getCurrentValue()))
idSet.add(getItemId(child.item));
idSet.addAll(notFoundIdList);
List<Integer> result = Lists.newArrayList(idSet);
Collections.sort(result);
configElement.set(result.toArray());
return requiresRestart;
}
public class ItemWrapperElement extends DummyConfigElement<Boolean> implements IConfigElement<Boolean> {
public T item;
public ItemWrapperElement(T item, boolean defaultValue, boolean currentValue) {
super(getItemName(item), defaultValue, ConfigGuiType.BOOLEAN, getItemName(item));
set(currentValue);
this.item = item;
}
@Override
public String getComment() {
return I18n.format(getTooltipLangKey(configElement.getQualifiedName()), EnumChatFormatting.GOLD + getItemName(item) + EnumChatFormatting.YELLOW);
}
public Boolean getCurrentValue() {
return (Boolean) value;
}
@Override
public void set(Boolean value) {
this.value = value;
}
public void setDefault(Boolean value) {
this.defaultValue = value;
}
}
}

View File

@@ -1,15 +0,0 @@
package mods.betterfoliage.client.gui.widget;
import java.util.List;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
public interface IOptionWidget {
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset);
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor);
public void onAction(int buttonId);
}

View File

@@ -1,53 +0,0 @@
package mods.betterfoliage.client.gui.widget;
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 implements IOptionWidget {
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);
}
public void onAction(int buttonId) {
if (buttonId == idDecrement) option.decrement();
if (buttonId == idIncrement) option.increment();
}
}

View File

@@ -1,50 +0,0 @@
package mods.betterfoliage.client.gui.widget;
import java.util.List;
import mods.betterfoliage.common.config.OptionInteger;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class OptionIntegerWidget implements IOptionWidget {
public OptionInteger option;
public int x;
public int y;
public int width;
public int numWidth;
public int idDecrement;
public int idIncrement;
public String keyLabel;
public OptionIntegerWidget(OptionInteger option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel) {
this.option = option;
this.x = x;
this.y = y;
this.width = width;
this.numWidth = numWidth;
this.idDecrement = idDecrement;
this.idIncrement = idIncrement;
this.keyLabel = keyLabel;
}
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, Integer.toString(option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
}
public void onAction(int buttonId) {
if (buttonId == idDecrement) option.decrement();
if (buttonId == idIncrement) option.increment();
}
}

View File

@@ -6,10 +6,25 @@ import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
/** Block rendering handler that is only used under certain conditions
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public interface IRenderBlockDecorator extends ISimpleBlockRenderingHandler {
/** Initialize necessary helper values
*/
public void init();
/**
* @param blockAccess the world
* @param x
* @param y
* @param z
* @param block
* @param original renderType of the block
* @return true if this renderer should handle this block
*/
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original);
}

View File

@@ -1,13 +1,13 @@
package mods.betterfoliage.client.render;
import mods.betterfoliage.common.util.Utils;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Loads an indexed set of textures
/** Holds an indexed set of textures
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
@@ -16,7 +16,7 @@ public class IconSet {
/** Icon array */
public IIcon[] icons = new IIcon[16];
/** Number of successfully loaded icons*/
/** Number of successfully loaded icons */
public int numLoaded = 0;
/** Resource domain of icons */
@@ -37,7 +37,7 @@ public class IconSet {
// if the path contains a domain, use that to check if the resource exists
String resolvedDomain = path.contains(":") ? new ResourceLocation(path).getResourceDomain() : domain;
String resolvedPath = String.format("textures/blocks/" + (path.contains(":") ? new ResourceLocation(path).getResourcePath() : path) + ".png", idx);
if (Utils.resourceExists(new ResourceLocation(resolvedDomain, resolvedPath)))
if (ResourceUtils.resourceExists(new ResourceLocation(resolvedDomain, resolvedPath)))
icons[numLoaded++] = register.registerIcon(domain + ":" + String.format(path, idx));
}
}

View File

@@ -6,6 +6,7 @@ import java.util.Iterator;
import java.util.List;
import mods.betterfoliage.common.util.Double3;
import mods.betterfoliage.common.util.RenderUtils;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
@@ -13,10 +14,12 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.util.ForgeDirection;
import org.lwjgl.opengl.GL11;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -37,6 +40,9 @@ public class RenderBlockAOBase extends RenderBlocks {
public float red;
public float green;
public float blue;
public void setGray(float value) {
red = value; green = value; blue = value;
}
}
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
@@ -121,6 +127,25 @@ public class RenderBlockAOBase extends RenderBlocks {
return 0;
}
protected void renderWorldBlockBase(int pass, IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return;
}
// render block
setPassCounters(1);
setRenderBoundsFromBlock(block);
ISimpleBlockRenderingHandler handler = RenderUtils.getRenderingHandler(block.getRenderType());
if (handler != null) {
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
} else {
renderStandardBlock(block, x, y, z);
}
}
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;

View File

@@ -0,0 +1,133 @@
package mods.betterfoliage.client.render.impl;
import java.awt.Color;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class EntityFXFallingLeaves extends EntityFX {
protected static double[] cos = new double[64];
protected static double[] sin = new double[64];
static {
for (int idx = 0; idx < 64; idx++) {
cos[idx] = Math.cos(2.0 * Math.PI / 64.0 * idx);
sin[idx] = Math.sin(2.0 * Math.PI / 64.0 * idx);
}
}
public static float biomeBrightnessMultiplier = 0.5f;
public boolean wasOnGround = false;
public boolean isMirrored;
public int particleRotation = 0;
public boolean rotationPositive = true;
public EntityFXFallingLeaves(World world, int x, int y, int z) {
super(world, x + 0.5, y, z + 0.5);
particleMaxAge = MathHelper.floor_double((0.6 + 0.4 * rand.nextDouble()) * Config.leafFXLifetime * 20.0);
isMirrored = (rand.nextInt() & 1) == 1;
motionY = -Config.leafFXSpeed;
particleRotation = rand.nextInt(64);
particleScale = (float) Config.leafFXSize;
Block block = world.getBlock(x, y, z);
IIcon blockIcon = block.getIcon(world, x, y, z, ForgeDirection.DOWN.ordinal());
particleIcon = BetterFoliageClient.leafParticles.getIconSet(blockIcon).get(rand.nextInt(1024));
calculateParticleColor(BetterFoliageClient.leafParticles.getColor(blockIcon), block.colorMultiplier(world, x, y, z));
}
@Override
public void onUpdate() {
super.onUpdate();
particleScale = (float) Config.leafFXSize;
if (rand.nextFloat() > 0.95f) rotationPositive = !rotationPositive;
if (particleAge > particleMaxAge - 20) particleAlpha = 0.05f * (particleMaxAge - particleAge);
if (onGround || wasOnGround) {
motionX = 0.0;
motionZ = 0.0;
motionZ = 0.0;
if (!wasOnGround) {
particleAge = Math.max(particleAge, particleMaxAge - 20);
}
wasOnGround = true;
} else {
motionX = (BetterFoliageClient.wind.currentX + cos[particleRotation] * Config.leafFXPerturb) * Config.leafFXSpeed;
motionZ = (BetterFoliageClient.wind.currentZ + sin[particleRotation] * Config.leafFXPerturb) * Config.leafFXSpeed;
motionY = -Config.leafFXSpeed;
particleRotation = (particleRotation + (rotationPositive ? 1 : -1)) & 63;
}
}
@Override
public void renderParticle(Tessellator tessellator, float partialTickTime, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ)
{
float minU = isMirrored ? particleIcon.getMinU() : particleIcon.getMaxU();
float maxU = isMirrored ? particleIcon.getMaxU() : particleIcon.getMinU();
float minV = particleIcon.getMinV();
float maxV = particleIcon.getMaxV();
float scale = 0.1F * this.particleScale;
Double3 center = new Double3(prevPosX + (posX - prevPosX) * partialTickTime - interpPosX,
prevPosY + (posY - prevPosY) * partialTickTime - interpPosY,
prevPosZ + (posZ - prevPosZ) * partialTickTime - interpPosZ);
Double3 vec1 = new Double3(rotX + rotXY, rotZ, rotYZ + rotXZ).scale(scale);
Double3 vec2 = new Double3(rotX - rotXY, -rotZ, rotYZ - rotXZ).scale(scale);
Double3 vec1Rot = vec1.scale(cos[particleRotation]).add(vec2.scale(sin[particleRotation]));
Double3 vec2Rot = vec1.scale(-sin[particleRotation]).add(vec2.scale(cos[particleRotation]));
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha);
addVertex(tessellator, center.sub(vec1Rot), maxU, maxV);
addVertex(tessellator, center.sub(vec2Rot), maxU, minV);
addVertex(tessellator, center.add(vec1Rot), minU, minV);
addVertex(tessellator, center.add(vec2Rot), minU, maxV);
}
protected void addVertex(Tessellator tessellator, Double3 coord, double u, double v) {
tessellator.addVertexWithUV(coord.x, coord.y, coord.z, u, v);
}
/** Calculates and sets the color of the particle by blending the average color of the block texture with the current biome color
* Blending is done in HSB color space, weighted by the relative saturation of the colors
* @param textureAvgColor average color of the block texture
* @param blockColor biome color at the spawning block
*/
public void calculateParticleColor(int textureAvgColor, int blockColor) {
float[] hsbTexture = Color.RGBtoHSB((textureAvgColor >> 16) & 0xFF, (textureAvgColor >> 8) & 0xFF, textureAvgColor & 0xFF, null);
float[] hsbBlock = Color.RGBtoHSB((blockColor >> 16) & 0xFF, (blockColor >> 8) & 0xFF, blockColor & 0xFF, null);
float weightTex = hsbTexture[1] / (hsbTexture[1] + hsbBlock[1]);
float weightBlock = 1.0f - weightTex;
// avoid circular average for hue for performance reasons
// one of the color components should dominate anyway
float h = weightTex * hsbTexture[0] + weightBlock * hsbBlock[0];
float s = weightTex * hsbTexture[1] + weightBlock * hsbBlock[1];
float b = weightTex * hsbTexture[2] + weightBlock * hsbBlock[2] * biomeBrightnessMultiplier;
int particleColor = Color.HSBtoRGB(h, s, b);
particleBlue = (particleColor & 0xFF) / 256.0f;
particleGreen = ((particleColor >> 8) & 0xFF) / 256.0f;
particleRed = ((particleColor >> 16) & 0xFF) / 256.0f;
}
@Override
public int getFXLayer() {
return 1;
}
}

View File

@@ -0,0 +1,121 @@
package mods.betterfoliage.client.render.impl;
import java.util.Deque;
import java.util.Iterator;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import com.google.common.collect.Lists;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class EntityFXRisingSoul extends EntityFX {
protected static double[] cos = new double[64];
protected static double[] sin = new double[64];
static {
for (int idx = 0; idx < 64; idx++) {
cos[idx] = Math.cos(2.0 * Math.PI / 64.0 * idx);
sin[idx] = Math.sin(2.0 * Math.PI / 64.0 * idx);
}
}
public int initialPhase;
public Deque<Double3> particleTrail = Lists.newLinkedList();
public EntityFXRisingSoul(World world, int x, int y, int z) {
super(world, x + 0.5, y + 1.0, z + 0.5);
motionY = 0.1f;
particleGravity = 0.0f;
particleIcon = BetterFoliageClient.soulParticles.soulHeadIcons.get(rand.nextInt(256));
particleMaxAge = MathHelper.floor_double((0.6 + 0.4 * rand.nextDouble()) * Config.soulFXLifetime * 20.0);
initialPhase = rand.nextInt(64);
}
@Override
public void onUpdate() {
super.onUpdate();
int phase = (initialPhase + particleAge) % 64;
motionY = 0.1f;
motionX = cos[phase] * Config.soulFXPerturb;
motionZ = sin[phase] * Config.soulFXPerturb;
particleTrail.addFirst(new Double3(posX, posY, posZ));
while (particleTrail.size() > Config.soulFXTrailLength) particleTrail.removeLast();
}
@Override
public void renderParticle(Tessellator tessellator, float partialTickTime, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ)
{
Double3 vec1 = new Double3(rotX + rotXY, rotZ, rotYZ + rotXZ);
Double3 vec2 = new Double3(rotX - rotXY, -rotZ, rotYZ - rotXZ);
Iterator<Double3> iter = particleTrail.iterator();
Double3 current, previous;
IIcon renderIcon = particleIcon;
double scale = Config.soulFXHeadSize * 0.25;
float alpha = (float) Config.soulFXOpacity;
if (particleAge > particleMaxAge - 40) alpha *= (particleMaxAge - particleAge) / 40.0f;
int idx = 0;
if (iter.hasNext()) {
previous = iter.next();
while(iter.hasNext()) {
current = previous;
previous = iter.next();
if (idx++ % Config.soulFXTrailDensity == 0) {
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, alpha);
renderParticleQuad(tessellator, partialTickTime, current, previous, vec1, vec2, renderIcon, scale);
}
if (idx == 1) {
// set initial trail particle size and icon after rendering head
scale = Config.soulFXTrailSize * 0.25;
renderIcon = BetterFoliageClient.soulParticles.soulTrackIcon;
}
scale *= Config.soulFXSizeDecay;
alpha *= Config.soulFXOpacityDecay;
}
}
}
protected void renderParticleQuad(Tessellator tessellator, float partialTickTime, Double3 currentPos, Double3 previousPos, Double3 vec1, Double3 vec2, IIcon icon, double scale) {
float minU = icon.getMinU();
float maxU = icon.getMaxU();
float minV = icon.getMinV();
float maxV = icon.getMaxV();
Double3 center = new Double3(previousPos.x + (currentPos.x - previousPos.x) * partialTickTime - interpPosX,
previousPos.y + (currentPos.y - previousPos.y) * partialTickTime - interpPosY,
previousPos.z + (currentPos.z - previousPos.z) * partialTickTime - interpPosZ);
addVertex(tessellator, center.sub(vec1.scale(scale)), maxU, maxV);
addVertex(tessellator, center.sub(vec2.scale(scale)), maxU, minV);
addVertex(tessellator, center.add(vec1.scale(scale)), minU, minV);
addVertex(tessellator, center.add(vec2.scale(scale)), minU, maxV);
}
protected void addVertex(Tessellator tessellator, Double3 coord, double u, double v) {
tessellator.addVertexWithUV(coord.x, coord.y, coord.z, u, v);
}
@Override
public int getFXLayer() {
return 1;
}
}

View File

@@ -6,9 +6,9 @@ import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDirt;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
@@ -30,20 +30,27 @@ public class RenderBlockBetterAlgae extends RenderBlockAOBase implements IRender
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.algaeEnabled) return false;
if (y >= 254 || !(block instanceof BlockDirt)) return false;
if (!Config.algaeEnabled) return false;
if (!Config.dirt.matchesID(block)) return false;
if (!Config.algaeBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
if (blockAccess.getBlock(x, y + 2, z).getMaterial() != Material.water) return false;
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
return terrainVariation < BetterFoliage.config.algaeChance.value;
return terrainVariation < Config.algaePopulation;
}
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
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render dirt block
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
@@ -54,10 +61,10 @@ public class RenderBlockBetterAlgae extends RenderBlockAOBase implements IRender
IIcon renderIcon = algaeIcons.get(variation);
if (renderIcon == null) return true;
double scale = BetterFoliage.config.algaeSize.value * 0.5;
double halfHeight = 0.5 * (BetterFoliage.config.algaeHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.algaeHeightMax.value - BetterFoliage.config.algaeHeightMin.value));
double scale = Config.algaeSize * 0.5;
double halfHeight = 0.5 * (Config.algaeHeightMin + pRand[heightVariation] * (Config.algaeHeightMax - Config.algaeHeightMin));
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.algaeHOffset.value, renderIcon, 0, false);
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], Config.algaeHOffset, renderIcon, 0, false);
return true;
}

View File

@@ -4,6 +4,7 @@ import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@@ -24,23 +25,31 @@ public class RenderBlockBetterCactus extends FakeRenderBlockAOBase implements IR
public IIcon cactusRoundIcon;
public IconSet cactusSideIcons = new IconSet("bettergrassandleaves", "better_cactus_arm_%d");
/** Possible directions for cactus side growth*/
public static ForgeDirection[] cactusDirections = new ForgeDirection[] { ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.EAST, ForgeDirection.WEST};
/** Inner radius of cactus stem */
public static double cactusRadius = 0.4375;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
return BetterFoliage.config.cactusEnabled && block == Blocks.cactus;
return Config.cactusEnabled && block == Blocks.cactus;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.renderBlockCactus(block, x, y, z);
return true;
}
// render cactus center
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
renderStandardBlock(block, x, y, z);
Tessellator.instance.setBrightness(getBrightness(block,x, y, z));
renderCactusCore(block.getBlockTextureFromSide(ForgeDirection.UP.ordinal()),
block.getBlockTextureFromSide(ForgeDirection.NORTH.ordinal()),

View File

@@ -0,0 +1,95 @@
package mods.betterfoliage.client.render.impl;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
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 net.minecraft.world.IBlockAccess;
import net.minecraft.world.gen.NoiseGeneratorSimplex;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class RenderBlockBetterCoral extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet coralCrustIcons = new IconSet("bettergrassandleaves", "better_crust_%d");
public IconSet coralCrossIcons = new IconSet("bettergrassandleaves", "better_coral_%d");
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!Config.coralEnabled) return false;
if (block != Blocks.sand) return false;
if (!Config.coralBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x * 0.1, z * 0.1) + 1.0) * 32.0);
return terrainVariation < Config.coralPopulation;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
blockAccess = world;
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
double offset = pRand[getSemiRandomFromPos(x, y, z, 6)] * Config.coralVOffset;
double halfSize = Config.coralSize * 0.5;
double halfCrustSize = Config.coralCrustSize * 0.5;
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
if (blockAccess.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ).getMaterial() != Material.water) continue;
if (blockAccess.isAirBlock(x + dir.offsetX, y + dir.offsetY + 1, z + dir.offsetZ)) continue;
int variation = getSemiRandomFromPos(x, y, z, dir.ordinal());
if (variation < Config.coralChance) {
IIcon crustIcon = coralCrustIcons.get(variation);
IIcon coralIcon = coralCrossIcons.get(variation);
if (crustIcon != null) renderCoralCrust(blockCenter, dir, offset, halfCrustSize, crustIcon, variation);
if (coralIcon != null) renderCrossedSideQuads(blockCenter.add(new Double3(dir).scale(0.5)), dir,
halfSize, halfSize,
pRot[variation], Config.coralHOffset,
coralIcon, 0, false);
}
}
return true;
}
protected void renderCoralCrust(Double3 blockCenter, ForgeDirection dir, double offset, double scale, IIcon icon, int uvRot) {
Double3 face1 = new Double3(faceDir1[dir.ordinal()]).scale(scale);
Double3 face2 = new Double3(faceDir2[dir.ordinal()]).scale(scale);
Double3 drawCenter = blockCenter.add(new Double3(dir).scale(0.5 + offset));
if (Minecraft.isAmbientOcclusionEnabled()) {
setShadingForFace(dir);
renderQuadWithShading(icon, drawCenter, face1, face2, uvRot, faceAOPP, faceAONP, faceAONN, faceAOPN);
} else {
renderQuad(icon, drawCenter, face1, face2, uvRot);
}
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
coralCrustIcons.registerIcons(event.map);
coralCrossIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d coral crust textures", coralCrustIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d coral textures", coralCrossIcons.numLoaded));
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 2));
}
}

View File

@@ -1,15 +1,16 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.BlockGrass;
import net.minecraft.block.material.Material;
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.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
@@ -22,47 +23,115 @@ import cpw.mods.fml.relauncher.SideOnly;
public class RenderBlockBetterGrass extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet grassIcons = new IconSet("bettergrassandleaves", "better_grass_long_%d");
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
public IconSet snowGrassIcons = new IconSet("bettergrassandleaves", "better_grass_snowed_%d");
public IIcon grassGenIcon;
public IIcon snowGrassGenIcon;
protected IIcon grassTopIcon;
boolean isSnowTop;
protected boolean connectXP, connectXN, connectZP, connectZN;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.grassEnabled) return false;
if (!((block instanceof BlockGrass || block == Blocks.mycelium))) return false;
if (y == 255 || !blockAccess.isAirBlock(x, y + 1, z)) return false;
return true;
return Config.grass.matchesID(block);
}
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
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
// check for connected grass
Material topMaterial = blockAccess.getBlock(x, y + 1, z).getMaterial();
isSnowTop = (topMaterial == Material.snow || topMaterial == Material.craftedSnow);
checkConnectedGrass(x, y, z);
grassTopIcon = block.getIcon(blockAccess, x, y, z, ForgeDirection.UP.ordinal());
int variation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
IIcon renderIcon = (block == Blocks.mycelium) ? myceliumIcons.get(variation) : grassIcons.get(variation);
if (renderIcon == null) return true;
double scale = BetterFoliage.config.grassSize.value * 0.5;
double halfHeight = 0.5 * (BetterFoliage.config.grassHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.grassHeightMax.value - BetterFoliage.config.grassHeightMin.value));
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.grassHOffset.value, renderIcon, 0, false);
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
if (!Config.grassEnabled) return true;
boolean isAirTop = blockAccess.isAirBlock(x, y + 1, z);
if (isSnowTop || isAirTop) {
// render short grass
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
double scale = Config.grassSize * 0.5;
double halfHeight = 0.5 * (Config.grassHeightMin + pRand[heightVariation] * (Config.grassHeightMax - Config.grassHeightMin));
IIcon shortGrassIcon = null;
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
shortGrassIcon = Config.grassUseGenerated ? snowGrassGenIcon : snowGrassIcons.get(iconVariation);
} else {
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
shortGrassIcon = Config.grassUseGenerated ? grassGenIcon : grassIcons.get(iconVariation);
}
if (shortGrassIcon == null) return true;
ShadersModIntegration.startGrassQuads();
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowTop ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[iconVariation], Config.grassHOffset, shortGrassIcon, 0, false);
}
return true;
}
protected void checkConnectedGrass(int x, int y, int z) {
if (isSnowTop) {
connectXP = false;
connectXN = false;
connectZP = false;
connectZN = false;
return;
}
Block blockBelow = blockAccess.getBlock(x, y - 1, z);
if (Config.ctxGrassAggressiveEnabled && (Config.grass.matchesID(blockBelow) || Config.dirt.matchesID(blockBelow))) {
connectXP = true;
connectXN = true;
connectZP = true;
connectZN = true;
} else if (Config.ctxGrassClassicEnabled) {
connectXP = Config.grass.matchesID(blockAccess.getBlock(x + 1, y - 1, z));
connectXN = Config.grass.matchesID(blockAccess.getBlock(x - 1, y - 1, z));
connectZP = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z + 1));
connectZN = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z - 1));
} else {
connectXP = false;
connectXN = false;
connectZP = false;
connectZN = false;
}
}
@Override
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZNeg(block, x, y, z, connectZN ? grassTopIcon : icon);
}
@Override
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZPos(block, x, y, z, connectZP ? grassTopIcon : icon);
}
@Override
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXNeg(block, x, y, z, connectXN ? grassTopIcon : icon);
}
@Override
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXPos(block, x, y, z, connectXP ? grassTopIcon : icon);
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
grassIcons.registerIcons(event.map);
myceliumIcons.registerIcons(event.map);
snowGrassIcons.registerIcons(event.map);
grassGenIcon = event.map.registerIcon("bf_shortgrass:minecraft:tallgrass");
snowGrassGenIcon = event.map.registerIcon("bf_shortgrass_snow:minecraft:tallgrass");
BetterFoliage.log.info(String.format("Found %d short grass textures", grassIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d snowy grass textures", snowGrassIcons.numLoaded));
}
}

View File

@@ -3,47 +3,52 @@ package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
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.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet snowedLeavesIcons = new IconSet("bettergrassandleaves", "better_leaves_snowed_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.leavesEnabled) return false;
if (!Config.leavesEnabled) return false;
if (original > 0 && original < 42) return false;
return BetterFoliageClient.leaves.matchesID(block) && !isBlockSurrounded(blockAccess, x, y, z);
return Config.leaves.matchesID(block) && !isBlockSurrounded(blockAccess, x, y, z);
}
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);
setRenderBoundsFromBlock(block);
if (block.getRenderType() == 0) {
renderStandardBlock(block, x, y, z);
} else {
ISimpleBlockRenderingHandler handler = Utils.getRenderingHandler(block.getRenderType());
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
}
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
// 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());
TextureAtlasSprite blockLeafIcon = null;
try {
blockLeafIcon = (TextureAtlasSprite) block.getIcon(world, x, y, z, ForgeDirection.NORTH.ordinal());
} catch (ClassCastException e) {
}
if (blockLeafIcon == null) {
BetterFoliage.log.debug(String.format("null leaf texture, x:%d, y:%d, z:%d, meta:%d, block:%s", x, y, z, blockAccess.getBlockMetadata(x, y, z), block.getClass().getName()));
return true;
}
IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
if (crossLeafIcon == null) {
return true;
@@ -51,36 +56,59 @@ public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRende
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
double halfSize = 0.5 * BetterFoliage.config.leavesSize.value;
boolean isAirTop = y == 255 || blockAccess.isAirBlock(x, y + 1, z);
boolean isAirBottom = y == 0 || blockAccess.isAirBlock(x, y - 1, z);
double halfSize = 0.5 * Config.leavesSize;
boolean isAirTop = blockAccess.isAirBlock(x, y + 1, z);
boolean isAirBottom = blockAccess.isAirBlock(x, y - 1, z);
boolean isSnowTop = blockAccess.getBlock(x, y + 1, z).getMaterial() == Material.snow;
Tessellator.instance.setBrightness(isAirTop ? getBrightness(block, x, y + 1, z) : (isAirBottom ? getBrightness(block, x, y - 1, z) : getBrightness(block, x, y, z)));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
if (BetterFoliage.config.leavesSkew) {
renderCrossedBlockQuadsSkew(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
pRot[(offsetVariation + 1) & 63].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
Double3 offset1 = pRot[offsetVariation].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
Double3 offset2 = pRot[(offsetVariation + 1) & 63].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
if (Config.leavesSkew) {
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
}
} else {
renderCrossedBlockQuadsTranslate(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
}
}
return true;
}
protected boolean isBlockSurrounded(IBlockAccess blockAccess, 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;
if (isBlockNonSurrounding(blockAccess.getBlock(x + 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x - 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y + 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y - 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z + 1))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z - 1))) return false;
return true;
}
protected boolean isBlockNonSurrounding(Block block) {
return block.getMaterial() == Material.air || block == Blocks.snow_layer;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
snowedLeavesIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d snowed leaves textures", snowedLeavesIcons.numLoaded));
}
}

View File

@@ -4,6 +4,7 @@ import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
@@ -23,14 +24,20 @@ public class RenderBlockBetterLilypad extends FakeRenderBlockAOBase implements I
public IconSet lilypadRoots = new IconSet("bettergrassandleaves", "better_lilypad_roots_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
return BetterFoliage.config.lilypadEnabled && block == Blocks.waterlily;
return Config.lilypadEnabled && block == Blocks.waterlily;
}
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
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.renderBlockLilyPad(block, x, y, z);
return true;
}
// render lilypad block
renderBlockLilyPad(block, x, y, z);
int chanceVariation = getSemiRandomFromPos(x, y, z, 0);
@@ -44,10 +51,10 @@ public class RenderBlockBetterLilypad extends FakeRenderBlockAOBase implements I
null, 0.0,
lilypadRoots.get(iconVariation), 2,
true);
if (chanceVariation < BetterFoliage.config.lilypadChance.value && lilypadFlowers.hasIcons())
if (chanceVariation < Config.lilypadChance && lilypadFlowers.hasIcons())
renderCrossedSideQuads(new Double3(x + 0.5, y + 0.02, z + 0.5), ForgeDirection.UP,
0.2, 0.3,
pRot[offsetVariation], BetterFoliage.config.lilypadHOffset.value,
pRot[offsetVariation], Config.lilypadHOffset,
lilypadFlowers.get(iconVariation), 0,
true);

View File

@@ -0,0 +1,70 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
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.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterMycelium extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!Config.grassEnabled) return false;
if (block != Blocks.mycelium) return false;
if (!blockAccess.isAirBlock(x, y + 1, z) && blockAccess.getBlock(x, y + 1, z) != Blocks.snow_layer) return false;
return true;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
blockAccess = world;
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
boolean isSnowed = blockAccess.getBlock(x, y + 1, z) == Blocks.snow_layer;
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
IIcon renderIcon = myceliumIcons.get(iconVariation);
if (isSnowed || renderIcon == null) return true;
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
double scale = Config.grassSize * 0.5;
double halfHeight = 0.5 * (Config.grassHeightMin + pRand[heightVariation] * (Config.grassHeightMax - Config.grassHeightMin));
if (isSnowed) {
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
}
// render mycelium
ShadersModIntegration.startGrassQuads();
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowed ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[iconVariation], Config.grassHOffset, renderIcon, 0, false);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
myceliumIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
}
}

View File

@@ -0,0 +1,64 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
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.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterNetherrack extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet netherrackVineIcons = new IconSet("bettergrassandleaves", "better_netherrack_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!Config.netherrackEnabled) return false;
if (block != Blocks.netherrack) return false;
if (!blockAccess.isAirBlock(x, y - 1, z)) return false;
return true;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
blockAccess = world;
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
IIcon renderIcon = netherrackVineIcons.get(iconVariation);
if (renderIcon == null) return true;
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
double scale = Config.netherrackSize * 0.5;
double halfHeight = 0.5 * (Config.netherrackHeightMin + pRand[heightVariation] * (Config.netherrackHeightMax - Config.netherrackHeightMin));
// render netherrack vines
ShadersModIntegration.startGrassQuads();
Tessellator.instance.setBrightness(getBrightness(block, x, y - 1, z));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
renderCrossedSideQuads(new Double3(x + 0.5, y, z + 0.5), ForgeDirection.DOWN, scale, halfHeight, pRot[iconVariation], Config.netherrackHOffset, renderIcon, 2, false);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
netherrackVineIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d netherrack vine textures", netherrackVineIcons.numLoaded));
}
}

View File

@@ -3,12 +3,13 @@ package mods.betterfoliage.client.render.impl;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDirt;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
@@ -31,23 +32,18 @@ public class RenderBlockBetterReed extends RenderBlockAOBase implements IRenderB
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.reedEnabled) return false;
if (y >= 254 || !(block instanceof BlockDirt)) return false;
if (!Config.reedEnabled) return false;
if (!(Config.dirt.matchesID(block))) return false;
if (!Config.reedBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
if (!blockAccess.isAirBlock(x, y + 2, z)) return false;
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f || blockAccess.getBiomeGenForCoords(x, z).rainfall < 0.4f) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
return terrainVariation < BetterFoliage.config.reedChance.value;
return terrainVariation < Config.reedPopulation;
}
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
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
@@ -56,11 +52,14 @@ public class RenderBlockBetterReed extends RenderBlockAOBase implements IRenderB
IIcon topIcon = reedTopIcons.get(iconVariation);
if (bottomIcon == null || topIcon == null) return true;
double quarterHeight = 0.25 * (BetterFoliage.config.reedHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.reedHeightMax.value - BetterFoliage.config.reedHeightMin.value));
double quarterHeight = 0.25 * (Config.reedHeightMin + pRand[heightVariation] * (Config.reedHeightMax - Config.reedHeightMin));
Tessellator.instance.setBrightness(getBrightness(block, x, y + 2, z));
Tessellator.instance.setColorOpaque(255, 255, 255);
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, bottomIcon, 0, true);
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + 2.0 * quarterHeight, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, topIcon, 0, true);
// render reeds
ShadersModIntegration.startGrassQuads();
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], Config.reedHOffset, bottomIcon, 0, true);
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + 2.0 * quarterHeight, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], Config.reedHOffset, topIcon, 0, true);
return true;
}

View File

@@ -0,0 +1,53 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.OffsetBlockAccess;
import mods.betterfoliage.common.util.RenderUtils;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.world.IBlockAccess;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Accepts dirt blocks with grass on top if aggressive connected grass is enabled.<br/>
* Renders the grass block in place of dirt.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class RenderBlocksBetterGrassSide extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
@Override
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
Material top2Material = blockAccess.getBlock(x, y + 2, z).getMaterial();
return Config.ctxGrassAggressiveEnabled &&
top2Material != Material.snow &&
top2Material != Material.craftedSnow &&
Config.dirt.matchesID(block) &&
Config.grass.matchesID(blockAccess.getBlock(x, y + 1, z));
}
@Override
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// fake grass block @(0, +1, 0) at render location
IBlockAccess originalBA = renderer.blockAccess;
renderer.blockAccess = new OffsetBlockAccess(world, x, y, z, 0, 1, 0);
Block renderBlock = renderer.blockAccess.getBlock(x, y, z);
boolean result;
ISimpleBlockRenderingHandler handler = RenderUtils.getRenderingHandler(renderBlock.getRenderType());
if (handler != null) {
result = handler.renderWorldBlock(renderer.blockAccess, x, y, z, renderBlock, renderBlock.getRenderType(), renderer);
} else {
result = renderer.renderStandardBlock(renderBlock, x, y, z);
}
// restore renderer to sanity
renderer.blockAccess = originalBA;
return result;
}
}

View File

@@ -6,7 +6,7 @@ import java.util.Map;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.util.Utils;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.IResource;
@@ -21,6 +21,10 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Base class for texture generators. Registers itself as a domain resource manager for the duration of block texture stitching.
* @author octarine-noise
*
*/
@SideOnly(Side.CLIENT)
public abstract class BlockTextureGenerator implements IResourceManager {
@@ -33,32 +37,22 @@ public abstract class BlockTextureGenerator implements IResourceManager {
/** Texture atlas for block textures used in the current run */
public TextureMap blockTextures;
/** Number of textures generated in the current run */
int counter = 0;
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
this.domainName = domainName;
this.missingResource = missingResource;
}
public void onStitchStart(TextureStitchEvent.Pre event) {}
public void onStitchEnd(TextureStitchEvent.Post event) {}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
blockTextures = event.map;
counter = 0;
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
Map<String, IResourceManager> domainManagers = ResourceUtils.getDomainResourceManagers();
if (domainManagers == null) {
BetterFoliage.log.warn("Failed to inject texture generator");
return;
}
domainManagers.put(domainName, this);
onStitchStart(event);
}
@SubscribeEvent
@@ -67,10 +61,8 @@ public abstract class BlockTextureGenerator implements IResourceManager {
if (event.map.getTextureType() != 0) return;
// don't leave a mess
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
Map<String, IResourceManager> domainManagers = ResourceUtils.getDomainResourceManagers();
if (domainManagers != null) domainManagers.remove(domainName);
onStitchEnd(event);
}
public Set<String> getResourceDomains() {
@@ -88,4 +80,13 @@ public abstract class BlockTextureGenerator implements IResourceManager {
public ResourceLocation unwrapResource(ResourceLocation wrapped) {
return new ResourceLocation(wrapped.getResourcePath().substring(16));
}
protected static int blendRGB(int rgbOrig, int rgbBlend, int weightOrig, int weightBlend) {
int r = (((rgbOrig >> 16) & 0xFF) * weightOrig + ((rgbBlend >> 16) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int g = (((rgbOrig >> 8) & 0xFF) * weightOrig + ((rgbBlend >> 8) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int b = ((rgbOrig & 0xFF) * weightOrig + (rgbBlend & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int a = (rgbOrig >> 24) & 0xFF;
int result = (int) (a << 24 | r << 16 | g << 8 | b);
return result;
}
}

View File

@@ -0,0 +1,51 @@
package mods.betterfoliage.client.resource;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.data.IMetadataSection;
/** {@link IResource} for a {@link BufferedImage}
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class BufferedImageResource implements IResource {
/** Raw PNG data*/
protected byte[] data = null;
public BufferedImageResource(BufferedImage image) {
// create PNG image
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", baos);
data = baos.toByteArray();
} catch (IOException e) {
}
}
@Override
public InputStream getInputStream() {
return data == null ? null : new ByteArrayInputStream(data);
}
@Override
public boolean hasMetadata() {
return false;
}
@Override
public IMetadataSection getMetadata(String var1) {
return null;
}
}

View File

@@ -1,74 +0,0 @@
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.FileNotFoundException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
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;
/** {@link IResource} of PNG containing one half (top or bottom) of a given texture resource
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class HalfTextureResource implements IResource {
/** Raw PNG data*/
public byte[] data = null;
/** Resource to return if generation fails */
public IResource fallbackResource;
public HalfTextureResource(ResourceLocation resource, boolean bottom, IResource fallbackResource) {
this.fallbackResource = fallbackResource;
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
try {
// load full texture
ResourceLocation origResource = new ResourceLocation(resource.getResourceDomain(), "textures/blocks/" + resource.getResourcePath());
BufferedImage origImage = ImageIO.read(resourceManager.getResource(origResource).getInputStream());
// draw half texture
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = result.createGraphics();
graphics.drawImage(origImage, 0, bottom ? -origImage.getHeight() / 2 : 0, null);
// create PNG image
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(result, "PNG", baos);
data = baos.toByteArray();
} catch (Exception e) {
// stop log spam with GLSL installed
if (e instanceof FileNotFoundException) return;
BetterFoliage.log.info(String.format("Could not load texture: %s, exception: %s", resource.toString(), e.getClass().getSimpleName()));
}
}
@Override
public InputStream getInputStream() {
return data != null ? new ByteArrayInputStream(data) : fallbackResource.getInputStream();
}
@Override
public boolean hasMetadata() {
return false;
}
@Override
public IMetadataSection getMetadata(String var1) {
return null;
}
}

View File

@@ -1,11 +0,0 @@
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,68 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.ShadersModIntegration;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class LeafGenerator extends LeafGeneratorBase {
/** Name of the default alpha mask to use */
public static String defaultMask = "default";
public LeafGenerator() {
super("bf_leaves", "betterfoliage", "textures/blocks/%s/%s", "betterfoliage:textures/blocks/leafmask_%d_%s.png", BetterFoliageClient.missingTexture);
}
@Override
protected BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException {
// load normal leaf texture
BufferedImage origImage = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(originalWithDirs).getInputStream());
if (origImage.getWidth() != origImage.getHeight()) throw new TextureGenerationException();
int size = origImage.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
if (!ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
// load alpha mask of appropriate size
BufferedImage maskImage = loadLeafMaskImage(defaultMask, size * 2);
int scale = size * 2 / maskImage.getWidth();
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));
}
}
return overlayIcon;
}
@Override
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
super.endTextureReload(event);
if (event.map.getTextureType() != 0) return;
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", drawnCounter));
BetterFoliage.log.info(String.format("Generated %d leaf textures", generatedCounter));
}
}

View File

@@ -0,0 +1,119 @@
package mods.betterfoliage.client.resource;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Texture generator base class for textures based on leaf blocks.
* Supports loading from resource packs instead of generating if available.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public abstract class LeafGeneratorBase extends BlockTextureGenerator {
@SideOnly(Side.CLIENT)
public static class TextureGenerationException extends Exception {
private static final long serialVersionUID = 7339757761980002651L;
};
/** Format string of pre-drawn texture location */
public String handDrawnLocationFormat;
/** Format string of alpha mask location */
public String maskImageLocationFormat;
/** Resource domain name of pre-drawn textures */
public String nonGeneratedDomain;
/** Number of textures generated in the current run */
public int generatedCounter = 0;
/** Number of pre-drawn textures found in the current run */
public int drawnCounter = 0;
public LeafGeneratorBase(String domain, String nonGeneratedDomain, String handDrawnLocationFormat, String maskImageLocationFormat, ResourceLocation missingResource) {
super(domain, missingResource);
this.nonGeneratedDomain = nonGeneratedDomain;
this.handDrawnLocationFormat = handDrawnLocationFormat;
this.maskImageLocationFormat = maskImageLocationFormat;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// check for provided texture
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format(handDrawnLocationFormat, originalNoDirs.getResourceDomain(), originalNoDirs.getResourcePath()));
if (ResourceUtils.resourceExists(handDrawnLocation)) {
drawnCounter++;
return resourceManager.getResource(handDrawnLocation);
}
// generate our own
if (!ResourceUtils.resourceExists(originalWithDirs)) return getMissingResource();
BufferedImage result;
try {
result = generateLeaf(originalWithDirs);
} catch (TextureGenerationException e) {
return getMissingResource();
}
generatedCounter++;
return new BufferedImageResource(result);
}
protected abstract BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException;
/** 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 >= 1) {
try {
maskResource = resourceManager.getResource(new ResourceLocation(String.format(maskImageLocationFormat, size, type)));
} catch (Exception e) {}
size /= 2;
}
try {
return maskResource == null ? null : ImageIO.read(maskResource.getInputStream());
} catch (IOException e) {
return null;
}
}
@Override
@SubscribeEvent
public void handleTextureReload(Pre event) {
super.handleTextureReload(event);
if (event.map.getTextureType() != 0) return;
generatedCounter = 0;
drawnCounter = 0;
}
@SubscribeEvent
public void handleRegisterTexture(LeafTextureFoundEvent event) {
event.blockTextures.registerIcon(new ResourceLocation(domainName, event.icon.getIconName()).toString());
}
}

View File

@@ -0,0 +1,129 @@
package mods.betterfoliage.client.resource;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;
import javax.imageio.ImageIO;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.TextureMatcher;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import com.google.common.collect.Maps;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Holds the textures for the falling leaf particles, and stores average texture color values for leaf textures
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class LeafParticleTextures {
/** Icons for leaf particles */
public Map<String, IconSet> iconSets = Maps.newHashMap();
/** Leaf type mappings */
public TextureMatcher leafTypes = new TextureMatcher();
/** Map of average color values */
public Map<IIcon, Integer> iconColors = Maps.newHashMap();
/** Default color value */
public int defaultColor = 0x208040;
public int loadedSets;
public LeafParticleTextures(int defaultColor) {
this.defaultColor = defaultColor;
}
public IconSet getIconSet(IIcon icon) {
String leafType = leafTypes.get(icon);
if (leafType == null) leafType = "default";
IconSet result = iconSets.get(leafType);
return result.hasIcons() ? result : iconSets.get("default");
}
public int getColor(IIcon icon) {
Integer result = iconColors.get(icon);
return result == null ? defaultColor : result;
}
/** Calculate average color value (in HSB color space) for a texture.
* @param icon texture
*/
protected Integer calculateTextureColor(TextureAtlasSprite icon) {
ResourceLocation locationNoDirs = new ResourceLocation(icon.getIconName());
ResourceLocation locationWithDirs = new ResourceLocation(locationNoDirs.getResourceDomain(), String.format("textures/blocks/%s.png", locationNoDirs.getResourcePath()));
try {
BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(locationWithDirs).getInputStream());
int numOpaque = 0;
float sumHueX = 0.0f;
float sumHueY = 0.0f;
float sumSaturation = 0.0f;
float sumBrightness = 0.0f;
for (int x = 0; x < image.getWidth(); x++) for (int y = 0; y < image.getHeight(); y++) {
int pixel = image.getRGB(x, y);
int alpha = (pixel >> 24) & 0xFF;
float[] hsbVals = Color.RGBtoHSB((pixel >> 16) & 0xFF, (pixel >> 8) & 0xFF, pixel & 0xFF, null);
if (alpha == 255) {
numOpaque++;
sumHueX += Math.cos((hsbVals[0] - 0.5) * 2.0 * Math.PI);
sumHueY += Math.sin((hsbVals[0] - 0.5) * 2.0 * Math.PI);
sumSaturation += hsbVals[1];
sumBrightness += hsbVals[2];
}
}
// average hue as usual for circular values - transform average unit vector back to polar angle
float avgHue = (float) (Math.atan2(sumHueY, sumHueX) / (2.0 * Math.PI) + 0.5);
return Color.HSBtoRGB(avgHue, sumSaturation / numOpaque, sumBrightness / numOpaque);
} catch (IOException e) {
return null;
}
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
loadedSets = 1;
iconSets.clear();
iconColors.clear();
leafTypes.loadMappings(new ResourceLocation("betterfoliage", "leafTextureMappings.cfg"));
IconSet defaultIcons = new IconSet("betterfoliage", "falling_leaf_default_%d");
iconSets.put("default", defaultIcons);
defaultIcons.registerIcons(event.map);
}
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
if (event.map.getTextureType() == 0) BetterFoliage.log.info(String.format("Loaded %d leaf particle sets", loadedSets));
}
@SubscribeEvent
public void handleRegisterTexture(LeafTextureFoundEvent event) {
Integer textureColor = calculateTextureColor(event.icon);
if (textureColor != null) iconColors.put(event.icon, textureColor);
String leafType = leafTypes.put(event.icon);
if (leafType != null && !iconSets.keySet().contains(leafType)) {
IconSet newSet = new IconSet("betterfoliage", String.format("falling_leaf_%s_%%d", leafType));
newSet.registerIcons(event.blockTextures);
iconSets.put(leafType, newSet);
loadedSets++;
}
}
}

View File

@@ -0,0 +1,110 @@
package mods.betterfoliage.client.resource;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.loader.DeobfHelper;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.IIcon;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.collect.Sets;
import cpw.mods.fml.common.eventhandler.Event;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.ReflectionHelper;
import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Enumerates all leaf textures at stitch time and emits an event for each.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class LeafTextureEnumerator implements IIconRegister {
/**{@link Event} that is emitted for each texture belonging to a leaf block.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public static class LeafTextureFoundEvent extends Event {
public TextureMap blockTextures;
public TextureAtlasSprite icon;
private LeafTextureFoundEvent(TextureMap blockTextures, TextureAtlasSprite icon) {
super();
this.blockTextures = blockTextures;
this.icon = icon;
}
}
/** Texture atlas for block textures used in the current run */
public TextureMap blockTextures;
/** Leaf blocks register their textures here.
* @return the originally registered {@link IIcon} already in the atlas
*/
public IIcon registerIcon(String resourceLocation) {
TextureAtlasSprite original = blockTextures.getTextureExtry(resourceLocation);
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, original));
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
*/
@SubscribeEvent(priority=EventPriority.LOWEST)
@SuppressWarnings("unchecked")
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
blockTextures = event.map;
BetterFoliage.log.info("Reloading leaf textures");
// register simple block textures
Iterator<Block> iter = Block.blockRegistry.iterator();
while(iter.hasNext()) {
Block block = iter.next();
if (Config.leaves.matchesClass(block)) {
BetterFoliage.log.debug(String.format("Inspecting leaf block: %s", block.getClass().getName()));
block.registerBlockIcons(this);
}
}
// enumerate all registered textures, find leaf textures among them
try {
Map<String, TextureAtlasSprite> mapAtlas = ReflectionHelper.<Map<String, TextureAtlasSprite>, TextureMap> getPrivateValue(
TextureMap.class, blockTextures, DeobfHelper.transformElementSearge("mapRegisteredSprites"), "mapRegisteredSprites"
);
Set<TextureAtlasSprite> foundLeafTextures = Sets.newHashSet();
for (TextureAtlasSprite icon : mapAtlas.values())
if (BetterFoliageClient.isLeafTexture(icon)) foundLeafTextures.add(icon);
for (TextureAtlasSprite icon : foundLeafTextures) {
BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", icon.getIconName()));
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, icon));
}
} catch (UnableToAccessFieldException e) {
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
}
}
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
if (event.map.getTextureType() != 0) return;
blockTextures = null;
}
}

View File

@@ -1,118 +0,0 @@
package mods.betterfoliage.client.resource;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.util.DeobfNames;
import mods.betterfoliage.common.util.Utils;
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.resources.IResource;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent.Post;
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
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 extends BlockTextureGenerator implements IIconRegister {
public String nonGeneratedDomain = "betterfoliage";
public int nonGeneratedCounter = 0;
public LeafTextureGenerator() {
super("bf_leaves_autogen", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png"));
}
/** List of helpers which can identify leaf textures loaded by alternate means */
public List<ILeafTextureRecognizer> recognizers = Lists.newLinkedList();
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
ResourceLocation original = unwrapResource(resourceLocation);
// check for provided texture
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format("textures/blocks/%s/%s", original.getResourceDomain(), original.getResourcePath()));
if (Utils.resourceExists(handDrawnLocation)) {
nonGeneratedCounter++;
return Minecraft.getMinecraft().getResourceManager().getResource(handDrawnLocation);
}
// generate our own
LeafTextureResource result = new LeafTextureResource(original, getMissingResource());
if (result.data != null) counter++;
return result;
}
/** 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")
@Override
public void onStitchStart(Pre event) {
nonGeneratedCounter = 0;
BetterFoliage.log.info("Reloading leaf textures");
// register simple block textures
Iterator<Block> iter = Block.blockRegistry.iterator();
while(iter.hasNext()) {
Block block = iter.next();
if (BetterFoliageClient.leaves.matchesClass(block)) {
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 = Utils.getField(blockTextures, DeobfNames.TM_MRS_SRG, Map.class);
if (mapAtlas == null) mapAtlas = Utils.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());
}
}
}
@Override
public void onStitchEnd(Post event) {
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", nonGeneratedCounter));
BetterFoliage.log.info(String.format("Generated %d leaf textures", counter));
}
}

View File

@@ -1,116 +0,0 @@
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.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
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";
/** Resource to return if generation fails */
public IResource fallbackResource;
public LeafTextureResource(ResourceLocation resLeaf, IResource fallbackResource) {
this.fallbackResource = fallbackResource;
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
BufferedImage maskImage = loadLeafMaskImage(defaultMask, 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) {
// stop log spam with GLSL installed
if (e instanceof FileNotFoundException) return;
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) : fallbackResource.getInputStream();
}
public boolean hasMetadata() {
return false;
}
public IMetadataSection getMetadata(String var1) {
return null;
}
}

View File

@@ -0,0 +1,44 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
@SideOnly(Side.CLIENT)
public class ReedGenerator extends BlockTextureGenerator {
public boolean isBottom;
public ReedGenerator(String domainName, ResourceLocation missingResource, boolean isBottom) {
super(domainName, missingResource);
this.isBottom = isBottom;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// load full texture
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
// draw half texture
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = result.createGraphics();
graphics.drawImage(origImage, 0, isBottom ? -origImage.getHeight() / 2 : 0, null);
return new BufferedImageResource(result);
}
}

View File

@@ -0,0 +1,56 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.betterfoliage.client.ShadersModIntegration;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
@SideOnly(Side.CLIENT)
public class ShortGrassGenerator extends BlockTextureGenerator {
protected boolean isSnowed = false;
protected int snowOriginalWeight = 2;
protected int snowWhiteWeight = 3;
public ShortGrassGenerator(String domainName, ResourceLocation missingResource, boolean isSnowed) {
super(domainName, missingResource);
this.isSnowed = isSnowed;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// load full texture
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
// draw bottom half of texture
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = result.createGraphics();
graphics.drawImage(origImage, 0, 3 * origImage.getHeight() / 8, null);
// blend with white if snowed
if (isSnowed && !ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
for (int x = 0; x < result.getWidth(); x++) for (int y = 0; y < result.getHeight(); y++) {
result.setRGB(x, y, blendRGB(result.getRGB(x, y), 0xFFFFFF, 2, 3));
}
}
return new BufferedImageResource(result);
}
}

View File

@@ -0,0 +1,33 @@
package mods.betterfoliage.client.resource;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IconSet;
import net.minecraft.util.IIcon;
import net.minecraftforge.client.event.TextureStitchEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Holds the textures for the rising soul particles
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class SoulParticleTextures {
public IIcon soulTrackIcon;
public IconSet soulHeadIcons = new IconSet("bettergrassandleaves", "rising_soul_%d");
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
soulTrackIcon = event.map.registerIcon("bettergrassandleaves:soul_track");
soulHeadIcons.registerIcons(event.map);
}
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
if (event.map.getTextureType() == 0) BetterFoliage.log.info(String.format("Found %d soul particle textures", soulHeadIcons.numLoaded));
}
}

View File

@@ -1,82 +0,0 @@
package mods.betterfoliage.common.config;
public class BetterFoliageConfig extends ConfigBase {
@CfgElement(category="leaves", key="enabled")
public boolean leavesEnabled = true;
@CfgElement(category="leaves", key="skewMode")
public boolean leavesSkew = false;
@CfgElement(category="grass", key="enabled")
public boolean grassEnabled = true;
@CfgElement(category="cactus", key="enabled")
public boolean cactusEnabled = true;
@CfgElement(category="lilypad", key="enabled")
public boolean lilypadEnabled = true;
@CfgElement(category="reed", key="enabled")
public boolean reedEnabled = true;
@CfgElement(category="algae", key="enabled")
public boolean algaeEnabled = true;
@CfgElement(category="leaves", key="horizontalOffset")
public OptionDouble leavesHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
@CfgElement(category="leaves", key="verticalOffset")
public OptionDouble leavesVOffset = new OptionDouble(0.0, 0.4, 0.025, 0.1);
@CfgElement(category="leaves", key="size")
public OptionDouble leavesSize = new OptionDouble(0.75, 1.8, 0.05, 1.4);
@CfgElement(category="grass", key="horizontalOffset")
public OptionDouble grassHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
@CfgElement(category="grass", key="heightMin")
@Limit(max="grassHeightMax")
public OptionDouble grassHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
@CfgElement(category="grass", key="heightMax")
public OptionDouble grassHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
@CfgElement(category="grass", key="size")
public OptionDouble grassSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
@CfgElement(category="lilypad", key="horizontalOffset")
public OptionDouble lilypadHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="lilypad", key="chance")
public OptionInteger lilypadChance = new OptionInteger(0, 64, 1, 16);
@CfgElement(category="reed", key="horizontalOffset")
public OptionDouble reedHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="reed", key="heightMin")
@Limit(max="reedHeightMax")
public OptionDouble reedHeightMin = new OptionDouble(1.5, 3.5, 0.1, 2.0);
@CfgElement(category="reed", key="heightMax")
public OptionDouble reedHeightMax = new OptionDouble(1.5, 3.5, 0.1, 2.5);
@CfgElement(category="reed", key="chance")
public OptionInteger reedChance = new OptionInteger(0, 64, 1, 32);
@CfgElement(category="algae", key="horizontalOffset")
public OptionDouble algaeHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="algae", key="size")
public OptionDouble algaeSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
@CfgElement(category="algae", key="heightMin")
@Limit(max="algaeHeightMax")
public OptionDouble algaeHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
@CfgElement(category="algae", key="heightMax")
public OptionDouble algaeHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
@CfgElement(category="algae", key="chance")
public OptionInteger algaeChance = new OptionInteger(0, 64, 1, 48);
}

View File

@@ -0,0 +1,322 @@
package mods.betterfoliage.common.config;
import java.io.File;
import java.util.List;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BlockMatcher;
import mods.betterfoliage.client.gui.AlternateTextBooleanEntry;
import mods.betterfoliage.client.gui.BiomeListConfigEntry;
import mods.betterfoliage.client.gui.NonVerboseArrayEntry;
import mods.betterfoliage.common.util.BiomeUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import com.google.common.collect.Lists;
import cpw.mods.fml.client.config.IConfigElement;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class Config {
public enum Category {
blockTypes, extraLeaves, shortGrass, cactus, lilypad, reed, algae, coral, netherrack, fallingLeaves, risingSoul, connectedGrass;
}
/** {@link Configuration} object bound to the config file */
public static Configuration rawConfig;
// block matchers
public static BlockMatcher leaves = new BlockMatcher();
public static BlockMatcher crops = new BlockMatcher();
public static BlockMatcher dirt = new BlockMatcher();
public static BlockMatcher grass = new BlockMatcher();
// extracted config values
public static boolean leavesEnabled;
public static boolean leavesSkew;
public static double leavesHOffset;
public static double leavesVOffset;
public static double leavesSize;
public static boolean grassEnabled;
public static boolean grassUseGenerated;
public static double grassHOffset;
public static double grassHeightMin;
public static double grassHeightMax;
public static double grassSize;
public static boolean cactusEnabled;
public static boolean lilypadEnabled;
public static double lilypadHOffset;
public static int lilypadChance;
public static boolean reedEnabled;
public static double reedHOffset;
public static double reedHeightMin;
public static double reedHeightMax;
public static int reedPopulation;
public static boolean algaeEnabled;
public static double algaeHOffset;
public static double algaeSize;
public static double algaeHeightMin;
public static double algaeHeightMax;
public static int algaePopulation;
public static boolean coralEnabled;
public static int coralPopulation;
public static int coralChance;
public static double coralVOffset;
public static double coralHOffset;
public static double coralCrustSize;
public static double coralSize;
public static boolean leafFXEnabled;
public static double leafFXSpeed;
public static double leafFXWindStrength;
public static double leafFXStormStrength;
public static double leafFXSize;
public static double leafFXChance;
public static double leafFXPerturb;
public static double leafFXLifetime;
public static boolean soulFXEnabled;
public static double soulFXChance;
public static double soulFXSpeed;
public static double soulFXPerturb;
public static double soulFXHeadSize;
public static double soulFXTrailSize;
public static double soulFXOpacity;
public static double soulFXSizeDecay;
public static double soulFXOpacityDecay;
public static double soulFXLifetime;
public static int soulFXTrailLength;
public static int soulFXTrailDensity;
public static boolean netherrackEnabled;
public static double netherrackHOffset;
public static double netherrackHeightMin;
public static double netherrackHeightMax;
public static double netherrackSize;
public static boolean ctxGrassClassicEnabled;
public static boolean ctxGrassAggressiveEnabled;
public static List<Integer> reedBiomeList = Lists.newArrayList();
public static List<Integer> algaeBiomeList = Lists.newArrayList();
public static List<Integer> coralBiomeList = Lists.newArrayList();
/** Read the config file
* @param configFile
*/
public static void readConfig(File configFile) {
rawConfig = new Configuration(configFile, true);
updateValues();
if (rawConfig.hasChanged()) rawConfig.save();
}
/** Extract the config properties to static value fields for quick access */
public static void updateValues() {
leavesEnabled = getBoolean(Category.extraLeaves, "enabled", true, "betterfoliage.enabled");
leavesSkew = getBoolean(Category.extraLeaves, "skewMode", false, "betterfoliage.leavesMode");
leavesHOffset = getDouble(Category.extraLeaves, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
leavesVOffset = getDouble(Category.extraLeaves, "vOffset", 0.1, 0.0, 0.4, "betterfoliage.vOffset");
leavesSize = getDouble(Category.extraLeaves, "size", 1.4, 0.75, 1.8, "betterfoliage.size");
grassEnabled = getBoolean(Category.shortGrass, "enabled", true, "betterfoliage.enabled");
grassHOffset = getDouble(Category.shortGrass, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
grassHeightMin = getDouble(Category.shortGrass, "heightMin", 0.6, 0.1, 1.5, "betterfoliage.minHeight");
grassHeightMax = getDouble(Category.shortGrass, "heightMax", 0.8, 0.1, 1.5, "betterfoliage.maxHeight");
grassSize = getDouble(Category.shortGrass, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
grassUseGenerated = getBoolean(Category.shortGrass, "useGenerated", false, "betterfoliage.shortGrass.useGenerated");
grassHeightMin = clampDoubleToMax(Category.shortGrass, "heightMin", "heightMax");
cactusEnabled = getBoolean(Category.cactus, "enabled", true, "betterfoliage.enabled");
lilypadEnabled = getBoolean(Category.lilypad, "enabled", true, "betterfoliage.enabled");
lilypadHOffset = getDouble(Category.lilypad, "hOffset", 0.1, 0.0, 0.25, "betterfoliage.hOffset");
lilypadChance = getInt(Category.lilypad, "flowerChance", 16, 0, 64, "betterfoliage.lilypad.flowerChance");
reedEnabled = getBoolean(Category.reed, "enabled", true, "betterfoliage.enabled");
reedHOffset = getDouble(Category.reed, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
reedHeightMin = getDouble(Category.reed, "heightMin", 2.0, 1.5, 3.5, "betterfoliage.minHeight");
reedHeightMax = getDouble(Category.reed, "heightMax", 2.5, 1.5, 3.5, "betterfoliage.maxHeight");
reedPopulation = getInt(Category.reed, "population", 32, 0, 64, "betterfoliage.population");
reedHeightMin = clampDoubleToMax(Category.reed, "heightMin", "heightMax");
reedBiomeList = getIntList(Category.reed, "reedBiomeList", reedBiomeList, "betterfoliage.reed.biomeList");
algaeEnabled = getBoolean(Category.algae, "enabled", true, "betterfoliage.enabled");
algaeHOffset = getDouble(Category.algae, "hOffset", 0.1, 0.0, 0.25, "betterfoliage.hOffset");
algaeSize = getDouble(Category.algae, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
algaeHeightMin = getDouble(Category.algae, "heightMin", 0.5, 0.1, 1.5, "betterfoliage.minHeight");
algaeHeightMax = getDouble(Category.algae, "heightMax", 1.0, 0.1, 1.5, "betterfoliage.maxHeight");
algaePopulation = getInt(Category.algae, "population", 48, 0, 64, "betterfoliage.population");
algaeHeightMin = clampDoubleToMax(Category.algae, "heightMin", "heightMax");
algaeBiomeList = getIntList(Category.algae, "algaeBiomeList", algaeBiomeList, "betterfoliage.algae.biomeList");
coralEnabled = getBoolean(Category.coral, "enabled", true, "betterfoliage.enabled");
coralHOffset = getDouble(Category.coral, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
coralVOffset = getDouble(Category.coral, "vOffset", 0.1, 0.0, 0.4, "betterfoliage.vOffset");
coralSize = getDouble(Category.coral, "size", 0.7, 0.5, 1.5, "betterfoliage.coral.size");
coralCrustSize = getDouble(Category.coral, "crustSize", 1.4, 0.5, 1.5, "betterfoliage.coral.crustSize");
coralChance = getInt(Category.coral, "chance", 32, 0, 64, "betterfoliage.coral.chance");
coralPopulation = getInt(Category.coral, "population", 48, 0, 64, "betterfoliage.population");
coralBiomeList = getIntList(Category.coral, "coralBiomeList", coralBiomeList, "betterfoliage.coral.biomeList");
leafFXEnabled = getBoolean(Category.fallingLeaves, "enabled", true, "betterfoliage.enabled");
leafFXSpeed = getDouble(Category.fallingLeaves, "speed", 0.05, 0.01, 0.15, "betterfoliage.fallingLeaves.speed");
leafFXWindStrength = getDouble(Category.fallingLeaves, "windStrength", 0.5, 0.1, 2.0, "betterfoliage.fallingLeaves.windStrength");
leafFXStormStrength = getDouble(Category.fallingLeaves, "stormStrength", 0.8, 0.1, 2.0, "betterfoliage.fallingLeaves.stormStrength");
leafFXSize = getDouble(Category.fallingLeaves, "size", 0.75, 0.25, 1.5, "betterfoliage.fallingLeaves.size");
leafFXChance = getDouble(Category.fallingLeaves, "chance", 0.05, 0.001, 1.0, "betterfoliage.fallingLeaves.chance");
leafFXPerturb = getDouble(Category.fallingLeaves, "perturb", 0.25, 0.01, 1.0, "betterfoliage.fallingLeaves.perturb");
leafFXLifetime = getDouble(Category.fallingLeaves, "lifetime", 5.0, 1.0, 15.0, "betterfoliage.fallingLeaves.lifetime");
soulFXEnabled = getBoolean(Category.risingSoul, "enabled", true, "betterfoliage.enabled");
soulFXChance = getDouble(Category.risingSoul, "chance", 0.02, 0.001, 1.0, "betterfoliage.risingSoul.chance");
soulFXSpeed = getDouble(Category.risingSoul, "speed", 0.1, 0.025, 0.25, "betterfoliage.risingSoul.speed");
soulFXPerturb = getDouble(Category.risingSoul, "perturb", 0.05, 0.01, 0.25, "betterfoliage.risingSoul.perturb");
soulFXHeadSize = getDouble(Category.risingSoul, "headSize", 1.0, 0.25, 1.5, "betterfoliage.risingSoul.headSize");
soulFXTrailSize = getDouble(Category.risingSoul, "trailSize", 0.75, 0.25, 1.5, "betterfoliage.risingSoul.trailSize");
soulFXOpacity = getDouble(Category.risingSoul, "opacity", 0.5, 0.05, 1.0, "betterfoliage.risingSoul.opacity");
soulFXSizeDecay = getDouble(Category.risingSoul, "sizeDecay", 0.97, 0.5, 1.0, "betterfoliage.risingSoul.sizeDecay");
soulFXOpacityDecay = getDouble(Category.risingSoul, "opacityDecay", 0.97, 0.5, 1.0, "betterfoliage.risingSoul.opacityDecay");
soulFXLifetime = getDouble(Category.risingSoul, "lifetime", 4.0, 1.0, 15.0, "betterfoliage.risingSoul.lifetime");
soulFXTrailLength = getInt(Category.risingSoul, "trailLength", 48, 2, 128, "betterfoliage.risingSoul.trailLength");
soulFXTrailDensity = getInt(Category.risingSoul, "trailDensity", 3, 1, 16, "betterfoliage.risingSoul.trailDensity");
netherrackEnabled = getBoolean(Category.netherrack, "enabled", true, "betterfoliage.enabled");
netherrackHOffset = getDouble(Category.netherrack, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
netherrackHeightMin = getDouble(Category.netherrack, "heightMin", 0.6, 0.1, 1.5, "betterfoliage.minHeight");
netherrackHeightMax = getDouble(Category.netherrack, "heightMax", 0.8, 0.1, 1.5, "betterfoliage.maxHeight");
netherrackSize = getDouble(Category.netherrack, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
netherrackHeightMin = clampDoubleToMax(Category.netherrack, "heightMin", "heightMax");
ctxGrassClassicEnabled = getBoolean(Category.connectedGrass, "classic", true, "betterfoliage.connectedGrass.classic");
ctxGrassAggressiveEnabled= getBoolean(Category.connectedGrass, "aggressive", true, "betterfoliage.connectedGrass.aggressive");
updateBlockMatcher(dirt, Category.blockTypes, "dirtWhitelist", "betterfoliage.blockTypes.dirtWhitelist", "dirtBlacklist", "betterfoliage.blockTypes.dirtBlacklist", new ResourceLocation("betterfoliage:classesDirtDefault.cfg"));
updateBlockMatcher(grass, Category.blockTypes, "grassWhitelist", "betterfoliage.blockTypes.grassWhitelist", "grassBlacklist", "betterfoliage.blockTypes.grassBlacklist", new ResourceLocation("betterfoliage:classesGrassDefault.cfg"));
updateBlockMatcher(leaves, Category.blockTypes, "leavesWhitelist", "betterfoliage.blockTypes.leavesWhitelist", "leavesBlacklist", "betterfoliage.blockTypes.leavesBlacklist", new ResourceLocation("betterfoliage:classesLeavesDefault.cfg"));
updateBlockMatcher(crops, Category.blockTypes, "cropWhitelist", "betterfoliage.blockTypes.cropWhitelist", "cropBlacklist", "betterfoliage.blockTypes.cropBlacklist", new ResourceLocation("betterfoliage:classesCropDefault.cfg"));
rawConfig.getCategory(Category.extraLeaves.toString()).get("skewMode").setConfigEntryClass(AlternateTextBooleanEntry.class);
rawConfig.getCategory(Category.reed.toString()).get("reedBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
rawConfig.getCategory(Category.algae.toString()).get("algaeBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
rawConfig.getCategory(Category.coral.toString()).get("coralBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
for (Category category : Category.values()) rawConfig.setCategoryLanguageKey(category.toString(), String.format("betterfoliage.%s", category.toString()));
setOrder(Category.extraLeaves, "enabled", "skewMode", "hOffset", "vOffset", "size");
setOrder(Category.shortGrass, "enabled", "useGenerated", "hOffset", "heightMin", "heightMax", "size");
setOrder(Category.lilypad, "enabled", "hOffset", "flowerChance");
setOrder(Category.reed, "enabled", "hOffset", "heightMin", "heightMax", "population", "biomeList");
setOrder(Category.algae, "enabled", "hOffset", "heightMin", "heightMax", "size", "population", "biomeList");
setOrder(Category.coral, "enabled", "hOffset", "vOffset", "size", "crustSize", "population", "chance", "biomeList");
setOrder(Category.netherrack, "enabled", "hOffset", "heightMin", "heightMax", "size");
setOrder(Category.fallingLeaves, "enabled", "chance", "size", "lifetime", "speed", "windStrength", "stormStrength", "perturb");
setOrder(Category.risingSoul, "enabled", "chance", "speed", "perturb", "headSize", "trailSize", "sizeDecay", "opacity", "opacityDecay", "lifetime", "trailLength", "trailDensity");
setOrder(Category.connectedGrass, "classic", "aggressive");
}
public static void getDefaultBiomes() {
List<BiomeGenBase> biomes = BiomeUtils.getAllBiomes();
reedBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeTempRainFilter(0.4f, null, 0.4f, null));
algaeBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeClassNameFilter("river", "ocean"));
coralBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeClassNameFilter("river", "ocean", "beach"));
}
@SuppressWarnings("rawtypes")
public static List<IConfigElement> getConfigRootElements() {
List<IConfigElement> result = Lists.newLinkedList();
for (Category category : Category.values()) {
ConfigElement<?> element = new ConfigElement(rawConfig.getCategory(category.toString()));
result.add(element);
}
return result;
}
protected static double getDouble(Category category, String key, double defaultValue, double min, double max, String langKey) {
Property prop = rawConfig.get(category.toString(), key, defaultValue);
prop.setMinValue(min);
prop.setMaxValue(max);
prop.setLanguageKey(langKey);
return prop.getDouble();
}
protected static double clampDoubleToMax(Category category, String keySmaller, String keyLarger) {
ConfigCategory cfgCat = rawConfig.getCategory(category.toString());
Property smaller = cfgCat.get(keySmaller);
Property larger = cfgCat.get(keyLarger);
if (smaller.getDouble() > larger.getDouble()) smaller.set(larger.getDouble());
return smaller.getDouble();
}
protected static int getInt(Category category, String key, int defaultValue, int min, int max, String langKey) {
Property prop = rawConfig.get(category.toString(), key, defaultValue);
prop.setMinValue(min);
prop.setMaxValue(max);
prop.setLanguageKey(langKey);
return prop.getInt();
}
protected static List<Integer> getIntList(Category category, String key, List<Integer> defaultList, String langKey) {
int[] defaults = new int[]{};
if (defaultList != null) {
defaults = new int[defaultList.size()];
int idx = 0;
for (Integer value : defaultList) defaults[idx++] = value;
}
Property prop = rawConfig.get(category.toString(), key, defaults);
prop.setLanguageKey(langKey);
int[] values = prop.getIntList();
List<Integer> result = Lists.newArrayListWithCapacity(values.length);
for (int value : values) result.add(value);
return result;
}
protected static boolean getBoolean(Category category, String key, boolean defaultValue, String langKey) {
Property prop = rawConfig.get(category.toString(), key, defaultValue);
prop.setLanguageKey(langKey);
return prop.getBoolean();
}
protected static void updateBlockMatcher(BlockMatcher bm, Category category, String whitelistKey, String whitelistLangKey, String blacklistKey, String blacklistLangKey, ResourceLocation defaults) {
List<String> defaultWhitelist = Lists.newLinkedList();
List<String> defaultBlacklist = Lists.newLinkedList();
BlockMatcher.loadDefaultLists(defaults, defaultBlacklist, defaultWhitelist);
Property whitelist = rawConfig.get(category.toString(), whitelistKey, defaultWhitelist.toArray(new String[]{}));
Property blacklist = rawConfig.get(category.toString(), blacklistKey, defaultBlacklist.toArray(new String[]{}));
whitelist.setLanguageKey(whitelistLangKey);
blacklist.setLanguageKey(blacklistLangKey);
whitelist.setConfigEntryClass(NonVerboseArrayEntry.class);
blacklist.setConfigEntryClass(NonVerboseArrayEntry.class);
bm.updateClassLists(whitelist.getStringList(), blacklist.getStringList());
}
protected static void setOrder(Category category, String... properties) {
rawConfig.setCategoryPropertyOrder(category.toString(), Lists.newArrayList(properties));
}
@SubscribeEvent
public void handleConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) {
if (event.modID.equals(BetterFoliage.MOD_ID)) {
updateValues();
if (rawConfig.hasChanged()) rawConfig.save();
Minecraft.getMinecraft().renderGlobal.loadRenderers();
}
}
}

View File

@@ -1,128 +0,0 @@
package mods.betterfoliage.common.config;
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
public class ConfigBase {
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface CfgElement {
String category();
String key();
String comment() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface Limit {
String min() default "";
String max() default "";
}
protected Configuration config;
public void load(File configFile) {
config = new Configuration(configFile);
config.load();
for (Field field : getClass().getDeclaredFields()) {
CfgElement annot = field.getAnnotation(CfgElement.class);
if (annot == null) continue;
field.setAccessible(true);
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
try {
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
field.setBoolean(this, prop.getBoolean(field.getBoolean(this)));
} catch (Exception e) {
}
} else if (field.getType().equals(OptionInteger.class)) {
try {
OptionInteger option = (OptionInteger) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
option.value = prop.getInt(option.value);
} catch (Exception e) {
}
} else if (field.getType().equals(OptionDouble.class)) {
try {
OptionDouble option = (OptionDouble) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
option.value = prop.getDouble(option.value);
} catch (Exception e) {
}
}
}
validateLimits();
if (config.hasChanged()) config.save();
}
protected void validateLimits() {
for (Field fieldThis : getClass().getDeclaredFields()) {
Limit annot = fieldThis.getAnnotation(Limit.class);
if (annot == null) continue;
try {
Field fieldMin = annot.min().isEmpty() ? null : getClass().getDeclaredField(annot.min());
Field fieldMax = annot.max().isEmpty() ? null : getClass().getDeclaredField(annot.max());
fieldThis.setAccessible(true);
fieldMin.setAccessible(true);
fieldMax.setAccessible(true);
if (fieldThis.getType().equals(OptionInteger.class)) {
OptionInteger optionThis = (OptionInteger) fieldThis.get(this);
OptionInteger optionMin = fieldMin == null ? null : (OptionInteger) fieldMin.get(this);
OptionInteger optionMax = fieldMax == null ? null : (OptionInteger) fieldMax.get(this);
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
} else if (fieldThis.getType().equals(OptionDouble.class)) {
OptionDouble optionThis = (OptionDouble) fieldThis.get(this);
OptionDouble optionMin = fieldMin == null ? null : (OptionDouble) fieldMin.get(this);
OptionDouble optionMax = fieldMax == null ? null : (OptionDouble) fieldMax.get(this);
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
}
} catch (Exception e) {}
}
}
public void save() {
for (Field field : getClass().getDeclaredFields()) {
CfgElement annot = field.getAnnotation(CfgElement.class);
if (annot == null) continue;
field.setAccessible(true);
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
try {
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
prop.set(field.getBoolean(this));
} catch (Exception e) {
}
} else if (field.getType().equals(OptionInteger.class)) {
try {
OptionInteger option = (OptionInteger) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
prop.set(option.value);
} catch (Exception e) {
}
} else if (field.getType().equals(OptionDouble.class)) {
try {
OptionDouble option = (OptionDouble) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
prop.set(option.value);
} catch (Exception e) {
}
}
}
config.save();
}
}

View File

@@ -1,26 +0,0 @@
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

@@ -1,26 +0,0 @@
package mods.betterfoliage.common.config;
public class OptionInteger {
public int min;
public int max;
public int step;
public int value;
public OptionInteger(int min, int max, int step, int 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,87 @@
package mods.betterfoliage.common.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.minecraft.world.biome.BiomeGenBase;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
public class BiomeUtils {
/** Hide constructor */
private BiomeUtils() {}
public static List<BiomeGenBase> getAllBiomes() {
List<BiomeGenBase> biomes = Lists.newArrayList(Collections2.filter(Arrays.asList(BiomeGenBase.getBiomeGenArray()), Predicates.notNull()));
Collections.sort(biomes, new Comparator<BiomeGenBase>() {
@Override
public int compare(BiomeGenBase o1, BiomeGenBase o2) {
return o1.biomeName.compareTo(o2.biomeName);
}
});
return biomes;
}
public static Predicate<BiomeGenBase> biomeIdFilter(final List<Integer> biomeIdList) {
return new Predicate<BiomeGenBase>() {
public boolean apply(BiomeGenBase biome) {
return biomeIdList.contains(biome.biomeID);
}
};
}
public static Function<BiomeGenBase, Integer> biomeIdTransform() {
return new Function<BiomeGenBase, Integer>() {
public Integer apply(BiomeGenBase input) {
return input.biomeID;
}
};
}
public static Predicate<BiomeGenBase> biomeTempRainFilter(final Float minTemp, final Float maxTemp, final Float minRain, final Float maxRain) {
return new Predicate<BiomeGenBase>() {
public boolean apply(BiomeGenBase biome) {
if (minTemp != null && biome.temperature < minTemp) return false;
if (maxTemp != null && biome.temperature > maxTemp) return false;
if (minRain != null && biome.rainfall < minRain) return false;
if (maxRain != null && biome.rainfall > maxRain) return false;
return true;
}
};
}
public static Predicate<BiomeGenBase> biomeClassFilter(final Class<?>... classList) {
return new Predicate<BiomeGenBase>() {
public boolean apply(BiomeGenBase biome) {
for (Class<?> clazz : classList)
if (clazz.isAssignableFrom(biome.getClass()) || clazz.equals(biome.getClass()))
return true;
return false;
}
};
}
public static Predicate<BiomeGenBase> biomeClassNameFilter(final String... names) {
return new Predicate<BiomeGenBase>() {
public boolean apply(BiomeGenBase biome) {
for (String name : names) if (biome.getClass().getName().toLowerCase().contains(name.toLowerCase())) return true;
return false;
}
};
}
public static List<Integer> getFilteredBiomeIds(Collection<BiomeGenBase> biomes, Predicate<BiomeGenBase> filter) {
return Lists.newArrayList(Collections2.transform(Collections2.filter(biomes, filter), biomeIdTransform()));
}
}

View File

@@ -1,71 +0,0 @@
package mods.betterfoliage.common.util;
public class DeobfNames {
private DeobfNames() {}
/** MCP name of RenderBlocks */
public static final String RB_NAME_MCP = "net/minecraft/client/renderer/RenderBlocks";
/** Obfuscated name of RenderBlocks */
public static final String RB_NAME_OBF = "ble";
/** MCP name of RenderBlocks.blockAccess */
public static final String RB_BA_NAME_MCP = "blockAccess";
/** Obfuscated name of RenderBlocks.blockAccess */
public static final String RB_BA_NAME_OBF = "a";
/** MCP signature of RenderBlocks.blockAccess */
public static final String RB_BA_SIG_MCP = "Lnet/minecraft/world/IBlockAccess;";
/** Obfuscated signature of RenderBlocks.blockAccess */
public static final String RB_BA_SIG_OBF = "Lafx;";
/** 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 BetterFoliageClient.getRenderTypeOverride() */
public static final String BFC_GRTO_SIG_MCP = "(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I";
/** Obfuscated signature of BetterFoliageClient.getRenderTypeOverride() */
public static final String BFC_GRTO_SIG_OBF = "(Lafx;IIILahu;I)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";
/** MCP signature of Shaders.pushEntity() */
public static final String SHADERS_PE_SIG_MCP = "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
/** Obfuscated signature of Shaders.pushEntity() */
public static final String SHADERS_PE_SIG_OBF = "(Lble;Lahu;III)V";
/** MCP signature of BetterFoliageClient.getGLSLBlockIdOverride() */
public static final String BFC_GLSLID_SIG_MCP = "(ILnet/minecraft/block/Block;)I";
/** Obfuscated signature of BetterFoliageClient.getGLSLBlockIdOverride() */
public static final String BFC_GLSLID_SIG_OBF = "(ILahu;)I";
}

View File

@@ -2,6 +2,9 @@ package mods.betterfoliage.common.util;
import net.minecraftforge.common.util.ForgeDirection;
/** Immutable 3D vector of double precision.
* @author octarine-noise
*/
public class Double3 {
public final double x;
@@ -24,6 +27,10 @@ public class Double3 {
return new Double3(x + other.x, y + other.y, z + other.z);
}
public Double3 sub(Double3 other) {
return new Double3(x - other.x, y - other.y, z - other.z);
}
public Double3 add(double x, double y, double z) {
return new Double3(this.x + x, this.y + y, this.z + z);
}

View File

@@ -0,0 +1,100 @@
package mods.betterfoliage.common.util;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Vec3Pool;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.util.ForgeDirection;
/** {@link IBlockAccess} wrapper that applies an offset for a single target coordinate for all rendering-related methods.
* Returns normal values for all other coordinates.
* @author octarine-noise
*
*/
public class OffsetBlockAccess implements IBlockAccess {
public IBlockAccess source;
public int xTarget, yTarget, zTarget;
public int xOffset, yOffset, zOffset;
public OffsetBlockAccess(IBlockAccess source, int x, int y, int z, int xOffset, int yOffset, int zOffset) {
this.source = source;
this.xTarget = x;
this.yTarget = y;
this.zTarget = z;
this.xOffset = xOffset;
this.yOffset = yOffset;
this.zOffset = zOffset;
}
public Block getBlock(int x, int y, int z) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.getBlock(x + xOffset, y + yOffset, z + zOffset);
else
return source.getBlock(x, y, z);
}
public TileEntity getTileEntity(int x, int y, int z) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.getTileEntity(x + xOffset, y + yOffset, z + zOffset);
else
return source.getTileEntity(x, y, z);
}
@SideOnly(Side.CLIENT)
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int min) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.getLightBrightnessForSkyBlocks(x + xOffset, y + yOffset, z + zOffset, min);
else
return source.getLightBrightnessForSkyBlocks(x, y, z, min);
}
public int getBlockMetadata(int x, int y, int z) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.getBlockMetadata(x + xOffset, y + yOffset, z + zOffset);
else
return source.getBlockMetadata(x, y, z);
}
public boolean isAirBlock(int x, int y, int z) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.isAirBlock(x + xOffset, y + yOffset, z + zOffset);
else
return source.isAirBlock(x, y, z);
}
@SideOnly(Side.CLIENT)
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
return source.getBiomeGenForCoords(x, z);
}
@SideOnly(Side.CLIENT)
public int getHeight() {
return source.getHeight();
}
@SideOnly(Side.CLIENT)
public boolean extendedLevelsInChunkCache() {
return source.extendedLevelsInChunkCache();
}
@Deprecated
public Vec3Pool getWorldVec3Pool() {
return source.getWorldVec3Pool();
}
public int isBlockProvidingPowerTo(int x, int y, int z, int dir) {
return source.isBlockProvidingPowerTo(x, y, z, dir);
}
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
if (x == xTarget && y == yTarget && z == zTarget)
return source.isSideSolid(x + xOffset, y + yOffset, z + zOffset, side, _default);
else
return source.isSideSolid(x, y, z, side, _default);
}
}

View File

@@ -0,0 +1,45 @@
package mods.betterfoliage.common.util;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.minecraft.util.EnumChatFormatting;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
public class RenderUtils {
/** Hide constructor */
private RenderUtils() {}
/** Retrieve a specific rendering handler from the registry
* @param renderType render type of block
* @return {@link ISimpleBlockRenderingHandler} if defined, null otherwise
*/
@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;
}
}
public static void stripTooltipDefaultText(List<String> tooltip) {
boolean defaultRows = false;
Iterator<String> iter = tooltip.iterator();
while(iter.hasNext()) {
if (iter.next().startsWith(EnumChatFormatting.AQUA.toString())) defaultRows = true;
if (defaultRows) iter.remove();
}
}
}

View File

@@ -0,0 +1,94 @@
package mods.betterfoliage.common.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.loader.DeobfHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.SimpleReloadableResourceManager;
import net.minecraft.util.ResourceLocation;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import cpw.mods.fml.relauncher.ReflectionHelper;
import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException;
public class ResourceUtils {
/** Hide constructor */
private ResourceUtils() {}
/**
* @return (({@link SimpleReloadableResourceManager}) Minecraft.getMinecraft().getResourceManager()).domainResourceManagers
*/
public static Map<String, IResourceManager> getDomainResourceManagers() {
try {
return ReflectionHelper.<Map<String, IResourceManager>, SimpleReloadableResourceManager> getPrivateValue(
SimpleReloadableResourceManager.class, (SimpleReloadableResourceManager) Minecraft.getMinecraft().getResourceManager(), DeobfHelper.transformElementSearge("domainResourceManagers"), "domainResourceManagers"
);
} catch (UnableToAccessFieldException e) {
return null;
}
}
/** Check for the existence of a {@link IResource}
* @param resourceLocation
* @return true if the resource exists
*/
public static boolean resourceExists(ResourceLocation resourceLocation) {
try {
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
if (resource != null) return true;
} catch (IOException e) {
}
return false;
}
/** Copy a text file from a resource to the filesystem
* @param resourceLocation resource location of text file
* @param target target file
* @throws IOException
*/
public static void copyFromTextResource(ResourceLocation resourceLocation, File target) throws IOException {
IResource defaults = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
BufferedReader reader = new BufferedReader(new InputStreamReader(defaults.getInputStream(), Charsets.UTF_8));
FileWriter writer = new FileWriter(target);
String line = reader.readLine();
while(line != null) {
writer.write(line + System.lineSeparator());
line = reader.readLine();
}
reader.close();
writer.close();
}
public static Iterable<String> getLines(ResourceLocation resource) {
BufferedReader reader = null;
List<String> result = Lists.newArrayList();
try {
reader = new BufferedReader(new InputStreamReader(Minecraft.getMinecraft().getResourceManager().getResource(resource).getInputStream(), Charsets.UTF_8));
String line = reader.readLine();
while(line != null) {
line = line.trim();
if (!line.isEmpty() && !line.startsWith("//")) result.add(line);
line = reader.readLine();
}
reader.close();
} catch (Exception e) {
BetterFoliage.log.warn(String.format("Error reading resource %s", resource.toString()));
return Lists.newArrayList();
}
return result;
}
}

View File

@@ -1,70 +0,0 @@
package mods.betterfoliage.common.util;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
public class Utils {
private Utils() {}
@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;
}
}
public static boolean resourceExists(ResourceLocation resourceLocation) {
try {
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
if (resource != null) return true;
} catch (IOException e) {
}
return false;
}
}

View File

@@ -4,7 +4,6 @@ import java.util.Map;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.MCVersion("1.7.2")
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
public class BetterFoliageLoader implements IFMLLoadingPlugin {

View File

@@ -1,48 +1,79 @@
package mods.betterfoliage.loader;
import mods.betterfoliage.common.util.DeobfNames;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
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.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class BetterFoliageTransformer extends EZTransformerBase {
import com.google.common.collect.ImmutableList;
@MethodTransform(className="net.minecraft.client.renderer.RenderBlocks",
obf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_OBF, signature=DeobfNames.RB_RBBRT_SIG_OBF),
deobf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_MCP, signature=DeobfNames.RB_RBBRT_SIG_MCP),
log="Applying RenderBlocks.renderBlockByRenderType() render type ovverride")
public void handleRenderBlockOverride(MethodNode method, boolean obf) {
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
insertAfter(method.instructions, storeRenderType,
new VarInsnNode(Opcodes.ALOAD, 0),
obf ? new FieldInsnNode(Opcodes.GETFIELD, DeobfNames.RB_NAME_OBF, DeobfNames.RB_BA_NAME_OBF, DeobfNames.RB_BA_SIG_OBF) :
new FieldInsnNode(Opcodes.GETFIELD, DeobfNames.RB_NAME_MCP, DeobfNames.RB_BA_NAME_MCP, DeobfNames.RB_BA_SIG_MCP),
new VarInsnNode(Opcodes.ILOAD, 2),
new VarInsnNode(Opcodes.ILOAD, 3),
new VarInsnNode(Opcodes.ILOAD, 4),
new VarInsnNode(Opcodes.ALOAD, 1),
new VarInsnNode(Opcodes.ILOAD, 5),
obf ? new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", DeobfNames.BFC_GRTO_SIG_OBF) :
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", DeobfNames.BFC_GRTO_SIG_MCP),
new VarInsnNode(Opcodes.ISTORE, 5)
);
import cpw.mods.fml.relauncher.FMLInjectionData;
public class BetterFoliageTransformer implements IClassTransformer {
protected Iterable<MethodTransformerBase> transformers = ImmutableList.<MethodTransformerBase>of(
new TransformRenderBlockOverride(),
new TransformShaderModBlockOverride(),
new TransformRandomDisplayTick()
);
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
public BetterFoliageTransformer() {
String mcVersion = FMLInjectionData.data()[4].toString();
if (!ImmutableList.<String>of("1.7.2", "1.7.10").contains(mcVersion))
logger.warn(String.format("Unsupported Minecraft version %s", mcVersion));
DeobfHelper.init();
}
@MethodTransform(className="shadersmodcore.client.Shaders",
obf=@MethodMatch(name="pushEntity", signature=DeobfNames.SHADERS_PE_SIG_OBF),
deobf=@MethodMatch(name="pushEntity", signature=DeobfNames.SHADERS_PE_SIG_MCP),
log="Applying Shaders.pushEntity() block id ovverride")
public void handleGLSLBlockIDOverride(MethodNode method, boolean obf) {
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
insertAfter(method.instructions, arrayStore.getPrevious(),
new VarInsnNode(Opcodes.ALOAD, 1),
obf ? new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getGLSLBlockIdOverride", DeobfNames.BFC_GLSLID_SIG_OBF) :
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getGLSLBlockIdOverride", DeobfNames.BFC_GLSLID_SIG_MCP)
);
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
// ???
if (basicClass == null) return null;
// read class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
boolean hasTransformed = false;
for (MethodTransformerBase transformer : transformers) {
// try to find specified method in class
if (!transformedName.equals(transformer.getClassName())) continue;
logger.debug(String.format("Found class: %s -> %s", name, transformedName));
for (MethodNode methodNode : classNode.methods) {
logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc));
Boolean isObfuscated = null;
if (methodNode.name.equals(DeobfHelper.transformElementName(transformer.getMethodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(transformer.getSignature()))) {
isObfuscated = true;
} else if (methodNode.name.equals(transformer.getMethodName()) && methodNode.desc.equals(transformer.getSignature())) {
isObfuscated = false;
}
if (isObfuscated != null) {
// transform
hasTransformed = true;
try {
transformer.transform(methodNode, isObfuscated);
logger.info(String.format("%s: SUCCESS", transformer.getLogMessage()));
} catch (Exception e) {
logger.info(String.format("%s: FAILURE", transformer.getLogMessage()));
}
break;
}
}
}
// return result
ClassWriter writer = new ClassWriter(0);
if (hasTransformed) classNode.accept(writer);
return !hasTransformed ? basicClass : writer.toByteArray();
}
}

View File

@@ -0,0 +1,84 @@
package mods.betterfoliage.loader;
import java.util.Map;
import com.google.common.collect.Maps;
import cpw.mods.fml.relauncher.FMLInjectionData;
public class DeobfHelper {
private static Map<String, String> obfClasses = Maps.newHashMap();
private static Map<String, String> obfElements = Maps.newHashMap();
private static Map<String, String> srgElements = Maps.newHashMap();
public static void init() {
String mcVersion = FMLInjectionData.data()[4].toString();
srgElements.put("domainResourceManagers", "field_110548_a");
srgElements.put("mapRegisteredSprites", "field_110574_e");
if ("1.7.2".equals(mcVersion)) {
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "ble");
obfClasses.put("net/minecraft/world/IBlockAccess", "afx");
obfClasses.put("net/minecraft/block/Block", "ahu");
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "biz");
obfClasses.put("net/minecraft/world/World", "afn");
obfElements.put("blockAccess", "a");
obfElements.put("renderBlockByRenderType", "b");
obfElements.put("mapRegisteredSprites", "bpr");
obfElements.put("doVoidFogParticles", "C");
} else if ("1.7.10".equals(mcVersion)) {
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "blm");
obfClasses.put("net/minecraft/world/IBlockAccess", "ahl");
obfClasses.put("net/minecraft/block/Block", "aji");
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "bjf");
obfClasses.put("net/minecraft/world/World", "ahb");
obfElements.put("blockAccess", "a");
obfElements.put("renderBlockByRenderType", "b");
obfElements.put("mapRegisteredSprites", "bpr");
obfElements.put("doVoidFogParticles", "C");
}
}
/** Transform a class name from MCP to obfuscated names.
* @param className MCP name
* @return obfuscated name
*/
public static String transformClassName(String className) {
return obfClasses.containsKey(className) ? obfClasses.get(className) : className;
}
/** Transform a method or field name from MCP to obfuscated names.
* @param elementName MCP name
* @return obfuscated name
*/
public static String transformElementName(String elementName) {
return obfElements.containsKey(elementName) ? obfElements.get(elementName) : elementName;
}
/** Transform a method or field name from MCP to SRG names.
* @param elementName MCP name
* @return SRG name
*/
public static String transformElementSearge(String elementName) {
return srgElements.containsKey(elementName) ? srgElements.get(elementName) : elementName;
}
/** Transform an ASM signature from MCP to obfuscated names.
* @param signature MCP signature
* @return obfuscated signature
*/
public static String transformSignature(String signature) {
String result = signature;
boolean hasChanged = false;
do {
hasChanged = false;
for (Map.Entry<String, String> entry : obfClasses.entrySet()) if (result.contains("L" + entry.getKey() + ";")) {
result = result.replace("L" + entry.getKey() + ";", "L" + entry.getValue() + ";");
hasChanged = true;
}
} while(hasChanged);
return result;
}
}

View File

@@ -1,131 +0,0 @@
package mods.betterfoliage.loader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
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.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
public class EZTransformerBase implements IClassTransformer {
public static interface IInstructionMatch {
public boolean matches(AbstractInsnNode node);
}
@Retention(RetentionPolicy.RUNTIME)
public static @interface MethodMatch {
public String name();
public String signature();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface MethodTransform {
public String className();
public MethodMatch deobf();
public MethodMatch obf();
public String log();
}
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
public byte[] transform(String name, String transformedName, byte[] basicClass) {
// ???
if (basicClass == null) return null;
// read class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
boolean hasTransformed = false;
for (Method classMethod : getClass().getMethods()) {
// check for annotated method with correct signature
MethodTransform annot = classMethod.getAnnotation(MethodTransform.class);
if (annot == null) continue;
if (classMethod.getParameterTypes().length != 2) continue;
if (!classMethod.getParameterTypes()[0].equals(MethodNode.class)) continue;
if (!classMethod.getParameterTypes()[1].equals(boolean.class)) continue;
// try to find specified method in class
if (!transformedName.equals(annot.className())) continue;
for (MethodNode methodNode : classNode.methods) {
Boolean obf = null;
if (methodNode.name.equals(annot.obf().name()) && methodNode.desc.equals(annot.obf().signature())) {
obf = true;
} else if (methodNode.name.equals(annot.deobf().name()) && methodNode.desc.equals(annot.deobf().signature())) {
obf = false;
}
if (obf != null) {
// transform
hasTransformed = true;
try {
classMethod.invoke(this, new Object[] {methodNode, obf});
logger.info(String.format("%s: SUCCESS", annot.log()));
} catch (Exception e) {
logger.info(String.format("%s: FAILURE", annot.log()));
}
break;
}
}
}
// return result
ClassWriter writer = new ClassWriter(0);
if (hasTransformed) classNode.accept(writer);
return !hasTransformed ? basicClass : writer.toByteArray();
}
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getNext();
}
return current;
}
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getPrevious();
}
return current;
}
protected static IInstructionMatch matchInvokeAny() {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node instanceof MethodInsnNode;
}
};
}
protected static IInstructionMatch matchOpcode(final int opcode) {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node.getOpcode() == opcode;
}
};
}
protected static void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
InsnList listAdd = new InsnList();
for (AbstractInsnNode inst : added) listAdd.add(inst);
insnList.insert(node, listAdd);
}
}

View File

@@ -0,0 +1,144 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
/** Base class for class transformers operating on a single method.
* @author octarine-noise
*/
public abstract class MethodTransformerBase {
/** Instruction node filter
* @author octarine-noise
*/
public static interface IInstructionMatch {
public boolean matches(AbstractInsnNode node);
}
/**
* @return MCP name of the class to transform
*/
public abstract String getClassName();
/**
* @return MCP name of the method to transform
*/
public abstract String getMethodName();
/**
* @return ASM signature of the method to transform using MCP names
*/
public abstract String getSignature();
/**
* @return Log message to write when method is found
*/
public abstract String getLogMessage();
/** Transform method node
* @param method method node
* @param isObfuscated true for obfuscated environment
*/
public abstract void transform(MethodNode method, boolean isObfuscated);
/** Transform a class name from MCP to obfuscated names if necessary.
* @param className MCP name
* @param isObfuscated true for obfuscated environment
* @return transformed name
*/
protected static String className(String className, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformClassName(className) : className;
}
/** Transform a method or field name from MCP to obfuscated names if necessary.
* @param fieldName MCP name
* @param isObfuscated true for obfuscated environment
* @return transformed name
*/
protected static String element(String fieldName, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName;
}
/** Transform an ASM signature from MCP to obfuscated names if necessary.
* @param signature MCP signature
* @param isObfuscated true for obfuscated environment
* @return transformed signature
*/
protected static String signature(String signature, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformSignature(signature) : signature;
}
/** Find the next instruction node in an instruction list starting from a given node, matching a given filter
* @param start start node
* @param match filter
* @return instruction node if found, null otherwise
*/
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getNext();
}
return current;
}
/** Find the previous instruction node in a list starting from a given node, matching a given filter
* @param start start node
* @param match filter
* @return instruction node if found, null otherwise
*/
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getPrevious();
}
return current;
}
/**
* @return an instruction node filter matching any invoke instruction
*/
protected IInstructionMatch matchInvokeAny() {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node instanceof MethodInsnNode;
}
};
}
/**
* @return an instruction node filter matching the given opcode
*/
protected IInstructionMatch matchOpcode(final int opcode) {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node.getOpcode() == opcode;
}
};
}
/** Insert a list of instruction nodes in a list after a given node
* @param insnList instruction list
* @param node start node
* @param added instructions to add
*/
protected void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
InsnList listAdd = new InsnList();
for (AbstractInsnNode inst : added) listAdd.add(inst);
insnList.insert(node, listAdd);
}
/** Insert a list of instruction nodes in a list before a given node
* @param insnList instruction list
* @param node start node
* @param added instructions to add
*/
protected void insertBefore(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
InsnList listAdd = new InsnList();
for (AbstractInsnNode inst : added) listAdd.add(inst);
insnList.insertBefore(node, listAdd);
}
}

View File

@@ -0,0 +1,43 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformRandomDisplayTick extends MethodTransformerBase {
@Override
public String getClassName() {
return "net.minecraft.client.multiplayer.WorldClient";
}
@Override
public String getMethodName() {
return "doVoidFogParticles";
}
@Override
public String getSignature() {
return "(III)V";
}
@Override
public String getLogMessage() {
return "Applying random display tick call hook";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode endLoop = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IINC));
insertBefore(method.instructions, endLoop,
new VarInsnNode(Opcodes.ALOAD, 10),
new VarInsnNode(Opcodes.ALOAD, 0),
new VarInsnNode(Opcodes.ILOAD, 7),
new VarInsnNode(Opcodes.ILOAD, 8),
new VarInsnNode(Opcodes.ILOAD, 9),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "onRandomDisplayTick", signature("(Lnet/minecraft/block/Block;Lnet/minecraft/world/World;III)V", obf))
);
}
}

View File

@@ -0,0 +1,49 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformRenderBlockOverride extends MethodTransformerBase {
@Override
public String getClassName() {
return "net.minecraft.client.renderer.RenderBlocks";
}
@Override
public String getMethodName() {
return "renderBlockByRenderType";
}
@Override
public String getSignature() {
return "(Lnet/minecraft/block/Block;III)Z";
}
@Override
public String getLogMessage() {
return "Applying RenderBlocks.renderBlockByRenderType() render type override";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
insertAfter(method.instructions, storeRenderType,
new VarInsnNode(Opcodes.ALOAD, 0),
new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks", obf), element("blockAccess", obf), signature("Lnet/minecraft/world/IBlockAccess;", obf)),
new VarInsnNode(Opcodes.ILOAD, 2),
new VarInsnNode(Opcodes.ILOAD, 3),
new VarInsnNode(Opcodes.ILOAD, 4),
new VarInsnNode(Opcodes.ALOAD, 1),
new VarInsnNode(Opcodes.ILOAD, 5),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I", obf)),
new VarInsnNode(Opcodes.ISTORE, 5)
);
}
}

View File

@@ -0,0 +1,40 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformShaderModBlockOverride extends MethodTransformerBase {
@Override
public String getClassName() {
return "shadersmodcore.client.Shaders";
}
@Override
public String getMethodName() {
return "pushEntity";
}
@Override
public String getSignature() {
return "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
}
@Override
public String getLogMessage() {
return "Applying Shaders.pushEntity() block id override";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
insertAfter(method.instructions, arrayStore.getPrevious(),
new VarInsnNode(Opcodes.ALOAD, 1),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I", obf))
);
}
}

View File

@@ -0,0 +1,35 @@
// Vanilla
net.minecraft.block.BlockTallGrass
net.minecraft.block.BlockCrops
net.minecraft.block.BlockReed
net.minecraft.block.BlockDoublePlant
-net.minecraft.block.BlockCarrot
-net.minecraft.block.BlockPotato
// Biomes O'Plenty
biomesoplenty.common.blocks.BlockBOPFlower
biomesoplenty.common.blocks.BlockBOPFlower2
// Tinkers' Construct
tconstruct.blocks.slime.SlimeTallGrass
// Plant Mega Pack
plantmegapack.block.PMPBlockBerrybush
plantmegapack.block.PMPBlockCrops
plantmegapack.block.PMPBlockDesert
plantmegapack.block.PMPBlockFern
plantmegapack.block.PMPBlockFlowerMulti
plantmegapack.block.PMPBlockFlowerSingle
plantmegapack.block.PMPBlockForest
plantmegapack.block.PMPBlockGrass
plantmegapack.block.PMPBlockJungle
plantmegapack.block.PMPBlockMountain
plantmegapack.block.PMPBlockSavanna
plantmegapack.block.PMPBlockShrub
plantmegapack.block.PMPBlockWetlands
// Pam's HarvestCraft
com.pam.harvestcraft.BlockPamCrop
com.pam.harvestcraft.BlockPamDesertGarden
com.pam.harvestcraft.BlockPamNormalGarden
com.pam.harvestcraft.BlockPamWaterGarden

View File

@@ -0,0 +1,5 @@
// Vanilla
net.minecraft.block.BlockDirt
// Enhanced Biomes
enhancedbiomes.blocks.BlockSoilEB

View File

@@ -0,0 +1,5 @@
// Vanilla
net.minecraft.block.BlockGrass
// Enhanced Biomes
enhancedbiomes.blocks.BlockGrassEB

View File

@@ -0,0 +1,4 @@
net.minecraft.block.BlockLeavesBase
forestry.arboriculture.gadgets.BlockLeaves
thaumcraft.common.blocks.BlockMagicalLeaves
-tconstruct.blocks.OreberryBushEssence

View File

@@ -1,26 +1,131 @@
key.betterfoliage.gui=Open Settings
betterfoliage.arrayEntryDisplay=%d entries
message.betterfoliage.optionOn=ON
message.betterfoliage.optionOff=OFF
message.betterfoliage.config=Config
message.betterfoliage.back=Back
message.betterfoliage.close=Close
betterfoliage.enabled=Enable
betterfoliage.enabled.tooltip=Is this feature enabled?
betterfoliage.hOffset=Horizontal offset
betterfoliage.hOffset.tooltip=The distance this element is shifted horizontally, in blocks
betterfoliage.vOffset=Vertical offset
betterfoliage.vOffset.tooltip=The distance this element is shifted vertically, in blocks
betterfoliage.size=Size
betterfoliage.size.tooltip=Size of this element
betterfoliage.minHeight=Minimum height
betterfoliage.minHeight.tooltip=Minimum height of element
betterfoliage.maxHeight=Maximum height
betterfoliage.maxHeight.tooltip=Maximum height of element
betterfoliage.population=Population
betterfoliage.population.tooltip=Chance (N in 64) that an eligible block will have this feature
message.betterfoliage.betterLeaves=Extra Leaves: %s
message.betterfoliage.betterGrass=Short Grass: %s
message.betterfoliage.betterCactus=Better Cactus: %s
message.betterfoliage.betterLilypad=Better Lilypad: %s
message.betterfoliage.betterReed=Reeds: %s
message.betterfoliage.betterAlgae=Algae: %s
betterfoliage.blockTypes=Block Types
betterfoliage.blockTypes.tooltip=Configure lists of block classes that will have specific features applied to them
betterfoliage.blockTypes.dirtWhitelist=Dirt Whitelist
betterfoliage.blockTypes.dirtBlacklist=Dirt Blacklist
betterfoliage.blockTypes.grassWhitelist=Grass Whitelist
betterfoliage.blockTypes.grassBlacklist=Grass Blacklist
betterfoliage.blockTypes.leavesWhitelist=Leaves Whitelist
betterfoliage.blockTypes.leavesBlacklist=Leaves Blacklist
betterfoliage.blockTypes.cropWhitelist=Crop Whitelist
betterfoliage.blockTypes.cropBlacklist=Crop Blacklist
message.betterfoliage.size=Size
message.betterfoliage.hOffset=H.Offset
message.betterfoliage.vOffset=V.Offset
message.betterfoliage.minHeight=Min.Height
message.betterfoliage.maxHeight=Max.Height
message.betterfoliage.flowerChance=Flower Chance
message.betterfoliage.reedChance=Reed Chance
message.betterfoliage.leavesMode=Leaves Offset: %s
message.betterfoliage.leavesSkew=Skew
message.betterfoliage.leavesTranslate=Translate
message.betterfoliage.algaeChance=Algae Chance
betterfoliage.blockTypes.dirtWhitelist.tooltip=Blocks recognized as Dirt. Has an impact on Reeds, Algae, Connected Grass
betterfoliage.blockTypes.dirtBlacklist.tooltip=Blocks never accepted as Dirt. Has an impact on Reeds, Algae, Connected Grass
betterfoliage.blockTypes.grassWhitelist.tooltip=Blocks recognized as Grass. Has an impact on Short Grass, Connected Grass
betterfoliage.blockTypes.grassBlacklist.tooltip=Blocks never accepted as Grass. Has an impact on Short Grass, Connected Grass
betterfoliage.blockTypes.leavesWhitelist.tooltip=Blocks recognized as Leaves. Has an impact on Extra Leaves, Falling Leaves. Leaves will render with leaves block ID in shader programs
betterfoliage.blockTypes.leavesBlacklist.tooltip=Blocks never accepted as Leaves. Has an impact on Extra Leaves, Falling Leaves. Leaves will render with leaves block ID in shader programs
betterfoliage.blockTypes.cropWhitelist.tooltip=Blocks recognized as crops. Crops will render with tallgrass block ID in shader programs
betterfoliage.blockTypes.cropBlacklist.tooltip=Blocks never accepted as crops. Crops will render with tallgrass block ID in shader programs
betterfoliage.extraLeaves=Extra Leaves
betterfoliage.extraLeaves.tooltip=Extra round leaves on leaf blocks
betterfoliage.leavesMode=Leaves offset mode
betterfoliage.leavesMode.tooltip=Translate draws the leaves off-center at a 45deg angle, Skew draws them dentered but with a slightly changed angle
betterfoliage.leavesMode.true=Skew
betterfoliage.leavesMode.false=Translate
betterfoliage.shortGrass=Short Grass
betterfoliage.shortGrass.tooltip=Tufts of grass on top of grass blocks
betterfoliage.shortGrass.useGenerated=Use generated texture
betterfoliage.shortGrass.useGenerated.tooltip=Generated texture is made by slicing the tallgrass texture from the active resource pack in half
betterfoliage.cactus=Better Cactus
betterfoliage.cactus.tooltip=Enhance cactus with extra bits and smooth shading
betterfoliage.lilypad=Better Lilypad
betterfoliage.lilypad.tooltip=Enhance lilypad with roots and occasional flowers
betterfoliage.lilypad.flowerChance=Flower chance
betterfoliage.lilypad.flowerChance.tooltip=Chance (N in 64) of a lilypad having a flower on it
betterfoliage.reed=Reeds
betterfoliage.reed.tooltip=Reeds on dirt blocks in shallow water
betterfoliage.reed.biomeList=Biome List
betterfoliage.reed.biomeList.tooltip=Configure which biomes reeds are allowed to appear in
betterfoliage.reeds.biomeSelectTooltip=Should reeds appear in the %s biome?
betterfoliage.algae=Algae
betterfoliage.algae.tooltip=Algae on dirt blocks in deep water
betterfoliage.algae.biomeList=Biome List
betterfoliage.algae.biomeList.tooltip=Configure which biomes algae is allowed to appear in
betterfoliage.algae.biomeSelectTooltip=Should algae appear in the %s biome?
betterfoliage.coral=Coral
betterfoliage.coral.tooltip=Coral on sand blocks in deep water
betterfoliage.coral.size=Coral size
betterfoliage.coral.size.tooltip=Size of coral bits sticking out
betterfoliage.coral.crustSize=Crust size
betterfoliage.coral.crustSize.tooltip=Size of the flat coral part
betterfoliage.coral.chance=Coral chance
betterfoliage.coral.chance.tooltip=Chance (N in 64) of a specific face of the block to show coral
betterfoliage.coral.biomeList=Biome List
betterfoliage.coral.biomeList.tooltip=Configure which biomes coral is allowed to appear in
betterfoliage.coral.biomeSelectTooltip=Should coral appear in the %s biome?
betterfoliage.netherrack=Netherrack Vines
betterfoliage.netherrack.tooltip=Hanging Vines under netherrack
betterfoliage.fallingLeaves=Falling leaves
betterfoliage.fallingLeaves.tooltip=Falling leaf particle FX emitted from the bottom of leaf blocks
betterfoliage.fallingLeaves.speed=Particle speed
betterfoliage.fallingLeaves.speed.tooltip=Overall particle speed
betterfoliage.fallingLeaves.windStrength=Wind strength
betterfoliage.fallingLeaves.windStrength.tooltip=Magnitude of wind effects in good weather (spread of normal distribution centered on 0)
betterfoliage.fallingLeaves.stormStrength=Storm strength
betterfoliage.fallingLeaves.stormStrength.tooltip=Additional magnitude of wind effects in rainy weather (spread of normal distribution centered on 0)
betterfoliage.fallingLeaves.size=Particle size
betterfoliage.fallingLeaves.chance=Particle chance
betterfoliage.fallingLeaves.chance.tooltip=Chance of each random render tick hitting a leaf block to spawn a particle
betterfoliage.fallingLeaves.perturb=Perturbation
betterfoliage.fallingLeaves.perturb.tooltip=Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle synchronized to its rotation
betterfoliage.fallingLeaves.lifetime=Maximum lifetime
betterfoliage.fallingLeaves.lifetime.tooltip=Maximum lifetime of particle in seconds. Minimum lifetime is 60%% of this value
betterfoliage.risingSoul=Rising souls
betterfoliage.risingSoul.tooltip=Rising soul particle FX emitted from the top of soulsand blocks
betterfoliage.risingSoul.chance=Particle chance
betterfoliage.risingSoul.chance.tooltip=Chance of each random render tick hitting a soulsand block to spawn a particle
betterfoliage.risingSoul.speed=Particle speed
betterfoliage.risingSoul.speed.tooltip=Vertical speed of soul particles
betterfoliage.risingSoul.perturb=Perturbation
betterfoliage.risingSoul.perturb.tooltip=Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle
betterfoliage.risingSoul.headSize=Soul size
betterfoliage.risingSoul.headSize.tooltip=Size of the soul particle
betterfoliage.risingSoul.trailSize=Trail size
betterfoliage.risingSoul.trailSize.tooltip=Initial size of the particle trail
betterfoliage.risingSoul.opacity=Opacity
betterfoliage.risingSoul.opacity.tooltip=Opacity of the particle effect
betterfoliage.risingSoul.sizeDecay=Size decay
betterfoliage.risingSoul.sizeDecay.tooltip=Trail particle size relative to its size in the previous tick
betterfoliage.risingSoul.opacityDecay=Opacity decay
betterfoliage.risingSoul.opacityDecay.tooltip=Trail particle opacity relative to its opacity in the previous tick
betterfoliage.risingSoul.lifetime=Maximum lifetime
betterfoliage.risingSoul.lifetime.tooltip=Maximum lifetime of particle effect in seconds. Minimum lifetime is 60%% of this value
betterfoliage.risingSoul.trailLength=Trail length
betterfoliage.risingSoul.trailLength.tooltip=Number of previous positions the particle remembers in ticks
betterfoliage.risingSoul.trailDensity=Trail density
betterfoliage.risingSoul.trailDensity.tooltip=Render every Nth previous position in the particle trail
betterfoliage.connectedGrass=Connected grass textures
betterfoliage.connectedGrass.classic=Classic connected grass
betterfoliage.connectedGrass.classic.tooltip=Draw grass top texture on grass block face if there is a grass block diagonally under it
betterfoliage.connectedGrass.aggressive=Aggressive connected grass
betterfoliage.connectedGrass.aggressive.tooltip=If there is a grass block on top of a dirt block: draw grass top texture on all grass block sides, render dirt block as standard grass block

View File

@@ -0,0 +1,6 @@
// Vanilla
spruce=spruce
jungle=jungle
// Biomes O' Plenty
fir=spruce

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Some files were not shown because too many files have changed in this diff Show More