Compare commits
93 Commits
1.14.4-For
...
0.9.11b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98ca2ae284 | ||
|
|
abb1c5cf22 | ||
|
|
c366e89266 | ||
|
|
03766727fd | ||
|
|
856daacd58 | ||
|
|
f8092685a8 | ||
|
|
8b4967584e | ||
|
|
ce37687a1b | ||
|
|
187b29ac41 | ||
|
|
7a38ec7c3b | ||
|
|
772b509a10 | ||
|
|
ee50ce1dc5 | ||
|
|
d2f0d9c0f6 | ||
|
|
4706b11f98 | ||
|
|
bc3e598b1e | ||
|
|
b407e51d19 | ||
|
|
dcbb636902 | ||
|
|
e88a1ba5ae | ||
|
|
f107fe0e1a | ||
|
|
97f528d426 | ||
|
|
f2aeb870ce | ||
|
|
b365c1cf06 | ||
|
|
2b1cab8e62 | ||
|
|
bae6d9b598 | ||
|
|
6472353e4a | ||
|
|
929553692f | ||
|
|
7babab8385 | ||
|
|
51589bbad4 | ||
|
|
6b57840b8f | ||
|
|
c176713f20 | ||
|
|
23abde8000 | ||
|
|
21f6569e1b | ||
|
|
e92e89f86c | ||
|
|
728e723d37 | ||
|
|
ab5d1c0f3a | ||
|
|
9d9d32b32a | ||
|
|
6623bee39f | ||
|
|
ffa8dd724e | ||
|
|
bd4a4885fe | ||
|
|
9d59105af1 | ||
|
|
7cdddef1bf | ||
|
|
168fad883d | ||
|
|
572d517828 | ||
|
|
d4963ec173 | ||
|
|
ad83a511fb | ||
|
|
e351af2369 | ||
|
|
32a88fa824 | ||
|
|
ec723113d3 | ||
|
|
4e42f63c36 | ||
|
|
0daf61583a | ||
|
|
504f033c2e | ||
|
|
fa099b1b97 | ||
|
|
b0c0cd0d1b | ||
|
|
df69605521 | ||
|
|
d3b1d138ba | ||
|
|
07369159b8 | ||
|
|
6700e724a5 | ||
|
|
5b34da3e61 | ||
|
|
fc7c9a5381 | ||
|
|
cf7ae0efa1 | ||
|
|
04bb240d36 | ||
|
|
91fda1522c | ||
|
|
7ca25c0da7 | ||
|
|
0a05a67eda | ||
|
|
43e7fb830b | ||
|
|
c72e93aedf | ||
|
|
f236ef64ad | ||
|
|
d38f556bde | ||
|
|
fad662443d | ||
|
|
32c4dc6035 | ||
|
|
aee6e5caca | ||
|
|
5fbe2ff16f | ||
|
|
7a133c95a7 | ||
|
|
25b1d76c9e | ||
|
|
7a02179481 | ||
|
|
244907f4cd | ||
|
|
4ccd753c0c | ||
|
|
2715acf9c8 | ||
|
|
1c146fb070 | ||
|
|
44a20ceab3 | ||
|
|
1be2382fed | ||
|
|
e12b7b803c | ||
|
|
8a94867cd8 | ||
|
|
3a391f1677 | ||
|
|
d5dd1a36e3 | ||
|
|
a589c868a9 | ||
|
|
37ffa219fc | ||
|
|
220f2356d8 | ||
|
|
ec184f9916 | ||
|
|
2b1fd84cd5 | ||
|
|
30f199e9a2 | ||
|
|
0c66849175 | ||
|
|
3bd402b964 |
8
.classpath
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.gradle/
|
||||
.settings/
|
||||
bin/
|
||||
build/
|
||||
libs/
|
||||
18
.project
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>BetterFoliage</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,4 +1,9 @@
|
||||
BetterFoliage
|
||||
=============
|
||||
|
||||
Minecraft mod that alters the appearance of leaves & grass
|
||||
|
||||
More info: http://www.minecraftforum.net/topic/2776217-better-foliage/
|
||||
|
||||
Latest Download
|
||||
========
|
||||
[BetterFoliage 0.9.10-beta] (http://goo.gl/oaSfCY) (MC 1.7.2 & 1.7.10)
|
||||
|
||||
45
build.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "forge"
|
||||
url = "http://files.minecraftforge.net/maven"
|
||||
}
|
||||
maven {
|
||||
name = "sonatype"
|
||||
url = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
|
||||
}
|
||||
}
|
||||
apply plugin: 'forge'
|
||||
|
||||
minecraft {
|
||||
version = '1.7.2-10.12.2.1147'
|
||||
}
|
||||
|
||||
jar.baseName = 'BetterFoliage'
|
||||
group = 'com.github.octarine-noise'
|
||||
version='0.9.11b'
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
inputs.property "mcversion", project.minecraft.version
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'mcmod.info'
|
||||
expand 'version':project.version, 'mcversion':project.minecraft.version
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude 'mcmod.info'
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
|
||||
}
|
||||
}
|
||||
52
src/main/java/mods/betterfoliage/BetterFoliage.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package mods.betterfoliage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
import mods.betterfoliage.client.BetterFoliageClient;
|
||||
import mods.betterfoliage.common.config.Config;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.network.NetworkCheckHandler;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
@Mod(name=BetterFoliage.MOD_NAME, modid=BetterFoliage.MOD_ID, acceptedMinecraftVersions=BetterFoliage.MC_VERSIONS, guiFactory=BetterFoliage.GUI_FACTORY)
|
||||
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,1.7.10]";
|
||||
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
|
||||
|
||||
@Mod.Instance
|
||||
public static BetterFoliage instance;
|
||||
|
||||
public static Logger log;
|
||||
|
||||
public static File configDir;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
log = event.getModLog();
|
||||
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
|
||||
configDir.mkdir();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void posInit(FMLPostInitializationEvent event) {
|
||||
if (event.getSide() == Side.CLIENT) {
|
||||
Config.getDefaultBiomes();
|
||||
Config.readConfig(new File(configDir, "betterfoliage.cfg"));
|
||||
BetterFoliageClient.postInit();
|
||||
}
|
||||
}
|
||||
|
||||
@NetworkCheckHandler
|
||||
public boolean checkVersion(Map<String, String> mods, Side side) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
113
src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package mods.betterfoliage.client;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import mods.betterfoliage.BetterFoliage;
|
||||
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||
import mods.betterfoliage.client.render.impl.EntityFXFallingLeaves;
|
||||
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
|
||||
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
|
||||
import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral;
|
||||
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.RenderBlockBetterReed;
|
||||
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.common.config.Config;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
|
||||
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 LeafGenerator leafGenerator = new LeafGenerator();
|
||||
public static LeafParticleTextures leafParticles = new LeafParticleTextures(0);
|
||||
public static WindTracker wind = new WindTracker();
|
||||
|
||||
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 RenderBlockBetterLilypad());
|
||||
registerRenderer(new RenderBlockBetterMycelium());
|
||||
registerRenderer(new RenderBlockBetterLeaves());
|
||||
registerRenderer(new RenderBlockBetterGrass());
|
||||
registerRenderer(new RenderBlockBetterReed());
|
||||
registerRenderer(new RenderBlockBetterAlgae());
|
||||
registerRenderer(new RenderBlockBetterCoral());
|
||||
registerRenderer(new RenderBlocksBetterGrassSide());
|
||||
|
||||
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(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 static boolean isLeafTexture(TextureAtlasSprite icon) {
|
||||
String resourceLocation = icon.getIconName();
|
||||
if (resourceLocation.startsWith("forestry:leaves/")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getRenderTypeOverride(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||
// universal sign for DON'T RENDER ME!
|
||||
if (original == -1) return original;
|
||||
|
||||
for (Map.Entry<Integer, IRenderBlockDecorator> entry : decorators.entrySet())
|
||||
if (entry.getValue().isBlockAccepted(blockAccess, x, y, z, block, original))
|
||||
return entry.getKey();
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
public static void onRandomDisplayTick(Block block, World world, int x, int y, int z) {
|
||||
if (!Config.leafFXEnabled) return;
|
||||
if (!Config.leaves.matchesID(block) || !world.isAirBlock(x, y - 1, z)) return;
|
||||
if (Math.random() > Config.leafFXChance) return;
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z));
|
||||
}
|
||||
|
||||
public static void registerRenderer(IRenderBlockDecorator decorator) {
|
||||
int renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||
decorators.put(renderId, decorator);
|
||||
RenderingRegistry.registerBlockHandler(renderId, decorator);
|
||||
MinecraftForge.EVENT_BUS.register(decorator);
|
||||
decorator.init();
|
||||
}
|
||||
|
||||
}
|
||||
93
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package mods.betterfoliage.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import mods.betterfoliage.BetterFoliage;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class BlockMatcher {
|
||||
|
||||
public Set<Class<?>> whiteList = Sets.newHashSet();
|
||||
public Set<Class<?>> blackList = Sets.newHashSet();
|
||||
public Set<Integer> blockIDs = Sets.newHashSet();
|
||||
|
||||
public boolean matchesClass(Block block) {
|
||||
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
|
||||
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean matchesID(int blockId) {
|
||||
return blockIDs.contains(blockId);
|
||||
}
|
||||
|
||||
public boolean matchesID(Block block) {
|
||||
return blockIDs.contains(Block.blockRegistry.getIDForObject(block));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@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) {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(Minecraft.getMinecraft().getResourceManager().getResource(defaults).getInputStream(), Charsets.UTF_8));
|
||||
String line = reader.readLine();
|
||||
while(line != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty() && !line.startsWith("//")) {
|
||||
if (line.startsWith("-"))
|
||||
blacklist.add(line.substring(1));
|
||||
else
|
||||
whitelist.add(line);
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
BetterFoliage.log.warn(String.format("Error reading configuration %s", defaults.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Caches block IDs on world load for fast lookup
|
||||
* @param event
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void handleWorldLoad(WorldEvent.Load event) {
|
||||
if (event.world instanceof WorldClient) updateBlockIDs();
|
||||
}
|
||||
}
|
||||
27
src/main/java/mods/betterfoliage/client/KeyHandler.java
Normal file
@@ -0,0 +1,27 @@
|
||||
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;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class KeyHandler {
|
||||
|
||||
public static KeyBinding guiBinding;
|
||||
|
||||
public KeyHandler() {
|
||||
guiBinding = new KeyBinding("key.betterfoliage.gui", 66, BetterFoliage.MOD_NAME);
|
||||
ClientRegistry.registerKeyBinding(guiBinding);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleKeyPress(InputEvent.KeyInputEvent event) {
|
||||
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiFactory.ConfigGuiBetterFoliage(null));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
64
src/main/java/mods/betterfoliage/client/WindTracker.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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() };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package mods.betterfoliage.client.gui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
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 static List<BiomeGenBase> reedBiomeList = Lists.newArrayList();
|
||||
public static List<BiomeGenBase> algaeBiomeList = Lists.newArrayList();
|
||||
public static List<BiomeGenBase> coralBiomeList = Lists.newArrayList();
|
||||
|
||||
public BiomeListConfigEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
|
||||
super(owningScreen, owningEntryList, configElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BiomeGenBase> getBaseSet(String qualifiedName) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BiomeGenBase> getDefaultSelected(String name) {
|
||||
if (name.equals("reedBiomeList")) return reedBiomeList;
|
||||
if (name.equals("algaeBiomeList")) return algaeBiomeList;
|
||||
if (name.equals("coralBiomeList")) return coralBiomeList;
|
||||
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 "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package mods.betterfoliage.client.gui;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ConfigGuiFactory implements IModGuiFactory {
|
||||
|
||||
public void initialize(Minecraft minecraftInstance) {
|
||||
|
||||
}
|
||||
|
||||
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
||||
return ConfigGuiBetterFoliage.class;
|
||||
}
|
||||
|
||||
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
|
||||
return ImmutableSet.<RuntimeOptionCategoryElement>of();
|
||||
}
|
||||
|
||||
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package mods.betterfoliage.client.gui;
|
||||
|
||||
import mods.betterfoliage.common.util.Utils;
|
||||
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);
|
||||
|
||||
Utils.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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.Utils;
|
||||
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);
|
||||
Utils.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package mods.betterfoliage.client.render;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
/** Same as {@link RenderBlockAOBase}, but does not actually render anything.
|
||||
* @author octarine-noise
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FakeRenderBlockAOBase extends RenderBlockAOBase {
|
||||
|
||||
@Override
|
||||
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoZNXYPP);
|
||||
saveShadingTopRight(aoZNXYNP);
|
||||
saveShadingBottomLeft(aoZNXYPN);
|
||||
saveShadingBottomRight(aoZNXYNN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoZPXYNP);
|
||||
saveShadingTopRight(aoZPXYPP);
|
||||
saveShadingBottomLeft(aoZPXYNN);
|
||||
saveShadingBottomRight(aoZPXYPN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoXNYZPN);
|
||||
saveShadingTopRight(aoXNYZPP);
|
||||
saveShadingBottomLeft(aoXNYZNN);
|
||||
saveShadingBottomRight(aoXNYZNP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoXPYZPP);
|
||||
saveShadingTopRight(aoXPYZPN);
|
||||
saveShadingBottomLeft(aoXPYZNP);
|
||||
saveShadingBottomRight(aoXPYZNN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoYNXZNP);
|
||||
saveShadingTopRight(aoYNXZPP);
|
||||
saveShadingBottomLeft(aoYNXZNN);
|
||||
saveShadingBottomRight(aoYNXZPN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
saveShadingTopLeft(aoYPXZPP);
|
||||
saveShadingTopRight(aoYPXZNP);
|
||||
saveShadingBottomLeft(aoYPXZPN);
|
||||
saveShadingBottomRight(aoYPXZNN);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package mods.betterfoliage.client.render;
|
||||
|
||||
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
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);
|
||||
|
||||
}
|
||||
52
src/main/java/mods/betterfoliage/client/render/IconSet.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package mods.betterfoliage.client.render;
|
||||
|
||||
import mods.betterfoliage.common.util.Utils;
|
||||
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;
|
||||
|
||||
/** Holds an indexed set of textures
|
||||
* @author octarine-noise
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class IconSet {
|
||||
|
||||
/** Icon array */
|
||||
public IIcon[] icons = new IIcon[16];
|
||||
|
||||
/** Number of successfully loaded icons */
|
||||
public int numLoaded = 0;
|
||||
|
||||
/** Resource domain of icons */
|
||||
String domain;
|
||||
|
||||
/** Format string of icon paths */
|
||||
String path;
|
||||
|
||||
public IconSet(String domain, String path) {
|
||||
this.domain = domain;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void registerIcons(IIconRegister register) {
|
||||
numLoaded = 0;
|
||||
for (int idx = 0; idx < 16; idx++) {
|
||||
icons[idx] = null;
|
||||
// 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)))
|
||||
icons[numLoaded++] = register.registerIcon(domain + ":" + String.format(path, idx));
|
||||
}
|
||||
}
|
||||
|
||||
public IIcon get(int variation) {
|
||||
return numLoaded == 0 ? null : icons[variation % numLoaded];
|
||||
}
|
||||
|
||||
public boolean hasIcons() {
|
||||
return numLoaded > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
package mods.betterfoliage.client.render;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import mods.betterfoliage.common.util.Double3;
|
||||
import mods.betterfoliage.common.util.Utils;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.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;
|
||||
|
||||
/** Block renderer base class. Stores calculated ambient occlusion light and color values when rendering
|
||||
* block sides for later use.
|
||||
* @author octarine-noise
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderBlockAOBase extends RenderBlocks {
|
||||
|
||||
/** AO light and color values
|
||||
* @author octarine-noise
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static class ShadingValues {
|
||||
public int passCounter = 0;
|
||||
public int brightness;
|
||||
public float red;
|
||||
public float green;
|
||||
public float blue;
|
||||
public void setGray(float value) {
|
||||
red = value; green = value; blue = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
|
||||
protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
|
||||
|
||||
protected ForgeDirection[] faceDir1 = new ForgeDirection[] {ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.EAST, ForgeDirection.SOUTH, ForgeDirection.NORTH};
|
||||
protected ForgeDirection[] faceDir2 = new ForgeDirection[] {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP};
|
||||
|
||||
/** Random vector pool. Unit rotation vectors in the XZ plane, Y coord goes between [-1.0, 1.0].
|
||||
* Filled at init time */
|
||||
public Double3[] pRot = new Double3[64];
|
||||
|
||||
/** Pool of random double values. Filled at init time. */
|
||||
public double[] pRand = new double[64];
|
||||
|
||||
public ShadingValues aoXPYZPP = new ShadingValues();
|
||||
public ShadingValues aoXPYZPN = new ShadingValues();
|
||||
public ShadingValues aoXPYZNP = new ShadingValues();
|
||||
public ShadingValues aoXPYZNN = new ShadingValues();
|
||||
public ShadingValues aoXNYZPP = new ShadingValues();
|
||||
public ShadingValues aoXNYZPN = new ShadingValues();
|
||||
public ShadingValues aoXNYZNP = new ShadingValues();
|
||||
public ShadingValues aoXNYZNN = new ShadingValues();
|
||||
public ShadingValues aoYPXZPP = new ShadingValues();
|
||||
public ShadingValues aoYPXZPN = new ShadingValues();
|
||||
public ShadingValues aoYPXZNP = new ShadingValues();
|
||||
public ShadingValues aoYPXZNN = new ShadingValues();
|
||||
public ShadingValues aoYNXZPP = new ShadingValues();
|
||||
public ShadingValues aoYNXZPN = new ShadingValues();
|
||||
public ShadingValues aoYNXZNP = new ShadingValues();
|
||||
public ShadingValues aoYNXZNN = new ShadingValues();
|
||||
public ShadingValues aoZPXYPP = new ShadingValues();
|
||||
public ShadingValues aoZPXYPN = new ShadingValues();
|
||||
public ShadingValues aoZPXYNP = new ShadingValues();
|
||||
public ShadingValues aoZPXYNN = new ShadingValues();
|
||||
public ShadingValues aoZNXYPP = new ShadingValues();
|
||||
public ShadingValues aoZNXYPN = new ShadingValues();
|
||||
public ShadingValues aoZNXYNP = new ShadingValues();
|
||||
public ShadingValues aoZNXYNN = new ShadingValues();
|
||||
|
||||
// temporary shading values for a single face
|
||||
public ShadingValues faceAOPP, faceAOPN, faceAONN, faceAONP;
|
||||
|
||||
/** Initialize random values */
|
||||
public void init() {
|
||||
List<Double3> perturbs = new ArrayList<Double3>(64);
|
||||
for (int idx = 0; idx < 64; idx++) {
|
||||
double angle = (double) idx * Math.PI * 2.0 / 64.0;
|
||||
perturbs.add(new Double3(Math.cos(angle), Math.random() * 2.0 - 1.0, Math.sin(angle)));
|
||||
pRand[idx] = Math.random();
|
||||
}
|
||||
Collections.shuffle(perturbs);
|
||||
Iterator<Double3> iter = perturbs.iterator();
|
||||
for (int idx = 0; idx < 64; idx++) pRot[idx] = iter.next();
|
||||
}
|
||||
|
||||
/** Get a semi-random value depending on block position.
|
||||
* @param x block X coord
|
||||
* @param y block Y coord
|
||||
* @param z block Z coord
|
||||
* @param seed additional seed
|
||||
* @return semirandom value
|
||||
*/
|
||||
protected int getSemiRandomFromPos(double x, double y, double z, int seed) {
|
||||
long lx = MathHelper.floor_double(x);
|
||||
long ly = MathHelper.floor_double(y);
|
||||
long lz = MathHelper.floor_double(z);
|
||||
long value = (lx * lx + ly * ly + lz * lz + lx * ly + ly * lz + lz * lx + seed * seed) & 63;
|
||||
value = (3 * lx * value + 5 * ly * value + 7 * lz * value + 11 * seed) & 63;
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
|
||||
renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
|
||||
}
|
||||
|
||||
public boolean shouldRender3DInInventory(int modelId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getRenderId() {
|
||||
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 = Utils.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;
|
||||
|
||||
float f2;
|
||||
float f3;
|
||||
int k;
|
||||
|
||||
p_147800_1_.setBlockBoundsForItemRender();
|
||||
renderer.setRenderBoundsFromBlock(p_147800_1_);
|
||||
GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
|
||||
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, -1.0F, 0.0F);
|
||||
renderer.renderFaceYNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 0, p_147800_2_));
|
||||
tessellator.draw();
|
||||
|
||||
if (flag && renderer.useInventoryTint)
|
||||
{
|
||||
k = p_147800_1_.getRenderColor(p_147800_2_);
|
||||
f2 = (float)(k >> 16 & 255) / 255.0F;
|
||||
f3 = (float)(k >> 8 & 255) / 255.0F;
|
||||
float f4 = (float)(k & 255) / 255.0F;
|
||||
GL11.glColor4f(f2 * p_147800_3_, f3 * p_147800_3_, f4 * p_147800_3_, 1.0F);
|
||||
}
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 1.0F, 0.0F);
|
||||
renderer.renderFaceYPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 1, p_147800_2_));
|
||||
tessellator.draw();
|
||||
|
||||
if (flag && renderer.useInventoryTint)
|
||||
{
|
||||
GL11.glColor4f(p_147800_3_, p_147800_3_, p_147800_3_, 1.0F);
|
||||
}
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, -1.0F);
|
||||
renderer.renderFaceZNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 2, p_147800_2_));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, 1.0F);
|
||||
renderer.renderFaceZPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 3, p_147800_2_));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 4, p_147800_2_));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 5, p_147800_2_));
|
||||
tessellator.draw();
|
||||
GL11.glTranslatef(0.5F, 0.5F, 0.5F);
|
||||
}
|
||||
|
||||
protected void setShadingForFace(ForgeDirection dir) {
|
||||
if (dir == ForgeDirection.DOWN) {
|
||||
// dir1 WEST, dir2 NORTH
|
||||
faceAOPP = aoYNXZPP; faceAOPN = aoYNXZPN; faceAONN = aoYNXZNN; faceAONP = aoYNXZNP;
|
||||
} else if (dir == ForgeDirection.UP) {
|
||||
// dir1 WEST, dir2 SOUTH
|
||||
faceAOPP = aoYPXZPP; faceAOPN = aoYPXZPN; faceAONN = aoYPXZNN; faceAONP = aoYPXZNP;
|
||||
} else if (dir == ForgeDirection.NORTH) {
|
||||
// dir1 WEST, dir2 UP
|
||||
faceAOPP = aoZNXYNP; faceAOPN = aoZNXYNN; faceAONN = aoZNXYPN; faceAONP = aoZNXYPP;
|
||||
} else if (dir == ForgeDirection.SOUTH) {
|
||||
// dir1 EAST, dir2 UP
|
||||
faceAOPP = aoZPXYPP; faceAOPN = aoZPXYPN; faceAONN = aoZPXYNN; faceAONP = aoZPXYNP;
|
||||
} else if (dir == ForgeDirection.WEST) {
|
||||
// dir1 SOUTH, dir2 UP
|
||||
faceAOPP = aoXNYZPP; faceAOPN = aoXNYZNP; faceAONN = aoXNYZNN; faceAONP = aoXNYZPN;
|
||||
} else if (dir == ForgeDirection.EAST) {
|
||||
// dir1 NORTH, dir2 UP
|
||||
faceAOPP = aoXPYZPN; faceAOPN = aoXPYZNN; faceAONN = aoXPYZNP; faceAONP = aoXPYZPP;
|
||||
}
|
||||
}
|
||||
|
||||
public void renderCrossedSideQuads(Double3 drawBase, ForgeDirection dir, double scale, double halfHeight, Double3 rendomVec, double offset, IIcon renderIcon, int uvRot, boolean noShading) {
|
||||
Double3 facePP, faceNP, faceNormal, drawCenter;
|
||||
|
||||
if (dir == ForgeDirection.UP) {
|
||||
// special case for block top, we'll be rendering a LOT of those
|
||||
facePP = new Double3(-scale, 0.0, scale);
|
||||
faceNP = new Double3(scale, 0.0, scale);
|
||||
faceNormal = new Double3(0.0, halfHeight, 0.0);
|
||||
drawCenter = drawBase.add(faceNormal);
|
||||
if (rendomVec != null) {
|
||||
drawCenter = drawBase.add(faceNormal).add(rendomVec.scaleAxes(-offset, 0.0, offset));
|
||||
}
|
||||
} else {
|
||||
facePP = new Double3(faceDir1[dir.ordinal()]).add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||
faceNP = new Double3(faceDir1[dir.ordinal()]).inverse().add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||
faceNormal = new Double3(dir).scale(halfHeight);
|
||||
drawCenter = drawBase.add(faceNormal);
|
||||
if (rendomVec != null) {
|
||||
drawCenter = drawCenter.add(new Double3(faceDir1[dir.ordinal()]).scale(rendomVec.x).scale(offset))
|
||||
.add(new Double3(faceDir2[dir.ordinal()]).scale(rendomVec.z).scale(offset));
|
||||
}
|
||||
}
|
||||
|
||||
if (Minecraft.isAmbientOcclusionEnabled() && !noShading) {
|
||||
setShadingForFace(dir);
|
||||
renderQuadWithShading(renderIcon, drawCenter, facePP, faceNormal, uvRot, faceAOPP, faceAONN, faceAONN, faceAOPP);
|
||||
renderQuadWithShading(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot, faceAONN, faceAOPP, faceAOPP, faceAONN);
|
||||
renderQuadWithShading(renderIcon, drawCenter, faceNP, faceNormal, uvRot, faceAONP, faceAOPN, faceAOPN, faceAONP);
|
||||
renderQuadWithShading(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot, faceAOPN, faceAONP, faceAONP, faceAOPN);
|
||||
} else {
|
||||
renderQuad(renderIcon, drawCenter, facePP, faceNormal, uvRot);
|
||||
renderQuad(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot);
|
||||
renderQuad(renderIcon, drawCenter, faceNP, faceNormal, uvRot);
|
||||
renderQuad(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot);
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderCrossedBlockQuadsTranslate(Double3 blockCenter, double halfSize, Double3 offsetVec, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||
Double3 drawCenter = blockCenter;
|
||||
if (offsetVec != null) drawCenter = drawCenter.add(offsetVec);
|
||||
Double3 horz1 = new Double3(halfSize, 0.0, halfSize);
|
||||
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize);
|
||||
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||
|
||||
renderCrossedBlockQuadsInternal(drawCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||
}
|
||||
|
||||
protected void renderCrossedBlockQuadsSkew(Double3 blockCenter, double halfSize, Double3 offsetVec1, Double3 offsetVec2, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||
Double3 horz1 = new Double3(halfSize, 0.0, halfSize).add(offsetVec1);
|
||||
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize).add(offsetVec2);
|
||||
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||
|
||||
renderCrossedBlockQuadsInternal(blockCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||
}
|
||||
|
||||
private void renderCrossedBlockQuadsInternal(Double3 drawCenter, Double3 horz1, Double3 horz2, Double3 vert1, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||
renderQuadWithShading(crossLeafIcon, drawCenter, horz1, vert1, uvRot,
|
||||
isAirTop ? aoYPXZPP : aoZPXYPP, isAirTop ? aoYPXZNN : aoXNYZPN, isAirBottom ? aoYNXZNN : aoXNYZNN, isAirBottom ? aoYNXZPP : aoZPXYPN);
|
||||
renderQuadWithShading(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot,
|
||||
isAirTop ? aoYPXZNN : aoZNXYNP, isAirTop ? aoYPXZPP : aoXPYZPP, isAirBottom ? aoYNXZPP : aoXPYZNP, isAirBottom ? aoYNXZNN : aoZNXYNN);
|
||||
renderQuadWithShading(crossLeafIcon, drawCenter, horz2, vert1, uvRot,
|
||||
isAirTop ? aoYPXZPN : aoXPYZPN, isAirTop ? aoYPXZNP : aoZPXYNP, isAirBottom ? aoYNXZNP : aoZPXYNN, isAirBottom ? aoYNXZPN : aoXPYZNN);
|
||||
renderQuadWithShading(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot,
|
||||
isAirTop ? aoYPXZNP : aoXNYZPP, isAirTop ? aoYPXZPN : aoZNXYPP, isAirBottom ? aoYNXZPN : aoZNXYPN, isAirBottom ? aoYNXZNP : aoXNYZNP);
|
||||
} else {
|
||||
renderQuad(crossLeafIcon, drawCenter, horz1, vert1, uvRot);
|
||||
renderQuad(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot);
|
||||
renderQuad(crossLeafIcon, drawCenter, horz2, vert1, uvRot);
|
||||
renderQuad(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceZNeg(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoZNXYPP);
|
||||
saveShadingTopRight(aoZNXYNP);
|
||||
saveShadingBottomLeft(aoZNXYPN);
|
||||
saveShadingBottomRight(aoZNXYNN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceZPos(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoZPXYNP);
|
||||
saveShadingTopRight(aoZPXYPP);
|
||||
saveShadingBottomLeft(aoZPXYNN);
|
||||
saveShadingBottomRight(aoZPXYPN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceXNeg(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoXNYZPN);
|
||||
saveShadingTopRight(aoXNYZPP);
|
||||
saveShadingBottomLeft(aoXNYZNN);
|
||||
saveShadingBottomRight(aoXNYZNP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceXPos(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoXPYZPP);
|
||||
saveShadingTopRight(aoXPYZPN);
|
||||
saveShadingBottomLeft(aoXPYZNP);
|
||||
saveShadingBottomRight(aoXPYZNN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceYNeg(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoYNXZNP);
|
||||
saveShadingTopRight(aoYNXZPP);
|
||||
saveShadingBottomLeft(aoYNXZNN);
|
||||
saveShadingBottomRight(aoYNXZPN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceYPos(block, x, y, z, icon);
|
||||
saveShadingTopLeft(aoYPXZPP);
|
||||
saveShadingTopRight(aoYPXZNP);
|
||||
saveShadingBottomLeft(aoYPXZPN);
|
||||
saveShadingBottomRight(aoYPXZNN);
|
||||
}
|
||||
|
||||
protected void saveShadingTopLeft(ShadingValues values) {
|
||||
if (--values.passCounter != 0) return;
|
||||
values.brightness = brightnessTopLeft;
|
||||
values.red = colorRedTopLeft;
|
||||
values.green = colorGreenTopLeft;
|
||||
values.blue = colorBlueTopLeft;
|
||||
}
|
||||
|
||||
protected void saveShadingTopRight(ShadingValues values) {
|
||||
if (--values.passCounter != 0) return;
|
||||
values.brightness = brightnessTopRight;
|
||||
values.red = colorRedTopRight;
|
||||
values.green = colorGreenTopRight;
|
||||
values.blue = colorBlueTopRight;
|
||||
}
|
||||
|
||||
protected void saveShadingBottomLeft(ShadingValues values) {
|
||||
if (--values.passCounter != 0) return;
|
||||
values.brightness = brightnessBottomLeft;
|
||||
values.red = colorRedBottomLeft;
|
||||
values.green = colorGreenBottomLeft;
|
||||
values.blue = colorBlueBottomLeft;
|
||||
}
|
||||
|
||||
protected void saveShadingBottomRight(ShadingValues values) {
|
||||
if (--values.passCounter != 0) return;
|
||||
values.brightness = brightnessBottomRight;
|
||||
values.red = colorRedBottomRight;
|
||||
values.green = colorGreenBottomRight;
|
||||
values.blue = colorBlueBottomRight;
|
||||
}
|
||||
|
||||
/** Set pass counter on all shading value objects.
|
||||
* Used to collect AO values from a specific draw pass
|
||||
* if the underlying renderer draws overlays
|
||||
* @param value pass counter
|
||||
*/
|
||||
protected void setPassCounters(int value) {
|
||||
aoXPYZPP.passCounter = value;
|
||||
aoXPYZPN.passCounter = value;
|
||||
aoXPYZNP.passCounter = value;
|
||||
aoXPYZNN.passCounter = value;
|
||||
aoXNYZPP.passCounter = value;
|
||||
aoXNYZPN.passCounter = value;
|
||||
aoXNYZNP.passCounter = value;
|
||||
aoXNYZNN.passCounter = value;
|
||||
aoYPXZPP.passCounter = value;
|
||||
aoYPXZPN.passCounter = value;
|
||||
aoYPXZNP.passCounter = value;
|
||||
aoYPXZNN.passCounter = value;
|
||||
aoYNXZPP.passCounter = value;
|
||||
aoYNXZPN.passCounter = value;
|
||||
aoYNXZNP.passCounter = value;
|
||||
aoYNXZNN.passCounter = value;
|
||||
aoZPXYPP.passCounter = value;
|
||||
aoZPXYPN.passCounter = value;
|
||||
aoZPXYNP.passCounter = value;
|
||||
aoZPXYNN.passCounter = value;
|
||||
aoZNXYPP.passCounter = value;
|
||||
aoZNXYPN.passCounter = value;
|
||||
aoZNXYNP.passCounter = value;
|
||||
aoZNXYNN.passCounter = value;
|
||||
}
|
||||
|
||||
/** Render textured quad
|
||||
* @param icon texture to use
|
||||
* @param center center of quad
|
||||
* @param vec1 vector to the half-point of one of the sides
|
||||
* @param vec2 vector to half-point of side next to vec1
|
||||
* @param uvRot number of increments to rotate UV coordinates by
|
||||
*/
|
||||
protected void renderQuad(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||
}
|
||||
|
||||
/** Render textured quad using AO information
|
||||
* @param icon texture to use
|
||||
* @param center center of quad
|
||||
* @param vec1 vector to the half-point of one of the sides
|
||||
* @param vec2 vector to half-point of side next to vec1
|
||||
* @param uvRot number of increments to rotate UV coordinates by
|
||||
* @param aoPP AO values for vertex at (+vec1, +vec2)
|
||||
* @param aoNP AO values for vertex at (-vec1, +vec2)
|
||||
* @param aoNN AO values for vertex at (-vec1, -vec2)
|
||||
* @param aoPN AO values for vertex at (+vec1, -vec2)
|
||||
*/
|
||||
protected void renderQuadWithShading(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot, ShadingValues aoPP, ShadingValues aoNP, ShadingValues aoNN, ShadingValues aoPN) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.setBrightness(aoPP.brightness);
|
||||
tessellator.setColorOpaque_F(aoPP.red, aoPP.green, aoPP.blue);
|
||||
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||
tessellator.setBrightness(aoNP.brightness);
|
||||
tessellator.setColorOpaque_F(aoNP.red, aoNP.green, aoNP.blue);
|
||||
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||
tessellator.setBrightness(aoNN.brightness);
|
||||
tessellator.setColorOpaque_F(aoNN.red, aoNN.green, aoNN.blue);
|
||||
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||
tessellator.setBrightness(aoPN.brightness);
|
||||
tessellator.setColorOpaque_F(aoPN.red, aoPN.green, aoPN.blue);
|
||||
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||
}
|
||||
|
||||
protected int getBrightness(Block block, int x, int y, int z) {
|
||||
return block.getMixedBrightnessForBlock(blockAccess, x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
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;
|
||||
particleIcon = BetterFoliageClient.leafParticles.icons.get(rand.nextInt(1024));
|
||||
|
||||
Block block = world.getBlock(x, y, z);
|
||||
IIcon blockIcon = block.getIcon(world, x, y, z, ForgeDirection.DOWN.ordinal());
|
||||
calculateParticleColor(BetterFoliageClient.leafParticles.getColor(blockIcon), block.colorMultiplier(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
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.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
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;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderBlockBetterAlgae extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
public IconSet algaeIcons = new IconSet("bettergrassandleaves", "better_algae_%d");
|
||||
public NoiseGeneratorSimplex noise;
|
||||
|
||||
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||
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;
|
||||
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
int variation = getSemiRandomFromPos(x, y, z, 0);
|
||||
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||
|
||||
IIcon renderIcon = algaeIcons.get(variation);
|
||||
if (renderIcon == null) return true;
|
||||
|
||||
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], Config.algaeHOffset, renderIcon, 0, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
algaeIcons.registerIcons(event.map);
|
||||
BetterFoliage.log.info(String.format("Found %d algae textures", algaeIcons.numLoaded));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleWorldLoad(WorldEvent.Load event) {
|
||||
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package mods.betterfoliage.client.render.impl;
|
||||
|
||||
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;
|
||||
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 RenderBlockBetterCactus extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
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 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);
|
||||
renderStandardBlock(block, x, y, z);
|
||||
|
||||
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||
Tessellator.instance.setBrightness(getBrightness(block,x, y, z));
|
||||
renderCactusCore(block.getBlockTextureFromSide(ForgeDirection.UP.ordinal()),
|
||||
block.getBlockTextureFromSide(ForgeDirection.NORTH.ordinal()),
|
||||
blockCenter, 0);
|
||||
|
||||
// render side growth
|
||||
ForgeDirection drawDirection = cactusDirections[getSemiRandomFromPos(x, y, z, 0) % 4];
|
||||
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||
Double3 drawBase = blockCenter.add(new Double3(drawDirection).scale(cactusRadius));
|
||||
|
||||
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||
if (cactusSideIcons.hasIcons()) renderCrossedSideQuads(drawBase, drawDirection, 0.5, 0.5, pRot[iconVariation], 0.2, cactusSideIcons.get(iconVariation), 0, false);
|
||||
renderCrossedBlockQuadsSkew(blockCenter, 0.65,
|
||||
pRot[iconVariation].scaleAxes(0.1, 0.0, 0.1),
|
||||
pRot[(iconVariation + 1) & 63].scaleAxes(0.1, 0.0, 0.1),
|
||||
cactusRoundIcon, iconVariation, false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void renderCactusCore(IIcon topIcon, IIcon sideIcon, Double3 blockCenter, int sideUvRot) {
|
||||
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||
renderQuadWithShading(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXPYZPN, aoXPYZPP, aoXPYZNP, aoXPYZNN);
|
||||
renderQuadWithShading(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXNYZPP, aoXNYZPN, aoXNYZNN, aoXNYZNP);
|
||||
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZPXYPP, aoZPXYNP, aoZPXYNN, aoZPXYPN);
|
||||
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZNXYNP, aoZNXYPP, aoZNXYPN, aoZNXYNN);
|
||||
renderQuadWithShading(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0, aoYPXZNP, aoYPXZPP, aoYPXZPN, aoYPXZNN);
|
||||
} else {
|
||||
renderQuad(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||
renderQuad(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||
renderQuad(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
cactusRoundIcon = event.map.registerIcon("bettergrassandleaves:better_cactus");
|
||||
cactusSideIcons.registerIcons(event.map);
|
||||
BetterFoliage.log.info(String.format("Found %d cactus arm textures", cactusSideIcons.numLoaded));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
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 RenderBlockBetterGrass extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
public IconSet grassIcons = new IconSet("bettergrassandleaves", "better_grass_long_%d");
|
||||
public IconSet snowGrassIcons = new IconSet("bettergrassandleaves", "better_grass_snowed_%d");
|
||||
public IIcon grassGenIcon;
|
||||
public IIcon snowGrassGenIcon;
|
||||
|
||||
protected IIcon grassTopIcon;
|
||||
protected boolean connectXP, connectXN, connectZP, connectZN;
|
||||
|
||||
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||
return Config.grass.matchesID(block);
|
||||
}
|
||||
|
||||
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||
blockAccess = world;
|
||||
|
||||
// check for connected grass
|
||||
checkConnectedGrass(x, y, z);
|
||||
grassTopIcon = block.getIcon(blockAccess, x, y, z, ForgeDirection.UP.ordinal());
|
||||
|
||||
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||
if (!Config.grassEnabled) return true;
|
||||
|
||||
boolean isSnowTop = blockAccess.getBlock(x, y + 1, z) == Blocks.snow_layer;
|
||||
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) {
|
||||
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);
|
||||
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 snowy grass textures", snowGrassIcons.numLoaded));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
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.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.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||
if (!Config.leavesEnabled) return false;
|
||||
if (original > 0 && original < 42) return false;
|
||||
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) {
|
||||
blockAccess = world;
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||
double halfSize = 0.5 * Config.leavesSize;
|
||||
boolean isAirTop = y == 255 || blockAccess.isAirBlock(x, y + 1, z);
|
||||
boolean isAirBottom = y == 0 || blockAccess.isAirBlock(x, y - 1, z);
|
||||
|
||||
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 (Config.leavesSkew) {
|
||||
renderCrossedBlockQuadsSkew(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
|
||||
pRot[offsetVariation].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset),
|
||||
pRot[(offsetVariation + 1) & 63].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset),
|
||||
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||
} else {
|
||||
renderCrossedBlockQuadsTranslate(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
|
||||
pRot[offsetVariation].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset),
|
||||
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isBlockSurrounded(IBlockAccess blockAccess, int x, int y, int z) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package mods.betterfoliage.client.render.impl;
|
||||
|
||||
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;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.init.Blocks;
|
||||
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 RenderBlockBetterLilypad extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
public IconSet lilypadFlowers = new IconSet("bettergrassandleaves", "better_lilypad_flower_%d");
|
||||
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 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;
|
||||
|
||||
// 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);
|
||||
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||
int offsetVariation = getSemiRandomFromPos(x, y, z, 2);
|
||||
|
||||
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||
Tessellator.instance.setColorOpaque(255, 255, 255);
|
||||
if (lilypadRoots.hasIcons()) renderCrossedSideQuads(new Double3(x + 0.5, y + 0.015, z + 0.5), ForgeDirection.DOWN,
|
||||
0.2, 0.3,
|
||||
null, 0.0,
|
||||
lilypadRoots.get(iconVariation), 2,
|
||||
true);
|
||||
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], Config.lilypadHOffset,
|
||||
lilypadFlowers.get(iconVariation), 0,
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
lilypadFlowers.registerIcons(event.map);
|
||||
lilypadRoots.registerIcons(event.map);
|
||||
BetterFoliage.log.info(String.format("Found %d lilypad flower textures", lilypadFlowers.numLoaded));
|
||||
BetterFoliage.log.info(String.format("Found %d lilypad root textures", lilypadRoots.numLoaded));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
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.material.Material;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
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;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderBlockBetterReed extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||
|
||||
public IconSet reedBottomIcons = new IconSet("bf_reed_bottom", "bettergrassandleaves:better_reed_%d");
|
||||
public IconSet reedTopIcons = new IconSet("bf_reed_top", "bettergrassandleaves:better_reed_%d");
|
||||
public NoiseGeneratorSimplex noise;
|
||||
|
||||
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||
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;
|
||||
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||
return terrainVariation < Config.reedPopulation;
|
||||
}
|
||||
|
||||
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);
|
||||
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||
|
||||
IIcon bottomIcon = reedBottomIcons.get(iconVariation);
|
||||
IIcon topIcon = reedTopIcons.get(iconVariation);
|
||||
if (bottomIcon == null || topIcon == null) return true;
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
reedBottomIcons.registerIcons(event.map);
|
||||
reedTopIcons.registerIcons(event.map);
|
||||
BetterFoliage.log.info(String.format("Found %d reed textures", reedBottomIcons.numLoaded));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleWorldLoad(WorldEvent.Load event) {
|
||||
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
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.Utils;
|
||||
import net.minecraft.block.Block;
|
||||
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) {
|
||||
return Config.ctxGrassAggressiveEnabled &&
|
||||
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 = Utils.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package mods.betterfoliage.client.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import mods.betterfoliage.BetterFoliage;
|
||||
import mods.betterfoliage.common.util.Utils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
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 {
|
||||
|
||||
/** Resource domain name of generated textures */
|
||||
public String domainName;
|
||||
|
||||
/** Resource location for fallback texture (if the generation process fails) */
|
||||
public ResourceLocation missingResource;
|
||||
|
||||
/** Texture atlas for block textures used in the current run */
|
||||
public TextureMap blockTextures;
|
||||
|
||||
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
|
||||
this.domainName = domainName;
|
||||
this.missingResource = missingResource;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
blockTextures = event.map;
|
||||
|
||||
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||
if (domainManagers == null) {
|
||||
BetterFoliage.log.warn("Failed to inject texture generator");
|
||||
return;
|
||||
}
|
||||
domainManagers.put(domainName, this);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||
blockTextures = null;
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
// don't leave a mess
|
||||
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||
if (domainManagers != null) domainManagers.remove(domainName);
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains() {
|
||||
return ImmutableSet.<String>of(domainName);
|
||||
}
|
||||
|
||||
public List<IResource> getAllResources(ResourceLocation resource) throws IOException {
|
||||
return ImmutableList.<IResource>of(getResource(resource));
|
||||
}
|
||||
|
||||
public IResource getMissingResource() throws IOException {
|
||||
return Minecraft.getMinecraft().getResourceManager().getResource(missingResource);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.Post;
|
||||
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(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));
|
||||
}
|
||||
}
|
||||
@@ -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.Utils;
|
||||
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 (Utils.resourceExists(handDrawnLocation)) {
|
||||
drawnCounter++;
|
||||
return resourceManager.getResource(handDrawnLocation);
|
||||
}
|
||||
|
||||
// generate our own
|
||||
if (!Utils.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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package mods.betterfoliage.client.resource;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import mods.betterfoliage.client.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 texture 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 IconSet icons = new IconSet("betterfoliage", "falling_leaf_default_%d");
|
||||
|
||||
/** Map of average color values */
|
||||
public Map<IIcon, Integer> colors = Maps.newHashMap();
|
||||
|
||||
/** Default color value */
|
||||
public int defaultColor = 0x208040;
|
||||
|
||||
public LeafParticleTextures(int defaultColor) {
|
||||
this.defaultColor = defaultColor;
|
||||
}
|
||||
|
||||
public int getColor(IIcon icon) {
|
||||
Integer result = colors.get(icon);
|
||||
return result == null ? defaultColor : result;
|
||||
}
|
||||
|
||||
/** Calculate average color value (in HSB color space) for a texture and store it in the map.
|
||||
* @param icon texture
|
||||
*/
|
||||
protected void addAtlasTexture(TextureAtlasSprite icon) {
|
||||
ResourceLocation locationNoDirs = new ResourceLocation(icon.getIconName());
|
||||
ResourceLocation locationWithDirs = new ResourceLocation(locationNoDirs.getResourceDomain(), String.format("textures/blocks/%s.png", locationNoDirs.getResourcePath()));
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(locationWithDirs).getInputStream());
|
||||
|
||||
int numOpaque = 0;
|
||||
float sumHueX = 0.0f;
|
||||
float sumHueY = 0.0f;
|
||||
float sumSaturation = 0.0f;
|
||||
float sumBrightness = 0.0f;
|
||||
for (int x = 0; x < image.getWidth(); x++) for (int y = 0; y < image.getHeight(); y++) {
|
||||
int pixel = image.getRGB(x, y);
|
||||
int alpha = (pixel >> 24) & 0xFF;
|
||||
float[] hsbVals = Color.RGBtoHSB((pixel >> 16) & 0xFF, (pixel >> 8) & 0xFF, pixel & 0xFF, null);
|
||||
if (alpha == 255) {
|
||||
numOpaque++;
|
||||
sumHueX += Math.cos((hsbVals[0] - 0.5) * 2.0 * Math.PI);
|
||||
sumHueY += Math.sin((hsbVals[0] - 0.5) * 2.0 * Math.PI);
|
||||
sumSaturation += hsbVals[1];
|
||||
sumBrightness += hsbVals[2];
|
||||
}
|
||||
}
|
||||
|
||||
// average hue as usual for circular values - transform average unit vector back to polar angle
|
||||
float avgHue = (float) (Math.atan2(sumHueY, sumHueX) / (2.0 * Math.PI) + 0.5);
|
||||
colors.put(icon, Color.HSBtoRGB(avgHue, sumSaturation / numOpaque, sumBrightness / numOpaque));
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
colors.clear();
|
||||
icons.registerIcons(event.map);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleRegisterTexture(LeafTextureFoundEvent event) {
|
||||
addAtlasTexture(event.icon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
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.common.util.Utils;
|
||||
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.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
|
||||
Map<String, TextureAtlasSprite> mapAtlas = null;
|
||||
mapAtlas = Utils.getField(blockTextures, DeobfHelper.transformElementSearge("mapRegisteredSprites"), Map.class);
|
||||
if (mapAtlas == null) mapAtlas = Utils.getField(blockTextures, "mapRegisteredSprites", Map.class);
|
||||
|
||||
if (mapAtlas == null) {
|
||||
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
blockTextures = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
306
src/main/java/mods/betterfoliage/common/config/Config.java
Normal file
@@ -0,0 +1,306 @@
|
||||
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.Utils;
|
||||
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.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
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, fallingLeaves, 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 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");
|
||||
|
||||
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", "population");
|
||||
setOrder(Category.coral, "enabled", "hOffset", "vOffset", "size", "crustSize", "population", "chance");
|
||||
setOrder(Category.fallingLeaves, "enabled", "size", "chance", "lifetime", "speed", "windStrength", "stormStrength", "perturb");
|
||||
setOrder(Category.connectedGrass, "classic", "aggressive");
|
||||
}
|
||||
|
||||
public static void getDefaultBiomes() {
|
||||
for (BiomeGenBase biome : BiomeGenBase.getBiomeGenArray()) {
|
||||
if (biome == null) continue;
|
||||
if (Utils.biomeTempRainFilter(0.4f, null, 0.4f, null).apply(biome)) {
|
||||
reedBiomeList.add(biome.biomeID);
|
||||
BiomeListConfigEntry.reedBiomeList.add(biome);
|
||||
}
|
||||
if (Utils.biomeClassNameFilter("river", "ocean").apply(biome)) {
|
||||
algaeBiomeList.add(biome.biomeID);
|
||||
BiomeListConfigEntry.algaeBiomeList.add(biome);
|
||||
}
|
||||
if (Utils.biomeClassNameFilter("river", "ocean", "beach").apply(biome)) {
|
||||
coralBiomeList.add(biome.biomeID);
|
||||
BiomeListConfigEntry.coralBiomeList.add(biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static List<Integer> getFilteredBiomeIds(List<BiomeGenBase> biomes, Predicate<BiomeGenBase> filter) {
|
||||
return Lists.newArrayList(Collections2.transform(
|
||||
Collections2.filter(biomes, filter),
|
||||
new Function<BiomeGenBase, Integer>() {
|
||||
public Integer apply(BiomeGenBase input) {
|
||||
return input.biomeID;
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/main/java/mods/betterfoliage/common/util/Double3.java
Normal file
@@ -0,0 +1,49 @@
|
||||
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;
|
||||
public final double y;
|
||||
public final double z;
|
||||
|
||||
public Double3(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Double3(ForgeDirection dir) {
|
||||
this.x = dir.offsetX;
|
||||
this.y = dir.offsetY;
|
||||
this.z = dir.offsetZ;
|
||||
}
|
||||
|
||||
public Double3 add(Double3 other) {
|
||||
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);
|
||||
}
|
||||
|
||||
public Double3 scaleAxes(double sx, double sy, double sz) {
|
||||
return new Double3(x * sx, y * sy, z * sz);
|
||||
}
|
||||
|
||||
public Double3 scale(double s) {
|
||||
return new Double3(x * s, y * s, z * s);
|
||||
}
|
||||
|
||||
public Double3 inverse() {
|
||||
return new Double3(-x, -y, -z);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
157
src/main/java/mods/betterfoliage/common/util/Utils.java
Normal file
@@ -0,0 +1,157 @@
|
||||
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.lang.reflect.Field;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.EnumChatFormatting;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
|
||||
public class Utils {
|
||||
|
||||
/** Hide constructor */
|
||||
private Utils() {}
|
||||
|
||||
/**
|
||||
* @return (({@link SimpleReloadableResourceManager}) Minecraft.getMinecraft().getResourceManager()).domainResourceManagers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, IResourceManager> getDomainResourceManagers() {
|
||||
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
|
||||
Map<String, IResourceManager> result = getField(manager, DeobfHelper.transformElementSearge("domainResourceManagers"), Map.class);
|
||||
if (result == null) result = getField(manager, "domainResourceManagers", 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package mods.betterfoliage.loader;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
|
||||
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
||||
|
||||
public String[] getASMTransformerClass() {
|
||||
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
|
||||
}
|
||||
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void injectData(Map<String, Object> data) {
|
||||
}
|
||||
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package mods.betterfoliage.loader;
|
||||
|
||||
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.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
84
src/main/java/mods/betterfoliage/loader/DeobfHelper.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.BlockDirt
|
||||
|
||||
// Enhanced Biomes
|
||||
enhancedbiomes.blocks.BlockSoilEB
|
||||
@@ -0,0 +1,5 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.BlockGrass
|
||||
|
||||
// Enhanced Biomes
|
||||
enhancedbiomes.blocks.BlockGrassEB
|
||||
@@ -0,0 +1,4 @@
|
||||
net.minecraft.block.BlockLeavesBase
|
||||
forestry.arboriculture.gadgets.BlockLeaves
|
||||
thaumcraft.common.blocks.BlockMagicalLeaves
|
||||
-tconstruct.blocks.OreberryBushEssence
|
||||
103
src/main/resources/assets/betterfoliage/lang/en_US.lang
Normal file
@@ -0,0 +1,103 @@
|
||||
key.betterfoliage.gui=Open Settings
|
||||
betterfoliage.arrayEntryDisplay=%d entries
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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.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.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
|
||||
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 275 B |
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 254 B |
|
After Width: | Height: | Size: 219 B |
|
After Width: | Height: | Size: 305 B |
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.9 KiB |