Compare commits

110 Commits

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

8
.classpath Normal file
View 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>

10
.gitignore vendored
View File

@@ -1,9 +1,5 @@
.idea/
*.iml
*.ipr
*.iws
run/
.gradle/
.settings/
bin/
build/
classes/
temp/
libs/

18
.project Normal file
View File

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

View File

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

45
build.gradle Normal file
View File

@@ -0,0 +1,45 @@
buildscript {
repositories {
mavenCentral()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
}
}
apply plugin: 'forge'
minecraft {
version = '1.7.2-10.12.2.1147'
}
jar.baseName = 'BetterFoliage'
group = 'com.github.octarine-noise'
version='1.0.0'
processResources {
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
expand 'version':project.version, 'mcversion':project.minecraft.version
}
from(sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
}
}
jar {
manifest {
attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
}
}

View File

@@ -1,60 +0,0 @@
plugins {
kotlin("jvm").version("1.3.61")
id("net.minecraftforge.gradle").version("3.0.157")
id("org.spongepowered.mixin").version("0.7-SNAPSHOT")
}
apply(plugin = "org.spongepowered.mixin")
repositories {
maven("http://files.minecraftforge.net/maven")
maven("https://repo.spongepowered.org/maven")
maven("https://minecraft.curseforge.com/api/maven")
}
dependencies {
"minecraft"("net.minecraftforge:forge:${properties["mcVersion"]}-${properties["forgeVersion"]}")
"implementation"("kottle:Kottle:${properties["kottleVersion"]}")
"implementation"("org.spongepowered:mixin:0.8-SNAPSHOT")
}
configurations["annotationProcessor"].extendsFrom(configurations["implementation"])
sourceSets {
get("main").ext["refMap"] = "betterfoliage.refmap.json"
}
minecraft {
mappings(properties["mappingsChannel"] as String, properties["mappingsVersion"] as String)
accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg"))
runs.create("client") {
workingDirectory(file("run"))
properties["forge.logging.markers"] = "CORE"
properties["forge.logging.console.level"] = "debug"
mods.create("betterfoliage") {
source(sourceSets["main"])
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlin {
target.compilations.configureEach {
kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions")
}
}
tasks.getByName<Jar>("jar") {
archiveName = "BetterFoliage-${project.version}-Forge-${properties["mcVersion"]}.jar"
manifest {
from(file("src/main/resources/META-INF/MANIFEST.MF"))
attributes["Implementation-Version"] = project.version
}
exclude("net")
filesMatching("META-INF/mods.toml") { expand(project.properties) }
filesMatching("mcmod.info") { expand(project.properties) }
}

View File

@@ -1,14 +0,0 @@
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
group = com.github.octarine-noise
jarName = BetterFoliage-Forge
version = 2.5.1
mcVersion = 1.14.4
forgeVersion = 28.2.0
mappingsChannel = snapshot
mappingsVersion = 20190719-1.14.3
kottleVersion = 1.4.0

Binary file not shown.

View File

@@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored
View File

@@ -1,172 +0,0 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored
View File

@@ -1,84 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,13 +0,0 @@
pluginManagement {
repositories {
maven("http://files.minecraftforge.net/maven")
maven("https://repo.spongepowered.org/maven")
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.let { it.namespace == "net.minecraftforge" && it.name == "gradle"} ) useModule("net.minecraftforge.gradle:ForgeGradle:${requested.version}")
if (requested.id.let { it.namespace == "org.spongepowered" && it.name == "mixin"} ) useModule("org.spongepowered:mixingradle:${requested.version}")
}
}
}

View 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 postInit(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;
}
}

View File

@@ -1,19 +0,0 @@
package mods.betterfoliage;
import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.connect.IMixinConnector;
public class MixinConnector implements IMixinConnector {
@Override
public void connect() {
Mixins.addConfiguration("betterfoliage.common.mixins.json");
try {
Class.forName("optifine.OptiFineTransformationService");
Mixins.addConfiguration("betterfoliage.optifine.mixins.json");
} catch (ClassNotFoundException e) {
Mixins.addConfiguration("betterfoliage.vanilla.mixins.json");
}
}
}

View File

@@ -0,0 +1,128 @@
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.EntityFXRisingSoul;
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral;
import mods.betterfoliage.client.render.impl.RenderBlockBetterGrass;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad;
import mods.betterfoliage.client.render.impl.RenderBlockBetterMycelium;
import mods.betterfoliage.client.render.impl.RenderBlockBetterNetherrack;
import mods.betterfoliage.client.render.impl.RenderBlockBetterReed;
import mods.betterfoliage.client.render.impl.RenderBlocksBetterGrassSide;
import mods.betterfoliage.client.resource.LeafGenerator;
import mods.betterfoliage.client.resource.LeafParticleTextures;
import mods.betterfoliage.client.resource.LeafTextureEnumerator;
import mods.betterfoliage.client.resource.ReedGenerator;
import mods.betterfoliage.client.resource.ShortGrassGenerator;
import mods.betterfoliage.client.resource.SoulParticleTextures;
import mods.betterfoliage.common.config.Config;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.init.Blocks;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.collect.Maps;
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 SoulParticleTextures soulParticles = new SoulParticleTextures();
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 RenderBlockBetterNetherrack());
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(soulParticles);
MinecraftForge.EVENT_BUS.register(leafGenerator);
MinecraftForge.EVENT_BUS.register(leafParticles);
MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator());
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true));
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_top", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass_snow", missingTexture, true));
ShadersModIntegration.init();
}
public 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.soulFXEnabled) {
if (world.getBlock(x, y, z) == Blocks.soul_sand && Math.random() < Config.soulFXChance) {
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXRisingSoul(world, x, y, z));
return;
}
}
if (Config.leafFXEnabled) {
if (Config.leaves.matchesID(block) && world.isAirBlock(x, y - 1, z) && Math.random() < Config.leafFXChance) {
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z));
return;
}
}
}
public static void registerRenderer(IRenderBlockDecorator decorator) {
int renderId = RenderingRegistry.getNextAvailableRenderId();
decorators.put(renderId, decorator);
RenderingRegistry.registerBlockHandler(renderId, decorator);
MinecraftForge.EVENT_BUS.register(decorator);
decorator.init();
}
}

View File

@@ -0,0 +1,77 @@
package mods.betterfoliage.client;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.block.Block;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.world.WorldEvent;
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) {
for (String line : ResourceUtils.getLines(defaults)) {
if (line.startsWith("-"))
blacklist.add(line.substring(1));
else
whitelist.add(line);
}
}
/** Caches block IDs on world load for fast lookup
* @param event
*/
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
if (event.world instanceof WorldClient) updateBlockIDs();
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,52 @@
package mods.betterfoliage.client.render;
import mods.betterfoliage.common.util.ResourceUtils;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** 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 (ResourceUtils.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;
}
}

View File

@@ -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.RenderUtils;
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 = RenderUtils.getRenderingHandler(block.getRenderType());
if (handler != null) {
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
} else {
renderStandardBlock(block, x, y, z);
}
}
protected void renderStandardBlockAsItem(RenderBlocks renderer, Block p_147800_1_, int p_147800_2_, float p_147800_3_) {
Tessellator tessellator = Tessellator.instance;
boolean flag = p_147800_1_ == Blocks.grass;
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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,137 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.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;
boolean isSnowTop;
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
Material topMaterial = blockAccess.getBlock(x, y + 1, z).getMaterial();
isSnowTop = (topMaterial == Material.snow || topMaterial == Material.craftedSnow);
checkConnectedGrass(x, y, z);
grassTopIcon = block.getIcon(blockAccess, x, y, z, ForgeDirection.UP.ordinal());
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
if (!Config.grassEnabled) return true;
boolean isAirTop = blockAccess.isAirBlock(x, y + 1, z);
if (isSnowTop || isAirTop) {
// render short grass
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
double scale = Config.grassSize * 0.5;
double halfHeight = 0.5 * (Config.grassHeightMin + pRand[heightVariation] * (Config.grassHeightMax - Config.grassHeightMin));
IIcon shortGrassIcon = null;
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
shortGrassIcon = Config.grassUseGenerated ? snowGrassGenIcon : snowGrassIcons.get(iconVariation);
} else {
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
shortGrassIcon = Config.grassUseGenerated ? grassGenIcon : grassIcons.get(iconVariation);
}
if (shortGrassIcon == null) return true;
ShadersModIntegration.startGrassQuads();
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowTop ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[iconVariation], Config.grassHOffset, shortGrassIcon, 0, false);
}
return true;
}
protected void checkConnectedGrass(int x, int y, int z) {
if (isSnowTop) {
connectXP = false;
connectXN = false;
connectZP = false;
connectZN = false;
return;
}
Block blockBelow = blockAccess.getBlock(x, y - 1, z);
if (Config.ctxGrassAggressiveEnabled && (Config.grass.matchesID(blockBelow) || Config.dirt.matchesID(blockBelow))) {
connectXP = true;
connectXN = true;
connectZP = true;
connectZN = true;
} else if (Config.ctxGrassClassicEnabled) {
connectXP = Config.grass.matchesID(blockAccess.getBlock(x + 1, y - 1, z));
connectXN = Config.grass.matchesID(blockAccess.getBlock(x - 1, y - 1, z));
connectZP = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z + 1));
connectZN = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z - 1));
} else {
connectXP = false;
connectXN = false;
connectZP = false;
connectZN = false;
}
}
@Override
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZNeg(block, x, y, z, connectZN ? grassTopIcon : icon);
}
@Override
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZPos(block, x, y, z, connectZP ? grassTopIcon : icon);
}
@Override
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXNeg(block, x, y, z, connectXN ? grassTopIcon : icon);
}
@Override
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXPos(block, x, y, z, connectXP ? grassTopIcon : icon);
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
grassIcons.registerIcons(event.map);
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));
}
}

View File

@@ -0,0 +1,114 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.config.Config;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet snowedLeavesIcons = new IconSet("bettergrassandleaves", "better_leaves_snowed_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!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 = blockAccess.isAirBlock(x, y + 1, z);
boolean isAirBottom = blockAccess.isAirBlock(x, y - 1, z);
boolean isSnowTop = blockAccess.getBlock(x, y + 1, z).getMaterial() == Material.snow;
Tessellator.instance.setBrightness(isAirTop ? getBrightness(block, x, y + 1, z) : (isAirBottom ? getBrightness(block, x, y - 1, z) : getBrightness(block, x, y, z)));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
Double3 offset1 = pRot[offsetVariation].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
Double3 offset2 = pRot[(offsetVariation + 1) & 63].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
if (Config.leavesSkew) {
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
}
} else {
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
if (isSnowTop) {
// clear biome colors
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
}
}
return true;
}
protected boolean isBlockSurrounded(IBlockAccess blockAccess, int x, int y, int z) {
if (isBlockNonSurrounding(blockAccess.getBlock(x + 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x - 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y + 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y - 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z + 1))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z - 1))) return false;
return true;
}
protected boolean isBlockNonSurrounding(Block block) {
return block.getMaterial() == Material.air || block == Blocks.snow_layer;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
snowedLeavesIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d snowed leaves textures", snowedLeavesIcons.numLoaded));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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.ResourceUtils;
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 = ResourceUtils.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 = ResourceUtils.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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
package mods.betterfoliage.common.util;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.minecraft.util.EnumChatFormatting;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
public class RenderUtils {
/** Hide constructor */
private RenderUtils() {}
/** Retrieve a specific rendering handler from the registry
* @param renderType render type of block
* @return {@link ISimpleBlockRenderingHandler} if defined, null otherwise
*/
@SuppressWarnings("unchecked")
public static ISimpleBlockRenderingHandler getRenderingHandler(int renderType) {
try {
Field field = RenderingRegistry.class.getDeclaredField("INSTANCE");
field.setAccessible(true);
RenderingRegistry inst = (RenderingRegistry) field.get(null);
field = RenderingRegistry.class.getDeclaredField("blockRenderers");
field.setAccessible(true);
return ((Map<Integer, ISimpleBlockRenderingHandler>) field.get(inst)).get(renderType);
} catch (Exception e) {
return null;
}
}
public static void stripTooltipDefaultText(List<String> tooltip) {
boolean defaultRows = false;
Iterator<String> iter = tooltip.iterator();
while(iter.hasNext()) {
if (iter.next().startsWith(EnumChatFormatting.AQUA.toString())) defaultRows = true;
if (defaultRows) iter.remove();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,29 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Mixin overriding the {@link VoxelShape} used for the neighbor block in {@link Block}.shouldSideBeRendered().
*
* This way log blocks can be made to not block rendering, without altering any {@link Block} or
* {@link BlockState} properties with potential gameplay ramifications.
*/
@Mixin(Block.class)
public class MixinBlock {
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getVoxelShape, ordinal = 1))
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
}
}

View File

@@ -1,27 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Mixin to override the result of {@link BlockState}.getAmbientOcclusionLightValue().
*
* Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
*/
@Mixin(BlockState.class)
@SuppressWarnings({"UnnecessaryQualifiedMemberReference", "deprecation"})
public class MixinBlockState {
private static final String callFrom = "Lnet/minecraft/block/BlockState;func_215703_d(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
private static final String callTo = "Lnet/minecraft/block/Block;func_220080_a(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
float getAmbientOcclusionValue(Block block, BlockState state, IBlockReader reader, BlockPos pos) {
return Hooks.getAmbientOcclusionLightValueOverride(block.func_220080_a(state, reader, pos), state);
}
}

View File

@@ -1,28 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.chunk.ChunkRender;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IEnviromentBlockReader;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.IModelData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Random;
@Mixin(ChunkRender.class)
public class MixinChunkRender {
private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
private static final String renderBlock = "Lnet/minecraft/client/renderer/BlockRendererDispatcher;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/IEnviromentBlockReader;Lnet/minecraft/client/renderer/BufferBuilder;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Z";
@Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = renderBlock))
public boolean renderBlock(BlockRendererDispatcher dispatcher, BlockState state, BlockPos pos, IEnviromentBlockReader reader, BufferBuilder buffer, Random random, IModelData modelData) {
return Hooks.renderWorldBlock(dispatcher, state, pos, reader, buffer, random, modelData, MinecraftForgeClient.getRenderLayer());
}
}

View File

@@ -1,21 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.chunk.ChunkRender;
import net.minecraft.util.BlockRenderLayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ChunkRender.class)
public class MixinChunkRenderVanilla {
private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
private static final String canRenderInLayer = "Lnet/minecraft/block/BlockState;canRenderInLayer(Lnet/minecraft/util/BlockRenderLayer;)Z";
@Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = canRenderInLayer))
boolean canRenderInLayer(BlockState state, BlockRenderLayer layer) {
return Hooks.canRenderInLayerOverride(state, layer);
}
}

View File

@@ -1,44 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Random;
@Mixin(ClientWorld.class)
public class MixinClientWorld {
private static final String worldAnimateTick = "Lnet/minecraft/client/world/ClientWorld;animateTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$MutableBlockPos;)V";
private static final String blockAnimateTick = "Lnet/minecraft/block/Block;animateTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
private static final String worldNotify = "Lnet/minecraft/client/world/ClientWorld;notifyBlockUpdate(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
private static final String rendererNotify = "Lnet/minecraft/client/renderer/WorldRenderer;notifyBlockUpdate(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
/**
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
*/
@Redirect(method = worldAnimateTick, at = @At(value = "INVOKE", target = blockAnimateTick))
void onAnimateTick(Block block, BlockState state, World world, BlockPos pos, Random random) {
Hooks.onRandomDisplayTick(block, state, world, pos, random);
block.animateTick(state, world, pos, random);
}
/**
* Inject callback to get notified of client-side blockstate changes.
* Used to invalidate caches in the {@link mods.betterfoliage.client.chunk.ChunkOverlayManager}
*/
@Redirect(method = worldNotify, at = @At(value = "INVOKE", target = rendererNotify))
void onClientBlockChanged(WorldRenderer renderer, IBlockReader world, BlockPos pos, BlockState oldState, BlockState newState, int flags) {
Hooks.onClientBlockChanged((ClientWorld) world, pos, oldState, newState, flags);
renderer.notifyBlockUpdate(world, pos, oldState, newState, flags);
}
}

View File

@@ -1,29 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.BetterFoliage;
import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.profiler.IProfiler;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ModelBakery.class)
abstract public class MixinModelBakery {
private static final String processLoading = "processLoading(Lnet/minecraft/profiler/IProfiler;)V";
private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/lang/Iterable;Lnet/minecraft/profiler/IProfiler;)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;";
@Redirect(method = processLoading, at = @At(value = "INVOKE", target = stitch))
AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable<ResourceLocation> idList, IProfiler profiler) {
return BetterFoliage.INSTANCE.getBlockSprites().finish(
atlas.stitch(
manager,
BetterFoliage.INSTANCE.getBlockSprites().prepare(this, manager, idList, profiler),
profiler
), profiler
);
}
}

View File

@@ -1,24 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.BlockState;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.optifine.util.BlockUtils;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockUtils.class)
public class MixinOptifineBlockUtils {
private static final String shouldSideBeRenderedCached = "shouldSideBeRenderedCached(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Lnet/optifine/render/RenderEnv;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)Z";
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
@SuppressWarnings("UnresolvedMixinReference")
@Redirect(method = shouldSideBeRenderedCached, at = @At(value = "INVOKE", target = getVoxelShape, ordinal = 1))
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
}
}

View File

@@ -1,31 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.chunk.ChunkRender;
import net.minecraft.util.BlockRenderLayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Coerce;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.Slice;
@Mixin(ChunkRender.class)
public class MixinOptifineChunkRender {
private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
private static final String invokeReflector = "Lnet/optifine/reflect/Reflector;callBoolean(Ljava/lang/Object;Lnet/optifine/reflect/ReflectorMethod;[Ljava/lang/Object;)Z";
private static final String forgeBlockCanRender = "Lnet/minecraft/client/renderer/chunk/ChunkRender;FORGE_BLOCK_CAN_RENDER_IN_LAYER:Z";
@Redirect(
method = rebuildChunk,
at = @At(value = "INVOKE", target = invokeReflector),
slice = @Slice(
from = @At(value = "FIELD", target = forgeBlockCanRender)
)
)
@SuppressWarnings("UnresolvedMixinReference")
boolean canRenderInLayer(Object state, @Coerce Object reflector, Object[] layer) {
return Hooks.canRenderInLayerOverride((BlockState) state, (BlockRenderLayer) layer[0]);
}
}

View File

@@ -1,33 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.BetterFoliage;
import mods.octarinecore.client.resource.AsnycSpriteProviderManager;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.profiler.IProfiler;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ParticleManager.class)
public class MixinParticleManager {
private static final String reload = "reload(Lnet/minecraft/resources/IFutureReloadListener$IStage;Lnet/minecraft/resources/IResourceManager;Lnet/minecraft/profiler/IProfiler;Lnet/minecraft/profiler/IProfiler;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;";
private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/lang/Iterable;Lnet/minecraft/profiler/IProfiler;)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;";
// ewww :S
@SuppressWarnings("UnresolvedMixinReference")
@Redirect(method = "*", at = @At(value = "INVOKE", target = stitch))
AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable<ResourceLocation> idList, IProfiler profiler) {
return BetterFoliage.INSTANCE.getParticleSprites().finish(
atlas.stitch(
manager,
BetterFoliage.INSTANCE.getParticleSprites().prepare(this, manager, idList, profiler),
profiler
), profiler
);
}
}

View File

@@ -1,24 +0,0 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.client.integration.ShadersModIntegration;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IEnviromentBlockReader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(BlockModelRenderer.class)
public class MixinShadersBlockModelRenderer {
private static final String renderModel = "renderModel(Lnet/minecraft/world/IEnviromentBlockReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/renderer/BufferBuilder;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z";
private static final String pushEntity = "Lnet/optifine/shaders/SVertexBuilder;pushEntity(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/IEnviromentBlockReader;Lnet/minecraft/client/renderer/BufferBuilder;)V";
@SuppressWarnings("UnresolvedMixinReference")
@ModifyArg(method = renderModel, at = @At(value = "INVOKE", target = pushEntity), remap = false)
BlockState overrideBlockState(BlockState state, BlockPos pos, IEnviromentBlockReader world, BufferBuilder buffer) {
return ShadersModIntegration.getBlockStateOverride(state, world, pos);
}
}

View File

@@ -1,5 +0,0 @@
package net.optifine.util;
public class BlockUtils {
// whyyyy?
}

View File

@@ -1,76 +0,0 @@
package mods.betterfoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.render.RisingSoulTextures
import mods.octarinecore.client.gui.textComponent
import mods.octarinecore.client.resource.AsnycSpriteProviderManager
import mods.octarinecore.client.resource.AsyncSpriteProvider
import mods.octarinecore.client.resource.Atlas
import mods.octarinecore.client.resource.GeneratedBlockTexturePack
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.particle.ParticleManager
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.util.math.BlockPos
import net.minecraft.util.text.TextFormatting
import net.minecraft.util.text.TranslationTextComponent
import net.minecraftforge.registries.ForgeRegistries
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.simple.SimpleLogger
import org.apache.logging.log4j.util.PropertiesUtil
import java.io.File
import java.io.PrintStream
import java.util.*
object BetterFoliage {
var log = LogManager.getLogger("BetterFoliage")
var logDetail = SimpleLogger(
"BetterFoliage",
Level.DEBUG,
false, false, true, false,
"yyyy-MM-dd HH:mm:ss",
null,
PropertiesUtil(Properties()),
PrintStream(File("logs/betterfoliage.log").apply {
parentFile.mkdirs()
if (!exists()) createNewFile()
})
)
val blockSprites = AsnycSpriteProviderManager<ModelBakery>("bf-blocks-extra")
val particleSprites = AsnycSpriteProviderManager<ParticleManager>("bf-particles-extra")
val asyncPack = GeneratedBlockTexturePack("bf_gen", "Better Foliage generated assets", logDetail)
fun getSpriteManager(atlas: Atlas) = when(atlas) {
Atlas.BLOCKS -> blockSprites
Atlas.PARTICLES -> particleSprites
} as AsnycSpriteProviderManager<Any>
init {
blockSprites.providers.add(asyncPack)
}
fun log(level: Level, msg: String) {
log.log(level, "[BetterFoliage] $msg")
logDetail.log(level, msg)
}
fun logDetail(msg: String) {
logDetail.log(Level.DEBUG, msg)
}
fun logRenderError(state: BlockState, location: BlockPos) {
if (state in Client.suppressRenderErrors) return
Client.suppressRenderErrors.add(state)
val blockName = ForgeRegistries.BLOCKS.getKey(state.block).toString()
val blockLoc = "${location.x},${location.y},${location.z}"
Minecraft.getInstance().ingameGUI.chatGUI.printChatMessage(TranslationTextComponent(
"betterfoliage.rendererror",
textComponent(blockName, TextFormatting.GOLD),
textComponent(blockLoc, TextFormatting.GOLD)
))
logDetail("Error rendering block $state at $blockLoc")
}
}

View File

@@ -1,35 +0,0 @@
package mods.betterfoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.resource.AsnycSpriteProviderManager
import mods.octarinecore.client.resource.GeneratedBlockTexturePack
import net.alexwells.kottle.FMLKotlinModLoadingContext
import net.minecraft.client.Minecraft
import net.minecraft.client.particle.ParticleManager
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.config.ModConfig
import org.apache.logging.log4j.Level.DEBUG
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.simple.SimpleLogger
import org.apache.logging.log4j.util.PropertiesUtil
import java.io.File
import java.io.PrintStream
import java.util.*
@Mod(BetterFoliageMod.MOD_ID)
object BetterFoliageMod {
const val MOD_ID = "betterfoliage"
val bus = FMLKotlinModLoadingContext.get().modEventBus
init {
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build())
Minecraft.getInstance().resourcePackList.addPackFinder(BetterFoliage.asyncPack.finder)
bus.register(BlockConfig)
Client.init()
}
}

View File

@@ -1,78 +0,0 @@
package mods.betterfoliage.client
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.integration.*
import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.texture.AsyncGrassDiscovery
import mods.betterfoliage.client.texture.AsyncLeafDiscovery
import mods.betterfoliage.client.texture.LeafParticleRegistry
import mods.octarinecore.client.gui.textComponent
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.resource.IConfigChangeListener
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.util.math.BlockPos
import net.minecraft.util.text.TextFormatting
import net.minecraft.util.text.TranslationTextComponent
import net.minecraftforge.registries.ForgeRegistries
import org.apache.logging.log4j.Level
/**
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
* except for the call hooks.
*/
object Client {
var renderers= emptyList<RenderDecorator>()
var configListeners = emptyList<IConfigChangeListener>()
val suppressRenderErrors = mutableSetOf<BlockState>()
fun init() {
// init renderers
renderers = listOf(
RenderGrass(),
RenderMycelium(),
RenderLeaves(),
RenderCactus(),
RenderLilypad(),
RenderReeds(),
RenderAlgae(),
RenderCoral(),
RenderLog(),
RenderNetherrack(),
RenderConnectedGrass(),
RenderConnectedGrassLog()
)
// init other singletons
val singletons = listOf(
BlockConfig,
ChunkOverlayManager,
LeafWindTracker,
RisingSoulTextures
)
// init mod integrations
val integrations = listOf(
ShadersModIntegration,
OptifineCustomColors
// ForestryIntegration,
// IC2RubberIntegration,
// TechRebornRubberIntegration
)
LeafParticleRegistry.init()
// add basic block support instances as last
AsyncLeafDiscovery.init()
AsyncGrassDiscovery.init()
AsyncLogDiscovery.init()
AsyncCactusDiscovery.init()
configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>()
configListeners.forEach { it.onConfigChange() }
}
}

View File

@@ -1,112 +0,0 @@
@file:JvmName("Hooks")
package mods.betterfoliage.client
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.*
import mods.octarinecore.ThreadLocalDelegate
import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.DefaultLightingCtx
import mods.octarinecore.common.plus
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.BufferBuilder
import net.minecraft.client.world.ClientWorld
import net.minecraft.util.BlockRenderLayer
import net.minecraft.util.BlockRenderLayer.CUTOUT
import net.minecraft.util.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.util.Direction
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.shapes.VoxelShape
import net.minecraft.util.math.shapes.VoxelShapes
import net.minecraft.world.IBlockReader
import net.minecraft.world.IEnviromentBlockReader
import net.minecraft.world.World
import net.minecraftforge.client.model.data.IModelData
import java.util.*
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat();
return original
}
fun getUseNeighborBrightnessOverride(original: Boolean, state: BlockState): Boolean {
return original || (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block));
}
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState, flags: Int) {
ChunkOverlayManager.onBlockChange(worldClient, pos)
}
fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: BlockPos, random: Random) {
if (Config.enabled &&
Config.risingSoul.enabled &&
state.block == Blocks.SOUL_SAND &&
world.isAirBlock(pos + up1) &&
Math.random() < Config.risingSoul.chance) {
EntityRisingSoulFX(world, pos).addIfValid()
}
if (Config.enabled &&
Config.fallingLeaves.enabled &&
BlockConfig.leafBlocks.matchesClass(state.block) &&
world.isAirBlock(pos + down1) &&
Math.random() < Config.fallingLeaves.chance) {
EntityFallingLeavesFX(world, pos).addIfValid()
}
}
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty()
return state.func_215702_a(reader, pos, dir)
}
val lightingCtx by ThreadLocalDelegate { DefaultLightingCtx(BasicBlockCtx(NonNullWorld, BlockPos.ZERO)) }
fun renderWorldBlock(dispatcher: BlockRendererDispatcher,
state: BlockState,
pos: BlockPos,
reader: IEnviromentBlockReader,
buffer: BufferBuilder,
random: Random,
modelData: IModelData,
layer: BlockRenderLayer
): Boolean {
// build context
val blockCtx = CachedBlockCtx(reader, pos)
val renderCtx = RenderCtx(dispatcher, buffer, layer, random, modelData)
lightingCtx.reset(blockCtx)
val combinedCtx = CombinedContext(blockCtx, renderCtx, lightingCtx)
// loop render decorators
val doBaseRender = state.canRenderInLayer(layer) || (layer == targetCutoutLayer && state.canRenderInLayer(otherCutoutLayer))
Client.renderers.forEach { renderer ->
if (renderer.isEligible(combinedCtx)) {
// render on the block's default layer
// also render on the cutout layer if the renderer requires it
val doCutoutRender = renderer.renderOnCutout && layer == targetCutoutLayer
val stopRender = renderer.onlyOnCutout && !layer.isCutout
if ((doBaseRender || doCutoutRender) && !stopRender) {
renderer.render(combinedCtx)
return combinedCtx.hasRendered
}
}
}
// no render decorators have taken on this block, proceed to normal rendering
combinedCtx.render()
return combinedCtx.hasRendered
}
fun canRenderInLayerOverride(state: BlockState, layer: BlockRenderLayer) = state.canRenderInLayer(layer) || layer == targetCutoutLayer
fun canRenderInLayerOverrideOptifine(state: BlockState, optifineReflector: Any?, layerArray: Array<Any>) =
canRenderInLayerOverride(state, layerArray[0] as BlockRenderLayer)
val targetCutoutLayer: BlockRenderLayer get() = if (Minecraft.getInstance().gameSettings.mipmapLevels > 0) CUTOUT_MIPPED else CUTOUT
val otherCutoutLayer: BlockRenderLayer get() = if (Minecraft.getInstance().gameSettings.mipmapLevels > 0) CUTOUT else CUTOUT_MIPPED

View File

@@ -1,125 +0,0 @@
package mods.betterfoliage.client.chunk
import mods.octarinecore.ChunkCacheOF
import mods.octarinecore.client.render.BlockCtx
import mods.octarinecore.metaprog.get
import mods.octarinecore.metaprog.isInstance
import net.minecraft.client.renderer.chunk.ChunkRenderCache
import net.minecraft.client.world.ClientWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraft.world.IEnviromentBlockReader
import net.minecraft.world.IWorldReader
import net.minecraft.world.dimension.DimensionType
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.world.ChunkEvent
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import java.util.*
import kotlin.collections.List
import kotlin.collections.MutableMap
import kotlin.collections.associateWith
import kotlin.collections.forEach
import kotlin.collections.mutableListOf
import kotlin.collections.mutableMapOf
import kotlin.collections.set
val IEnviromentBlockReader.dimType: DimensionType get() = when {
this is IWorldReader -> dimension.type
this is ChunkRenderCache -> world.dimension.type
this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].world.dimension.type
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
}
/**
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
*/
interface ChunkOverlayLayer<T> {
fun calculate(ctx: BlockCtx): T
fun onBlockUpdate(world: IEnviromentBlockReader, pos: BlockPos)
}
/**
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
*/
object ChunkOverlayManager {
var tempCounter = 0
init {
MinecraftForge.EVENT_BUS.register(this)
}
val chunkData = IdentityHashMap<DimensionType, MutableMap<ChunkPos, ChunkOverlayData>>()
val layers = mutableListOf<ChunkOverlayLayer<*>>()
/**
* Get the overlay data for a given layer and position
*
* @param layer Overlay layer to query
* @param reader World to use if calculation of overlay value is necessary
* @param pos Block position
*/
fun <T> get(layer: ChunkOverlayLayer<T>, ctx: BlockCtx): T? {
val data = chunkData[ctx.world.dimType]?.get(ChunkPos(ctx.pos)) ?: return null
data.get(layer, ctx.pos).let { value ->
if (value !== ChunkOverlayData.UNCALCULATED) return value
val newValue = layer.calculate(ctx)
data.set(layer, ctx.pos, newValue)
return newValue
}
}
/**
* Clear the overlay data for a given layer and position
*
* @param layer Overlay layer to clear
* @param pos Block position
*/
fun <T> clear(dimension: DimensionType, layer: ChunkOverlayLayer<T>, pos: BlockPos) {
chunkData[dimension]?.get(ChunkPos(pos))?.clear(layer, pos)
}
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
if (chunkData[world.dimType]?.containsKey(ChunkPos(pos)) == true) {
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
}
}
@SubscribeEvent
fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world ->
chunkData[world.dimType] = mutableMapOf()
}
@SubscribeEvent
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
chunkData.remove(world.dimType)
}
@SubscribeEvent
fun handleLoadChunk(event: ChunkEvent.Load) = (event.world as? ClientWorld)?.let { world ->
chunkData[world.dimType]?.let { chunks ->
// check for existence first because Optifine fires a TON of these
if (event.chunk.pos !in chunks.keys) chunks[event.chunk.pos] = ChunkOverlayData(layers)
}
}
@SubscribeEvent
fun handleUnloadChunk(event: ChunkEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
chunkData[world.dimType]?.remove(event.chunk.pos)
}
}
class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
val BlockPos.isValid: Boolean get() = y in validYRange
val rawData = layers.associateWith { emptyOverlay() }
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T? else null
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data) else null
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED) else null
companion object {
val UNCALCULATED = object {}
fun emptyOverlay() = Array(16) { Array(16) { Array<Any?>(256) { UNCALCULATED }}}
val validYRange = 0 until 256
}
}

View File

@@ -1,169 +0,0 @@
package mods.betterfoliage.client.config
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.common.config.*
import net.minecraft.util.ResourceLocation
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.config.ModConfig
private fun featureEnable() = boolean(true).lang("enabled")
// Config singleton
object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_ID) {
val enabled by boolean(true)
val nVidia by boolean(false)
object leaves : ConfigCategory() {
val enabled by featureEnable()
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
val size by double(min=0.75, max=2.5, default=1.4).lang("size")
val dense by boolean(false)
val hideInternal by boolean(true)
}
object shortGrass : ConfigCategory(){
val grassEnabled by boolean(true)
val myceliumEnabled by boolean(true)
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=0.1, max=2.5, default=0.6).lang("heightMin")
val heightMax by double(min=0.1, max=2.5, default=0.8).lang("heightMax")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
val population by int(max=64, default=64).lang("population")
val useGenerated by boolean(false)
val shaderWind by boolean(true).lang("shaderWind")
val saturationThreshold by double(default=0.1)
}
object connectedGrass : ConfigCategory(){
val enabled by boolean(true)
val snowEnabled by boolean(false)
}
object roundLogs : ConfigCategory(){
val enabled by featureEnable()
val radiusSmall by double(max=0.5, default=0.25)
val radiusLarge by double(max=0.5, default=0.44)
val dimming by double(default = 0.7)
val connectSolids by boolean(false)
val lenientConnect by boolean(true)
val connectPerpendicular by boolean(true)
val connectGrass by boolean(true)
val defaultY by boolean(false)
val zProtection by double(min = 0.9, default = 0.99)
}
object cactus : ConfigCategory(){
val enabled by featureEnable()
val size by double(min=0.5, max=1.5, default=0.8).lang("size")
val sizeVariation by double(max=0.5, default=0.1)
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
}
object lilypad : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
val flowerChance by int(max=64, default=16, min=0)
}
object reed : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin")
val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax")
val population by int(max=64, default=32).lang("population")
val minBiomeTemp by double(default=0.4)
val minBiomeRainfall by double(default=0.4)
// val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) }
val shaderWind by boolean(true).lang("shaderWind")
}
object algae : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin")
val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax")
val population by int(max=64, default=48).lang("population")
// val biomes by biomeList { it.filterClass("river", "ocean") }
val shaderWind by boolean(true).lang("shaderWind")
}
object coral : ConfigCategory(){
val enabled by featureEnable()
val shallowWater by boolean(false)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
val size by double(min=0.5, max=1.5, default=0.7).lang("size")
val crustSize by double(min=0.5, max=1.5, default=1.4)
val chance by int(max=64, default=32)
val population by int(max=64, default=48).lang("population")
// val biomes by biomeList { it.filterClass("river", "ocean", "beach") }
}
object netherrack : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin")
val heightMax by double(min=0.1, max=1.5, default=0.8).lang("heightMax")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
}
object fallingLeaves : ConfigCategory(){
val enabled by featureEnable()
val speed by double(min=0.01, max=0.15, default=0.05)
val windStrength by double(min=0.1, max=2.0, default=0.5)
val stormStrength by double(min=0.1, max=2.0, default=0.8)
val size by double(min=0.25, max=1.5, default=0.75).lang("size")
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=1.0, default=0.25)
val lifetime by double(min=1.0, max=15.0, default=5.0)
val opacityHack by boolean(true)
}
object risingSoul : ConfigCategory(){
val enabled by featureEnable()
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=0.25, default=0.05)
val headSize by double(min=0.25, max=1.5, default=1.0)
val trailSize by double(min=0.25, max=1.5, default=0.75)
val opacity by double(min=0.05, max=1.0, default=0.5)
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
val lifetime by double(min=1.0, max=15.0, default=4.0)
val trailLength by int(min=2, max=128, default=48)
val trailDensity by int(min=1, max=16, default=3)
}
}
object BlockConfig {
private val list = mutableListOf<Any>()
val leafBlocks = blocks("leaves_blocks_default.cfg")
val leafModels = models("leaves_models_default.cfg")
val grassBlocks = blocks("grass_blocks_default.cfg")
val grassModels = models("grass_models_default.cfg")
val mycelium = blocks("mycelium_blocks_default.cfg")
// val dirt = blocks("dirt_default.cfg")
val crops = blocks("crop_default.cfg")
val logBlocks = blocks("log_blocks_default.cfg")
val logModels = models("log_models_default.cfg")
val lilypad = blocks("lilypad_default.cfg")
init { BetterFoliageMod.bus.register(this) }
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) }
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) }
@SubscribeEvent
fun onConfig(event: ModConfig.ModConfigEvent) {
list.forEach { when(it) {
is ConfigurableBlockMatcher -> it.readDefaults()
is ModelTextureListConfiguration -> it.readDefaults()
} }
}
}

View File

@@ -1,135 +0,0 @@
package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.render.AsyncLogDiscovery
import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.betterfoliage.client.texture.LeafInfo
import mods.betterfoliage.client.texture.defaultRegisterLeaf
import mods.octarinecore.HasLogger
import mods.octarinecore.Map
import mods.octarinecore.ResourceLocation
import mods.octarinecore.String
import mods.octarinecore.client.resource.*
import mods.octarinecore.metaprog.*
import mods.octarinecore.metaprog.ClassRef.Companion.boolean
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.resources.IResourceManager
import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader
import net.minecraftforge.fml.ModList
import org.apache.logging.log4j.Level
import java.util.concurrent.CompletableFuture
import kotlin.collections.component1
import kotlin.collections.component2
val TextureLeaves = ClassRef<Any>("forestry.arboriculture.models.TextureLeaves")
val TextureLeaves_leafTextures = FieldRef(TextureLeaves, "leafTextures", Map)
val TextureLeaves_plain = FieldRef(TextureLeaves, "plain", ResourceLocation)
val TextureLeaves_fancy = FieldRef(TextureLeaves, "fancy", ResourceLocation)
val TextureLeaves_pollinatedPlain = FieldRef(TextureLeaves, "pollinatedPlain", ResourceLocation)
val TextureLeaves_pollinatedFancy = FieldRef(TextureLeaves, "pollinatedFancy", ResourceLocation)
val TileLeaves = ClassRef<Any>("forestry.arboriculture.tiles.TileLeaves")
val TileLeaves_getLeaveSprite = MethodRef(TileLeaves, "getLeaveSprite", ResourceLocation, boolean)
val PropertyWoodType = ClassRef<Any>("forestry.arboriculture.blocks.PropertyWoodType")
val IWoodType = ClassRef<Any>("forestry.api.arboriculture.IWoodType")
val IWoodType_barkTex = MethodRef(IWoodType, "getBarkTexture", String)
val IWoodType_heartTex = MethodRef(IWoodType, "getHeartTexture", String)
val PropertyTreeType = ClassRef<Any>("forestry.arboriculture.blocks.PropertyTreeType")
val IAlleleTreeSpecies = ClassRef<Any>("forestry.api.arboriculture.IAlleleTreeSpecies")
val ILeafSpriteProvider = ClassRef<Any>("forestry.api.arboriculture.ILeafSpriteProvider")
val TreeDefinition = ClassRef<Any>("forestry.arboriculture.genetics.TreeDefinition")
val IAlleleTreeSpecies_getLeafSpriteProvider = MethodRef(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider)
val TreeDefinition_species = FieldRef(TreeDefinition, "species", IAlleleTreeSpecies)
val ILeafSpriteProvider_getSprite = MethodRef(ILeafSpriteProvider, "getSprite", ResourceLocation, boolean, boolean)
object ForestryIntegration {
init {
if (ModList.get().isLoaded("forestry") && allAvailable(TileLeaves_getLeaveSprite, IAlleleTreeSpecies_getLeafSpriteProvider, ILeafSpriteProvider_getSprite)) {
// Just keep it inactive for now until Forestry updates
}
}
}
object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider<ModelBakery>, ModelRenderRegistry<LeafInfo> {
override val logger = BetterFoliage.logDetail
var idToValue = emptyMap<ResourceLocation, LeafInfo>()
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? {
// check variant property (used in decorative leaves)
state.values.entries.find {
PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value)
} ?.let {
val species = it.value[TreeDefinition_species]
val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]()
val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, Minecraft.isFancyGraphicsEnabled())
return idToValue[textureLoc]
}
// extract leaf texture information from TileEntity
val tile = world.getTileEntity(pos) ?: return null
if (!TileLeaves.isInstance(tile)) return null
val textureLoc = tile[TileLeaves_getLeaveSprite](Minecraft.isFancyGraphicsEnabled())
return idToValue[textureLoc]
}
override fun setup(manager: IResourceManager, bakeryF: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
val futures = mutableMapOf<ResourceLocation, CompletableFuture<LeafInfo>>()
return StitchPhases(
discovery = bakeryF.thenRunAsync {
val allLeaves = TextureLeaves_leafTextures.getStatic()
allLeaves.entries.forEach { (type, leaves) ->
log("base leaf type $type")
leaves!!
listOf(
leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain],
leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy]
).forEach { textureLocation ->
futures[textureLocation] = defaultRegisterLeaf(textureLocation, atlasFuture)
}
}
},
cleanup = atlasFuture.runAfter {
idToValue = futures.mapValues { it.value.get() }
}
)
}
}
object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// respect class list to avoid triggering on fences, stairs, etc.
if (!BlockConfig.logBlocks.matchesClass(ctx.state.block)) return null
// find wood type property
val woodType = ctx.state.values.entries.find {
PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value)
}
if (woodType != null) {
logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}")
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
// get texture names for wood type
val bark = woodType.value[IWoodType_barkTex]()
val heart = woodType.value[IWoodType_heartTex]()
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
val heartSprite = atlas.sprite(heart)
val barkSprite = atlas.sprite(bark)
return atlas.mapAfter {
SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get()))
}
}
return null
}
}

View File

@@ -1,49 +0,0 @@
package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.octarinecore.*
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.metaprog.allAvailable
import mods.octarinecore.metaprog.reflectField
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.model.BakedQuad
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
import net.minecraft.util.Direction.UP
import net.minecraft.util.math.BlockPos
import org.apache.logging.log4j.Level
/**
* Integration for OptiFine custom block colors.
*/
@Suppress("UNCHECKED_CAST")
object OptifineCustomColors {
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
init {
BetterFoliage.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }")
}
val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }
val fakeQuad = BakedQuad(IntArray(0), 1, UP, null, true, DefaultVertexFormats.BLOCK)
fun getBlockColor(ctx: CombinedContext): Int {
val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
renderEnv.reset(ctx.state, ctx.pos)
CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
} else null
return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor
}
}
class OptifineRenderEnv {
val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let {
it.isAccessible = true
it.newInstance(null, null)
}
fun reset(state: BlockState, pos: BlockPos) {
RenderEnv.reset.invoke(wrapped, state, pos)
}
}

View File

@@ -1,160 +0,0 @@
package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.octarinecore.Sprite
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Quad
import mods.octarinecore.client.render.lighting.QuadIconResolver
import mods.octarinecore.client.resource.*
import mods.octarinecore.common.rotate
import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.allAvailable
import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.util.Direction
import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation
import net.minecraftforge.fml.ModList
import java.util.concurrent.CompletableFuture
object IC2RubberIntegration {
val BlockRubWood = ClassRef<Any>("ic2.core.block.BlockRubWood")
init {
if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
// keep it inactive for now until IC2 updates
// BetterFoliage.log(Level.INFO, "IC2 rubber support initialized")
// LogRegistry.registries.add(IC2LogDiscovery)
// BetterFoliage.blockSprites.providers.add(IC2LogDiscovery)
}
}
}
// Probably unneeded, as TechReborn went Fabric-only
/*
object TechRebornRubberIntegration {
val BlockRubberLog = ClassRef<Any>("techreborn.blocks.BlockRubberLog")
init {
if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
BetterFoliage.log(Level.INFO, "TechReborn rubber support initialized")
LogRegistry.registries.add(TechRebornLogDiscovery)
BetterFoliage.blockSprites.providers.add(TechRebornLogDiscovery)
}
}
}
*/
class RubberLogInfo(
axis: Axis?,
val spotDir: Direction,
topTexture: Sprite,
bottomTexture: Sprite,
val spotTexture: Sprite,
sideTextures: List<Sprite>
) : SimpleColumnInfo(axis, topTexture, bottomTexture, sideTextures) {
override val side: QuadIconResolver = { ctx: CombinedContext, idx: Int, quad: Quad ->
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.modelRotation)
if (worldFace == spotDir) spotTexture else {
val sideIdx = if (this.sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % this.sideTextures.size else 0
this.sideTextures[sideIdx]
}
}
}
object IC2LogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock, and "state" blockstate property
if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null
val blockLoc = ctx.models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
val type = ctx.state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
// logs with no rubber spot
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
val axis = when(type) {
"plain_y" -> Axis.Y
"plain_x" -> Axis.X
"plain_z" -> Axis.Z
else -> null
}
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) }
if (textureNames.any { it == "missingno" }) return null
log("IC2LogSupport: block state ${ctx.state.toString()}")
log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
return atlas.mapAfter {
SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
}
}
// logs with rubber spot
val spotDir = when(type) {
"dry_north", "wet_north" -> NORTH
"dry_south", "wet_south" -> SOUTH
"dry_west", "wet_west" -> WEST
"dry_east", "wet_east" -> EAST
else -> null
}
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) }
if (textureNames.any { it == "missingno" }) return null
log("IC2LogSupport: block state ${ctx.state.toString()}")
log("IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
val upSprite = atlas.sprite(textureNames[0])
val downSprite = atlas.sprite(textureNames[1])
val sideSprite = atlas.sprite(textureNames[2])
val spotSprite = atlas.sprite(textureNames[3])
return if (spotDir != null) atlas.mapAfter {
RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get()))
} else atlas.mapAfter {
SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get()))
}
}
}
/*
object TechRebornLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) return null
val blockLoc = ctx.models.map { it as? Pair<BlockModel, ResourceLocation> }.firstOrNull() ?: return null
val hasSap = ctx.state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
val sapSide = ctx.state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
log("$logName: block state ${ctx.state}")
if (hasSap) {
val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTextureName(it) }
log("$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
if (textureNames.all { it != "missingno" }) {
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
val sapSprite = atlas.sprite(textureNames[2])
return atlas.mapAfter {
RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get()))
}
}
} else {
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) }
log("$logName: end=${textureNames[0]}, side=${textureNames[1]}")
if (textureNames.all { it != "missingno" }) {
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
return atlas.mapAfter {
SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
}
}
}
return null
}
}
*/

View File

@@ -1,66 +0,0 @@
package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.*
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.metaprog.allAvailable
import mods.octarinecore.metaprog.get
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockRenderType.MODEL
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.util.math.BlockPos
import net.minecraft.world.IEnviromentBlockReader
import org.apache.logging.log4j.Level.INFO
/**
* Integration for ShadersMod.
*/
object ShadersModIntegration {
@JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.popState, BlockAliases.getAliasBlockId)
val defaultLeaves = Blocks.OAK_LEAVES.defaultState
val defaultGrass = Blocks.GRASS.defaultState
/**
* Called from transformed ShadersMod code.
* @see mods.betterfoliage.loader.BetterFoliageTransformer
*/
@JvmStatic fun getBlockStateOverride(state: BlockState, world: IEnviromentBlockReader, pos: BlockPos): BlockState {
if (LeafRegistry[state, world, pos] != null) return defaultLeaves
if (BlockConfig.crops.matchesClass(state.block)) return defaultGrass
return state
}
init {
BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
}
inline fun renderAs(ctx: CombinedContext, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, ctx.state, renderType, enabled, func)
/** Quads rendered inside this block will use the given block entity data in shader programs. */
inline fun renderAs(ctx: CombinedContext, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) {
if (isAvailable && enabled) {
val buffer = ctx.renderCtx.renderBuffer
val aliasBlockId = BlockAliases.getAliasBlockId.invokeStatic(state)
val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder]
SVertexBuilder.pushState.invoke(sVertexBuilder, aliasBlockId)
func()
SVertexBuilder.popState.invoke(sVertexBuilder)
} else {
func()
}
}
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultGrass, MODEL, enabled, func)
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultLeaves, MODEL, enabled, func)
}

View File

@@ -1,141 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.texture.LeafParticleRegistry
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.PI2
import mods.octarinecore.client.render.AbstractEntityFX
import mods.octarinecore.client.render.lighting.HSB
import mods.octarinecore.common.Double3
import mods.octarinecore.minmax
import mods.octarinecore.random
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.BufferBuilder
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper
import net.minecraft.world.World
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.TickEvent
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import org.lwjgl.opengl.GL11
import java.util.*
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
const val rotationFactor = PI2.toFloat() / 64.0f
class EntityFallingLeavesFX(
world: World, pos: BlockPos
) : AbstractEntityFX(
world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5
) {
companion object {
@JvmStatic val biomeBrightnessMultiplier = 0.5f
}
var particleRot = rand.nextInt(64)
var rotPositive = true
val isMirrored = (rand.nextInt() and 1) == 1
var wasCollided = false
init {
maxAge = MathHelper.floor(random(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
motionY = -Config.fallingLeaves.speed
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
val state = world.getBlockState(pos)
val leafInfo = LeafRegistry[state, world, pos]
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
if (leafInfo != null) {
sprite = leafInfo.particleTextures[rand.nextInt(1024)]
calculateParticleColor(leafInfo.averageColor, blockColor)
} else {
sprite = LeafParticleRegistry["default"][rand.nextInt(1024)]
setColor(blockColor)
}
}
override val isValid: Boolean get() = (sprite != null)
override fun update() {
if (rand.nextFloat() > 0.95f) rotPositive = !rotPositive
if (age > maxAge - 20) particleAlpha = 0.05f * (maxAge - age)
if (onGround || wasCollided) {
velocity.setTo(0.0, 0.0, 0.0)
if (!wasCollided) {
age = Math.max(age, maxAge - 20)
wasCollided = true
}
} else {
velocity.setTo(cos[particleRot], 0.0, sin[particleRot]).mul(Config.fallingLeaves.perturb)
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
particleRot = (particleRot + (if (rotPositive) 1 else -1)) and 63
particleAngle = rotationFactor * particleRot.toFloat()
}
}
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
if (Config.fallingLeaves.opacityHack) GL11.glDepthMask(true)
renderParticleQuad(worldRenderer, partialTickTime, rotation = particleRot, isMirrored = isMirrored)
}
fun calculateParticleColor(textureAvgColor: Int, blockColor: Int) {
val texture = HSB.fromColor(textureAvgColor)
val block = HSB.fromColor(blockColor)
val weightTex = texture.saturation / (texture.saturation + block.saturation)
val weightBlock = 1.0f - weightTex
// avoid circular average for hue for performance reasons
// one of the color components should dominate anyway
val particle = HSB(
weightTex * texture.hue + weightBlock * block.hue,
weightTex * texture.saturation + weightBlock * block.saturation,
weightTex * texture.brightness + weightBlock * block.brightness * biomeBrightnessMultiplier
)
setColor(particle.asColor)
}
}
object LeafWindTracker {
var random = Random()
val target = Double3.zero
val current = Double3.zero
var nextChange: Long = 0
init {
MinecraftForge.EVENT_BUS.register(this)
}
fun changeWind(world: World) {
nextChange = world.worldInfo.gameTime + 120 + random.nextInt(80)
val direction = PI2 * random.nextDouble()
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
target.setTo(cos(direction) * speed, 0.0, sin(direction) * speed)
}
@SubscribeEvent
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
if (event.phase == TickEvent.Phase.START) Minecraft.getInstance().world?.let { world ->
// change target wind speed
if (world.worldInfo.dayTime >= nextChange) changeWind(world)
// change current wind speed
val changeRate = if (world.isRaining) 0.015 else 0.005
current.add(
(target.x - current.x).minmax(-changeRate, changeRate),
0.0,
(target.z - current.z).minmax(-changeRate, changeRate)
)
}
}
@SubscribeEvent
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world.world) }
}

View File

@@ -1,69 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.AbstractEntityFX
import mods.octarinecore.client.resource.Atlas
import mods.octarinecore.client.resource.ResourceHandler
import mods.octarinecore.common.Double3
import mods.octarinecore.forEachPairIndexed
import net.minecraft.client.renderer.BufferBuilder
import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper
import net.minecraft.world.World
import java.util.*
class EntityRisingSoulFX(world: World, pos: BlockPos) :
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5) {
val particleTrail: Deque<Double3> = LinkedList<Double3>()
val initialPhase = rand.nextInt(64)
init {
motionY = 0.1
particleGravity = 0.0f
sprite = RisingSoulTextures.headIcons[rand.nextInt(256)]
maxAge = MathHelper.floor((0.6 + 0.4 * rand.nextDouble()) * Config.risingSoul.lifetime * 20.0)
}
override val isValid: Boolean get() = true
override fun update() {
val phase = (initialPhase + age) % 64
velocity.setTo(cos[phase] * Config.risingSoul.perturb, 0.1, sin[phase] * Config.risingSoul.perturb)
particleTrail.addFirst(currentPos.copy())
while (particleTrail.size > Config.risingSoul.trailLength) particleTrail.removeLast()
if (!Config.enabled) setExpired()
}
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
var alpha = Config.risingSoul.opacity.toFloat()
if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
renderParticleQuad(worldRenderer, partialTickTime,
size = Config.risingSoul.headSize * 0.25,
alpha = alpha
)
var scale = Config.risingSoul.trailSize * 0.25
particleTrail.forEachPairIndexed { idx, current, previous ->
scale *= Config.risingSoul.sizeDecay
alpha *= Config.risingSoul.opacityDecay.toFloat()
if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
currentPos = current,
prevPos = previous,
size = scale,
alpha = alpha,
icon = RisingSoulTextures.trackIcon
)
}
}
}
object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus, targetAtlas = Atlas.PARTICLES) {
val headIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "rising_soul_$idx") }
val trackIcon by sprite(ResourceLocation(BetterFoliageMod.MOD_ID, "soul_track"))
}

View File

@@ -1,146 +0,0 @@
@file:JvmName("ModelColumn")
package mods.betterfoliage.client.render
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.common.Double3
import mods.octarinecore.exchange
import net.minecraft.util.Direction.*
/** Weight of the same-side AO values on the outer edges of the 45deg chamfered column faces. */
const val chamferAffinity = 0.9f
/** Amount to shrink column extension bits to stop Z-fighting. */
val zProtectionScale: Double3 get() = Double3(Config.roundLogs.zProtection, 1.0, Config.roundLogs.zProtection)
fun Model.columnSide(radius: Double, yBottom: Double, yTop: Double, transform: (Quad) -> Quad = { it }) {
val halfRadius = radius * 0.5
listOf(
verticalRectangle(x1 = 0.0, z1 = 0.5, x2 = 0.5 - radius, z2 = 0.5, yBottom = yBottom, yTop = yTop)
.clampUV(minU = 0.0, maxU = 0.5 - radius)
.setAoShader(faceOrientedInterpolate(overrideFace = SOUTH))
.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y)), predicate = { v, vi -> vi == 1 || vi == 2}),
verticalRectangle(x1 = 0.5 - radius, z1 = 0.5, x2 = 0.5 - halfRadius, z2 = 0.5 - halfRadius, yBottom = yBottom, yTop = yTop)
.clampUV(minU = 0.5 - radius)
.setAoShader(
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming.toFloat()))
)
.setAoShader(
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming.toFloat())),
predicate = { v, vi -> vi == 1 || vi == 2}
)
).forEach { transform(it.setFlatShader(FaceFlat(SOUTH))).add() }
listOf(
verticalRectangle(x1 = 0.5 - halfRadius, z1 = 0.5 - halfRadius, x2 = 0.5, z2 = 0.5 - radius, yBottom = yBottom, yTop = yTop)
.clampUV(maxU = radius - 0.5)
.setAoShader(
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming.toFloat())))
.setAoShader(
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming.toFloat())),
predicate = { v, vi -> vi == 0 || vi == 3}
),
verticalRectangle(x1 = 0.5, z1 = 0.5 - radius, x2 = 0.5, z2 = 0.0, yBottom = yBottom, yTop = yTop)
.clampUV(minU = radius - 0.5, maxU = 0.0)
.setAoShader(faceOrientedInterpolate(overrideFace = EAST))
.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y)), predicate = { v, vi -> vi == 0 || vi == 3})
).forEach { transform(it.setFlatShader(FaceFlat(EAST))).add() }
quads.exchange(1, 2)
}
/**
* Create a model of the side of a square column quadrant.
*
* @param[transform] transformation to apply to the model
*/
fun Model.columnSideSquare(yBottom: Double, yTop: Double, transform: (Quad) -> Quad = { it }) {
listOf(
verticalRectangle(x1 = 0.0, z1 = 0.5, x2 = 0.5, z2 = 0.5, yBottom = yBottom, yTop = yTop)
.clampUV(minU = 0.0)
.setAoShader(faceOrientedInterpolate(overrideFace = SOUTH))
.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y)), predicate = { v, vi -> vi == 1 || vi == 2}),
verticalRectangle(x1 = 0.5, z1 = 0.5, x2 = 0.5, z2 = 0.0, yBottom = yBottom, yTop = yTop)
.clampUV(maxU = 0.0)
.setAoShader(faceOrientedInterpolate(overrideFace = EAST))
.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y)), predicate = { v, vi -> vi == 0 || vi == 3})
).forEach {
transform(it.setFlatShader(faceOrientedAuto(corner = cornerFlat))).add()
}
}
/**
* Create a model of the top lid of a chamfered column quadrant.
*
* @param[radius] the chamfer radius
* @param[transform] transformation to apply to the model
*/
fun Model.columnLid(radius: Double, transform: (Quad)->Quad = { it }) {
val v1 = Vertex(Double3(0.0, 0.5, 0.0), UV(0.0, 0.0))
val v2 = Vertex(Double3(0.0, 0.5, 0.5), UV(0.0, 0.5))
val v3 = Vertex(Double3(0.5 - radius, 0.5, 0.5), UV(0.5 - radius, 0.5))
val v4 = Vertex(Double3(0.5 - radius * 0.5, 0.5, 0.5 - radius * 0.5), UV(0.5, 0.5))
val v5 = Vertex(Double3(0.5, 0.5, 0.5 - radius), UV(0.5, 0.5 - radius))
val v6 = Vertex(Double3(0.5, 0.5, 0.0), UV(0.5, 0.0))
val q1 = Quad(v1, v2, v3, v4).setAoShader(faceOrientedAuto(overrideFace = UP, corner = cornerAo(Axis.Y)))
.transformVI { vertex, idx -> vertex.copy(aoShader = when(idx) {
0 -> FaceCenter(UP)
1 -> EdgeInterpolateFallback(UP, SOUTH, 0.0)
else -> vertex.aoShader
})}
.cycleVertices(if (Config.nVidia) 0 else 1)
val q2 = Quad(v1, v4, v5, v6).setAoShader(faceOrientedAuto(overrideFace = UP, corner = cornerAo(Axis.Y)))
.transformVI { vertex, idx -> vertex.copy(aoShader = when(idx) {
0 -> FaceCenter(UP)
3 -> EdgeInterpolateFallback(UP, EAST, 0.0)
else -> vertex.aoShader
})}
.cycleVertices(if (Config.nVidia) 0 else 1)
listOf(q1, q2).forEach { transform(it.setFlatShader(FaceFlat(UP))).add() }
}
/**
* Create a model of the top lid of a square column quadrant.
*
* @param[transform] transformation to apply to the model
*/
fun Model.columnLidSquare(transform: (Quad)-> Quad = { it }) {
transform(
horizontalRectangle(x1 = 0.0, x2 = 0.5, z1 = 0.0, z2 = 0.5, y = 0.5)
.transformVI { vertex, idx -> vertex.copy(uv = UV(vertex.xyz.x, vertex.xyz.z), aoShader = when(idx) {
0 -> FaceCenter(UP)
1 -> EdgeInterpolateFallback(UP, SOUTH, 0.0)
2 -> CornerSingleFallback(UP, SOUTH, EAST, UP)
else -> EdgeInterpolateFallback(UP, EAST, 0.0)
}) }
.setFlatShader(FaceFlat(UP))
).add()
}
/**
* Transform a chamfered side quadrant model of a column that extends from the top of the block.
* (clamp UV coordinates, apply some scaling to avoid Z-fighting).
*
* @param[size] amount that the model extends from the top
*/
fun topExtension(size: Double) = { q: Quad ->
q.clampUV(minV = 0.5 - size).transformVI { vertex, idx ->
if (idx < 2) vertex else vertex.copy(xyz = vertex.xyz * zProtectionScale)
}
}
/**
* Transform a chamfered side quadrant model of a column that extends from the bottom of the block.
* (clamp UV coordinates, apply some scaling to avoid Z-fighting).
*
* @param[size] amount that the model extends from the bottom
*/
fun bottomExtension(size: Double) = { q: Quad ->
q.clampUV(maxV = -0.5 + size).transformVI { vertex, idx ->
if (idx > 1) vertex else vertex.copy(xyz = vertex.xyz * zProtectionScale)
}
}

View File

@@ -1,42 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import net.minecraft.block.material.Material
import net.minecraft.tags.BlockTags
import net.minecraft.util.ResourceLocation
import net.minecraft.world.biome.Biome
import org.apache.logging.log4j.Level.DEBUG
class RenderAlgae : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val noise = simplexNoise()
val algaeIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_algae_$idx") }
val algaeModels = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.algae.heightMin, Config.algae.heightMax)(idx) }
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.algae.enabled &&
ctx.state(up2).material == Material.WATER &&
ctx.state(up1).material == Material.WATER &&
BlockTags.DIRT_LIKE.contains(ctx.state.block) &&
ctx.biome.category.let { it == Biome.Category.OCEAN || it == Biome.Category.BEACH || it == Biome.Category.RIVER } &&
noise[ctx.pos] < Config.algae.population
override fun render(ctx: CombinedContext) {
ctx.render()
if (!ctx.isCutout) return
val rand = ctx.semiRandomArray(3)
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
ctx.render(
algaeModels[rand[2]],
icon = { _, qi, _ -> algaeIcons[rand[qi and 1]] }
)
}
}
}

View File

@@ -1,103 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.client.resource.*
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.common.config.SimpleBlockMatcher
import net.minecraft.block.BlockState
import net.minecraft.block.CactusBlock
import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation
import java.util.concurrent.CompletableFuture
object AsyncCactusDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
val sprites = textures.map { atlas.sprite(ResourceLocation(it)) }
return atlas.mapAfter {
SimpleColumnInfo(
Axis.Y,
sprites[0].get(),
sprites[1].get(),
sprites.drop(2).map { it.get() }
)
}
}
fun init() {
BetterFoliage.blockSprites.providers.add(this)
}
}
class RenderCactus : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val cactusStemRadius = 0.4375
val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] }
val iconCross by sprite(ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus"))
val iconArm = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx") }
val modelStem = model {
horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5)
.scaleUV(cactusStemRadius * 2.0)
.let { listOf(it.flipped.move(1.0 to DOWN), it) }
.forEach { it.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y), edge = null)).add() }
verticalRectangle(x1 = -0.5, z1 = cactusStemRadius, x2 = 0.5, z2 = cactusStemRadius, yBottom = -0.5, yTop = 0.5)
.setAoShader(faceOrientedAuto(corner = cornerAo(Axis.Y), edge = null))
.toCross(UP).addAll()
}
val modelCross = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
.setAoShader(edgeOrientedAuto(corner = cornerAoMaxGreen))
.scale(1.4)
.transformV { v ->
val perturb = xzDisk(modelIdx) * Config.cactus.sizeVariation
Vertex(v.xyz + (if (v.uv.u < 0.0) perturb else -perturb), v.uv, v.aoShader)
}
.toCross(UP).addAll()
}
val modelArm = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0)
.scale(Config.cactus.size).move(0.5 to UP)
.setAoShader(faceOrientedAuto(overrideFace = UP, corner = cornerAo(Axis.Y), edge = null))
.toCross(UP) { it.move(xzDisk(modelIdx) * Config.cactus.hOffset) }.addAll()
}
override fun isEligible(ctx: CombinedContext): Boolean =
Config.enabled && Config.cactus.enabled &&
AsyncCactusDiscovery[ctx] != null
override val onlyOnCutout get() = true
override fun render(ctx: CombinedContext) {
val icons = AsyncCactusDiscovery[ctx]!!
ctx.render(
modelStem.model,
icon = { ctx, qi, q -> when(qi) {
0 -> icons.bottom(ctx, qi, q); 1 -> icons.top(ctx, qi, q); else -> icons.side(ctx, qi, q)
} }
)
ctx.render(
modelCross[ctx.semiRandom(0)],
icon = { _, _, _ -> iconCross }
)
ctx.render(
modelArm[ctx.semiRandom(1)],
cactusArmRotation[ctx.semiRandom(2) % 4],
icon = { _, _, _ -> iconArm[ctx.semiRandom(3)] }
)
}
}

View File

@@ -1,45 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.texture.GrassRegistry
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.common.Int3
import mods.octarinecore.common.horizontalDirections
import mods.octarinecore.common.offset
import net.minecraft.tags.BlockTags
class RenderConnectedGrass : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.connectedGrass.enabled &&
BlockTags.DIRT_LIKE.contains(ctx.state.block) &&
GrassRegistry[ctx, up1] != null &&
(Config.connectedGrass.snowEnabled || !ctx.state(up2).isSnow)
override fun render(ctx: CombinedContext) {
// if the block sides are not visible anyway, render normally
if (horizontalDirections.none { ctx.shouldSideBeRendered(it) }) {
ctx.render()
} else {
ctx.exchange(Int3.zero, up1).exchange(up1, up2).render()
}
}
}
class RenderConnectedGrassLog : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&
BlockTags.DIRT_LIKE.contains(ctx.state.block) &&
LogRegistry[ctx, up1] != null
override fun render(ctx: CombinedContext) {
val grassDir = horizontalDirections.find { GrassRegistry[ctx, it.offset] != null }
if (grassDir == null) {
ctx.render()
} else {
ctx.exchange(Int3.zero, grassDir.offset).render()
}
}
}

View File

@@ -1,64 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.common.allDirections
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.tags.BlockTags
import net.minecraft.util.Direction.Axis
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import net.minecraft.world.biome.Biome
import org.apache.logging.log4j.Level.DEBUG
class RenderCoral : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val noise = simplexNoise()
val coralIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_coral_$idx") }
val crustIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_crust_$idx") }
val coralModels = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0)
.scale(Config.coral.size).move(0.5 to UP)
.toCross(UP) { it.move(xzDisk(modelIdx) * Config.coral.hOffset) }.addAll()
val separation = random(0.01, Config.coral.vOffset)
horizontalRectangle(x1 = -0.5, x2 = 0.5, z1 = -0.5, z2 = 0.5, y = 0.0)
.scale(Config.coral.crustSize).move(0.5 + separation to UP).add()
transformQ {
it.setAoShader(faceOrientedAuto(overrideFace = UP, corner = cornerAo(Axis.Y)))
.setFlatShader(faceOrientedAuto(overrideFace = UP, corner = cornerFlat))
}
}
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.coral.enabled &&
(ctx.state(up2).material == Material.WATER || Config.coral.shallowWater) &&
ctx.state(up1).material == Material.WATER &&
BlockTags.SAND.contains(ctx.state.block) &&
ctx.biome.category.let { it == Biome.Category.OCEAN || it == Biome.Category.BEACH } &&
noise[ctx.pos] < Config.coral.population
override fun render(ctx: CombinedContext) {
val baseRender = ctx.render()
if (!ctx.isCutout) return
allDirections.forEachIndexed { idx, face ->
if (ctx.state(face).material == Material.WATER && ctx.semiRandom(idx) < Config.coral.chance) {
var variation = ctx.semiRandom(6)
ctx.render(
coralModels[variation++],
rotationFromUp[idx],
icon = { _, qi, _ -> if (qi == 4) crustIcons[variation] else coralIcons[variation + (qi and 1)] }
)
}
}
}
}

View File

@@ -1,102 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.OptifineCustomColors
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.texture.GeneratedGrass
import mods.betterfoliage.client.texture.GrassRegistry
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Model
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.fullCube
import mods.octarinecore.client.render.lighting.cornerAo
import mods.octarinecore.client.render.lighting.cornerFlat
import mods.octarinecore.client.render.lighting.faceOrientedAuto
import mods.octarinecore.common.Double3
import mods.octarinecore.common.allDirections
import mods.octarinecore.random
import net.minecraft.tags.BlockTags
import net.minecraft.util.Direction.Axis
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
class RenderGrass : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
companion object {
@JvmStatic fun grassTopQuads(heightMin: Double, heightMax: Double): Model.(Int)->Unit = { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.5,
yTop = 0.5 + random(heightMin, heightMax)
)
.setAoShader(faceOrientedAuto(overrideFace = UP, corner = cornerAo(Axis.Y)))
.setFlatShader(faceOrientedAuto(overrideFace = UP, corner = cornerFlat))
.toCross(UP) { it.move(xzDisk(modelIdx) * Config.shortGrass.hOffset) }.addAll()
}
}
val noise = simplexNoise()
val normalIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx") }
val snowedIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_grass_snowed_$idx") }
val normalGenIcon by sprite { GeneratedGrass(sprite = "minecraft:blocks/tall_grass_top", isSnowed = false).register(BetterFoliage.asyncPack) }
val snowedGenIcon by sprite { GeneratedGrass(sprite = "minecraft:blocks/tall_grass_top", isSnowed = true).register(BetterFoliage.asyncPack) }
val grassModels = modelSet(64) { idx -> grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) }
override fun isEligible(ctx: CombinedContext) =
Config.enabled &&
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
GrassRegistry[ctx] != null
override val onlyOnCutout get() = true
override fun render(ctx: CombinedContext) {
val isConnected = BlockTags.DIRT_LIKE.contains(ctx.state(DOWN).block) || GrassRegistry[ctx, down1] != null
val isSnowed = ctx.state(UP).isSnow
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
val grass = GrassRegistry[ctx]!!
val blockColor = OptifineCustomColors.getBlockColor(ctx)
if (connectedGrass) {
// check occlusion
val isVisible = allDirections.map { ctx.shouldSideBeRendered(it) }
// render full grass block
ctx.render(
fullCube,
quadFilter = { qi, _ -> isVisible[qi] },
icon = { _, _, _ -> grass.grassTopTexture },
postProcess = { ctx, _, _, _, _ ->
rotateUV(2)
if (isSnowed) {
if (!ctx.aoEnabled) setGrey(1.4f)
} else if (ctx.aoEnabled && grass.overrideColor == null) multiplyColor(blockColor)
}
)
} else {
ctx.render()
}
if (!Config.shortGrass.grassEnabled) return
if (isSnowed && !Config.shortGrass.snowEnabled) return
if (ctx.offset(UP).isNormalCube) return
if (Config.shortGrass.population < 64 && noise[ctx.pos] >= Config.shortGrass.population) return
// render grass quads
val iconset = if (isSnowed) snowedIcons else normalIcons
val iconGen = if (isSnowed) snowedGenIcon else normalGenIcon
val rand = ctx.semiRandomArray(2)
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
ctx.render(
grassModels[rand[0]],
translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero),
icon = { _, qi, _ -> if (Config.shortGrass.useGenerated) iconGen else iconset[rand[qi and 1]] },
postProcess = { _, _, _, _, _ -> if (isSnowed) setGrey(1.0f) else multiplyColor(grass.overrideColor ?: blockColor) }
)
}
}
}

View File

@@ -1,78 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.OptifineCustomColors
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.PI2
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.FlatOffset
import mods.octarinecore.client.render.lighting.cornerAoMaxGreen
import mods.octarinecore.client.render.lighting.edgeOrientedAuto
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.allDirections
import mods.octarinecore.common.vec
import mods.octarinecore.random
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import java.lang.Math.cos
import java.lang.Math.sin
class RenderLeaves : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val leavesModel = model {
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
.setAoShader(edgeOrientedAuto(corner = cornerAoMaxGreen))
.setFlatShader(FlatOffset(Int3.zero))
.scale(Config.leaves.size)
.toCross(UP).addAll()
}
val snowedIcon = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_leaves_snowed_$idx") }
val perturbs = vectorSet(64) { idx ->
val angle = PI2 * idx / 64.0
Double3(cos(angle), 0.0, sin(angle)) * Config.leaves.hOffset +
UP.vec * random(-1.0, 1.0) * Config.leaves.vOffset
}
override fun isEligible(ctx: CombinedContext) =
Config.enabled &&
Config.leaves.enabled &&
LeafRegistry[ctx] != null &&
!(Config.leaves.hideInternal && allDirections.all { ctx.offset(it).isNormalCube } )
override val onlyOnCutout get() = true
override fun render(ctx: CombinedContext) {
val isSnowed = ctx.state(UP).isSnow
val leafInfo = LeafRegistry[ctx]!!
val blockColor = OptifineCustomColors.getBlockColor(ctx)
ctx.render(force = true)
ShadersModIntegration.leaves(ctx) {
val rand = ctx.semiRandomArray(2)
(if (Config.leaves.dense) denseLeavesRot else normalLeavesRot).forEach { rotation ->
ctx.render(
leavesModel.model,
rotation,
translation = ctx.blockCenter + perturbs[rand[0]],
icon = { _, _, _ -> leafInfo.roundLeafTexture },
postProcess = { _, _, _, _, _ ->
rotateUV(rand[1])
multiplyColor(blockColor)
}
)
}
if (isSnowed && Config.leaves.snowEnabled) ctx.render(
leavesModel.model,
translation = ctx.blockCenter + perturbs[rand[0]],
icon = { _, _, _ -> snowedIcon[rand[1]] },
postProcess = whitewash
)
}
}
}

View File

@@ -1,56 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.FlatOffsetNoColor
import mods.octarinecore.common.Int3
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
class RenderLilypad : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val rootModel = model {
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -1.5, yTop = -0.5)
.setFlatShader(FlatOffsetNoColor(Int3.zero))
.toCross(UP).addAll()
}
val flowerModel = model {
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0)
.scale(0.5).move(0.5 to DOWN)
.setFlatShader(FlatOffsetNoColor(Int3.zero))
.toCross(UP).addAll()
}
val rootIcon = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_roots_$idx") }
val flowerIcon = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_flower_$idx") }
val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset }
override fun isEligible(ctx: CombinedContext): Boolean =
Config.enabled && Config.lilypad.enabled &&
BlockConfig.lilypad.matchesClass(ctx.state.block)
override fun render(ctx: CombinedContext) {
ctx.render()
val rand = ctx.semiRandomArray(5)
ShadersModIntegration.grass(ctx) {
ctx.render(
rootModel.model,
translation = ctx.blockCenter.add(perturbs[rand[2]]),
forceFlat = true,
icon = { ctx, qi, q -> rootIcon[rand[qi and 1]] }
)
}
if (rand[3] < Config.lilypad.flowerChance) ctx.render(
flowerModel.model,
translation = ctx.blockCenter.add(perturbs[rand[4]]),
forceFlat = true,
icon = { _, _, _ -> flowerIcon[rand[0]] }
)
}
}

View File

@@ -1,86 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.column.AbstractRenderColumn
import mods.betterfoliage.client.render.column.ColumnRenderLayer
import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.resource.*
import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.tryDefault
import net.minecraft.block.BlockState
import net.minecraft.block.LogBlock
import net.minecraft.util.Direction.Axis
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level
import java.util.concurrent.CompletableFuture
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
override val renderOnCutout: Boolean get() = false
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.roundLogs.enabled &&
LogRegistry[ctx] != null
override val overlayLayer = RoundLogOverlayLayer()
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
override val radiusSmall: Double get() = Config.roundLogs.radiusSmall
override val radiusLarge: Double get() = Config.roundLogs.radiusLarge
init {
ChunkOverlayManager.layers.add(overlayLayer)
}
}
class RoundLogOverlayLayer : ColumnRenderLayer() {
override val registry: ModelRenderRegistry<ColumnTextureInfo> get() = LogRegistry
override val blockPredicate = { state: BlockState -> BlockConfig.logBlocks.matchesClass(state.block) }
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
override val lenientConnect: Boolean get() = Config.roundLogs.lenientConnect
override val defaultToY: Boolean get() = Config.roundLogs.defaultY
}
object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>()
object AsyncLogDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo> {
val axis = getAxis(state)
logger.log(Level.DEBUG, "$logName: axis $axis")
val spriteList = textures.map { atlas.sprite(ResourceLocation(it)) }
return atlas.mapAfter {
SimpleColumnInfo(
axis,
spriteList[0].get(),
spriteList[1].get(),
spriteList.drop(2).map { it.get() }
)
}
}
fun getAxis(state: BlockState): Axis? {
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
state.values.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
return when (axis) {
"x" -> Axis.X
"y" -> Axis.Y
"z" -> Axis.Z
else -> null
}
}
fun init() {
LogRegistry.registries.add(this)
BetterFoliage.blockSprites.providers.add(this)
}
}

View File

@@ -1,39 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.noPost
import mods.octarinecore.common.Double3
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
class RenderMycelium : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val myceliumIcon = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_mycel_$idx") }
val myceliumModel = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) }
override fun isEligible(ctx: CombinedContext): Boolean {
if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false
return BlockConfig.mycelium.matchesClass(ctx.state.block)
}
override fun render(ctx: CombinedContext) {
ctx.render()
if (!ctx.isCutout) return
val isSnowed = ctx.state(UP).isSnow
if (isSnowed && !Config.shortGrass.snowEnabled) return
if (ctx.offset(UP).isNormalCube) return
val rand = ctx.semiRandomArray(2)
ctx.render(
myceliumModel[rand[0]],
translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero),
icon = { _, qi, _ -> myceliumIcon[rand[qi and 1]] },
postProcess = if (isSnowed) whitewash else noPost
)
}
}

View File

@@ -1,43 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.cornerAo
import mods.octarinecore.client.render.lighting.cornerFlat
import mods.octarinecore.client.render.lighting.faceOrientedAuto
import mods.octarinecore.random
import net.minecraft.block.Blocks
import net.minecraft.util.Direction.Axis
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
class RenderNetherrack : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val netherrackIcon = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_netherrack_$idx") }
val netherrackModel = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yTop = -0.5,
yBottom = -0.5 - random(Config.netherrack.heightMin, Config.netherrack.heightMax))
.setAoShader(faceOrientedAuto(overrideFace = DOWN, corner = cornerAo(Axis.Y)))
.setFlatShader(faceOrientedAuto(overrideFace = DOWN, corner = cornerFlat))
.toCross(UP) { it.move(xzDisk(modelIdx) * Config.shortGrass.hOffset) }.addAll()
}
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.netherrack.enabled && ctx.state.block == Blocks.NETHERRACK
override fun render(ctx: CombinedContext) {
ctx.render()
if (!ctx.isCutout) return
if (ctx.offset(DOWN).isNormalCube) return
val rand = ctx.semiRandomArray(2)
ctx.render(
netherrackModel[rand[0]],
icon = { _, qi, _ -> netherrackIcon[rand[qi and 1]] }
)
}
}

View File

@@ -1,65 +0,0 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.FlatOffsetNoColor
import mods.octarinecore.client.resource.CenteredSprite
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.tags.BlockTags
import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
class RenderReeds : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val noise = simplexNoise()
val reedIcons = spriteSetTransformed(
check = { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_reed_$idx") },
register = { CenteredSprite(it).register(BetterFoliage.asyncPack) }
)
val reedModels = modelSet(64) { modelIdx ->
val height = random(Config.reed.heightMin, Config.reed.heightMax)
val waterline = 0.875f
val vCutLine = 0.5 - waterline / height
listOf(
// below waterline
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.5, yTop = 0.5 + waterline)
.setFlatShader(FlatOffsetNoColor(up1)).clampUV(minV = vCutLine),
// above waterline
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.5 + waterline, yTop = 0.5 + height)
.setFlatShader(FlatOffsetNoColor(up2)).clampUV(maxV = vCutLine)
).forEach {
it.clampUV(minU = -0.25, maxU = 0.25)
.toCross(UP) { it.move(xzDisk(modelIdx) * Config.reed.hOffset) }.addAll()
}
}
override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.reed.enabled &&
ctx.state(up2).material == Material.AIR &&
ctx.state(UP).material == Material.WATER &&
BlockTags.DIRT_LIKE.contains(ctx.state.block) &&
ctx.biome.let { it.downfall > Config.reed.minBiomeRainfall && it.defaultTemperature >= Config.reed.minBiomeTemp } &&
noise[ctx.pos] < Config.reed.population
override val onlyOnCutout get() = false
override fun render(ctx: CombinedContext) {
ctx.render()
if (!ctx.isCutout) return
val iconVar = ctx.semiRandom(1)
ShadersModIntegration.grass(ctx, Config.reed.shaderWind) {
ctx.render(
reedModels[ctx.semiRandom(0)],
forceFlat = true,
icon = { _, _, _ -> reedIcons[iconVar] }
)
}
}
}

View File

@@ -1,65 +0,0 @@
@file:JvmName("Utils")
package mods.betterfoliage.client.render
import mods.octarinecore.PI2
import mods.octarinecore.client.render.Model
import mods.octarinecore.client.render.lighting.PostProcessLambda
import mods.octarinecore.client.render.Quad
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.times
import net.minecraft.block.BlockState
import net.minecraft.block.material.Material
import net.minecraft.util.BlockRenderLayer
import net.minecraft.util.Direction
import net.minecraft.util.Direction.*
import kotlin.math.cos
import kotlin.math.sin
val up1 = Int3(1 to UP)
val up2 = Int3(2 to UP)
val down1 = Int3(1 to DOWN)
val snowOffset = UP * 0.0625
val normalLeavesRot = arrayOf(Rotation.identity)
val denseLeavesRot = arrayOf(Rotation.identity, Rotation.rot90[EAST.ordinal], Rotation.rot90[SOUTH.ordinal])
val whitewash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.4f) }
val greywash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.0f) }
val BlockState.isSnow: Boolean get() = material.let { it == Material.SNOW }
fun Quad.toCross(rotAxis: Direction, trans: (Quad)->Quad) =
(0..3).map { rotIdx ->
trans(rotate(Rotation.rot90[rotAxis.ordinal] * rotIdx).mirrorUV(rotIdx > 1, false))
}
fun Quad.toCross(rotAxis: Direction) = toCross(rotAxis) { it }
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
val rotationFromUp = arrayOf(
Rotation.rot90[EAST.ordinal] * 2,
Rotation.identity,
Rotation.rot90[WEST.ordinal],
Rotation.rot90[EAST.ordinal],
Rotation.rot90[SOUTH.ordinal],
Rotation.rot90[NORTH.ordinal]
)
fun Model.mix(first: Model, second: Model, predicate: (Int)->Boolean) {
first.quads.forEachIndexed { qi, quad ->
val otherQuad = second.quads[qi]
Quad(
if (predicate(0)) otherQuad.v1.copy() else quad.v1.copy(),
if (predicate(1)) otherQuad.v2.copy() else quad.v2.copy(),
if (predicate(2)) otherQuad.v3.copy() else quad.v3.copy(),
if (predicate(3)) otherQuad.v4.copy() else quad.v4.copy()
).add()
}
}
val BlockRenderLayer.isCutout: Boolean get() = (this == BlockRenderLayer.CUTOUT) || (this == BlockRenderLayer.CUTOUT_MIPPED)
fun BlockState.canRenderInLayer(layer: BlockRenderLayer) = this.block.canRenderInLayer(this, layer)
fun BlockState.canRenderInCutout() = this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT) || this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT_MIPPED)

View File

@@ -1,210 +0,0 @@
package mods.betterfoliage.client.render.column
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs
import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Model
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.noPost
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.face
import mods.octarinecore.common.rot
import net.minecraft.block.BlockRenderType.MODEL
import net.minecraft.util.Direction.*
import net.minecraftforge.eventbus.api.IEventBus
@Suppress("NOTHING_TO_INLINE")
abstract class AbstractRenderColumn(modId: String, modBus: IEventBus) : RenderDecorator(modId, modBus) {
/** The rotations necessary to bring the models in position for the 4 quadrants */
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
// ============================
// Configuration
// ============================
abstract val overlayLayer: ColumnRenderLayer
abstract val connectPerpendicular: Boolean
abstract val radiusSmall: Double
abstract val radiusLarge: Double
// ============================
// Models
// ============================
val sideSquare = model { columnSideSquare(-0.5, 0.5) }
val sideRoundSmall = model { columnSide(radiusSmall, -0.5, 0.5) }
val sideRoundLarge = model { columnSide(radiusLarge, -0.5, 0.5) }
val extendTopSquare = model { columnSideSquare(0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
val extendTopRoundSmall = model { columnSide(radiusSmall, 0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
val extendTopRoundLarge = model { columnSide(radiusLarge, 0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
inline fun extendTop(type: QuadrantType) = when(type) {
SMALL_RADIUS -> extendTopRoundSmall.model
LARGE_RADIUS -> extendTopRoundLarge.model
SQUARE -> extendTopSquare.model
INVISIBLE -> extendTopSquare.model
else -> null
}
val extendBottomSquare = model { columnSideSquare(-0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
val extendBottomRoundSmall = model { columnSide(radiusSmall, -0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
val extendBottomRoundLarge = model { columnSide(radiusLarge, -0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
inline fun extendBottom(type: QuadrantType) = when (type) {
SMALL_RADIUS -> extendBottomRoundSmall.model
LARGE_RADIUS -> extendBottomRoundLarge.model
SQUARE -> extendBottomSquare.model
INVISIBLE -> extendBottomSquare.model
else -> null
}
val topSquare = model { columnLidSquare() }
val topRoundSmall = model { columnLid(radiusSmall) }
val topRoundLarge = model { columnLid(radiusLarge) }
inline fun flatTop(type: QuadrantType) = when(type) {
SMALL_RADIUS -> topRoundSmall.model
LARGE_RADIUS -> topRoundLarge.model
SQUARE -> topSquare.model
INVISIBLE -> topSquare.model
else -> null
}
val bottomSquare = model { columnLidSquare() { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
val bottomRoundSmall = model { columnLid(radiusSmall) { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
val bottomRoundLarge = model { columnLid(radiusLarge) { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
inline fun flatBottom(type: QuadrantType) = when(type) {
SMALL_RADIUS -> bottomRoundSmall.model
LARGE_RADIUS -> bottomRoundLarge.model
SQUARE -> bottomSquare.model
INVISIBLE -> bottomSquare.model
else -> null
}
val transitionTop = model { mix(sideRoundLarge.model, sideRoundSmall.model) { it > 1 } }
val transitionBottom = model { mix(sideRoundSmall.model, sideRoundLarge.model) { it > 1 } }
inline fun continuous(q1: QuadrantType, q2: QuadrantType) =
q1 == q2 || ((q1 == SQUARE || q1 == INVISIBLE) && (q2 == SQUARE || q2 == INVISIBLE))
@Suppress("NON_EXHAUSTIVE_WHEN")
override fun render(ctx: CombinedContext) {
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
when(roundLog) {
ColumnLayerData.SkipRender -> return
ColumnLayerData.NormalRender -> return ctx.render()
ColumnLayerData.ResolveError, null -> {
BetterFoliage.logRenderError(ctx.state, ctx.pos)
return ctx.render()
}
}
// if log axis is not defined and "Default to vertical" config option is not set, render normally
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
return ctx.render()
}
val baseRotation = rotationFromUp[((roundLog.column.axis ?: Axis.Y) to AxisDirection.POSITIVE).face.ordinal]
renderAs(ctx, MODEL) {
quadrantRotations.forEachIndexed { idx, quadrantRotation ->
// set rotation for the current quadrant
val rotation = baseRotation + quadrantRotation
// disallow sharp discontinuities in the chamfer radius, or tapering-in where inappropriate
if (roundLog.quadrants[idx] == LARGE_RADIUS &&
roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] != LARGE_RADIUS &&
roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] != LARGE_RADIUS) {
roundLog.quadrants[idx] = SMALL_RADIUS
}
// render side of current quadrant
val sideModel = when (roundLog.quadrants[idx]) {
SMALL_RADIUS -> sideRoundSmall.model
LARGE_RADIUS -> if (roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] == SMALL_RADIUS) transitionTop.model
else if (roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] == SMALL_RADIUS) transitionBottom.model
else sideRoundLarge.model
SQUARE -> sideSquare.model
else -> null
}
if (sideModel != null) ctx.render(
sideModel,
rotation,
icon = roundLog.column.side,
postProcess = noPost
)
// render top and bottom end of current quadrant
var upModel: Model? = null
var downModel: Model? = null
var upIcon = roundLog.column.top
var downIcon = roundLog.column.bottom
var isLidUp = true
var isLidDown = true
when (roundLog.upType) {
NONSOLID -> upModel = flatTop(roundLog.quadrants[idx])
PERPENDICULAR -> {
if (!connectPerpendicular) {
upModel = flatTop(roundLog.quadrants[idx])
} else {
upIcon = roundLog.column.side
upModel = extendTop(roundLog.quadrants[idx])
isLidUp = false
}
}
PARALLEL -> {
if (!continuous(roundLog.quadrants[idx], roundLog.quadrantsTop[idx])) {
if (roundLog.quadrants[idx] == SQUARE || roundLog.quadrants[idx] == INVISIBLE) {
upModel = topSquare.model
}
}
}
}
when (roundLog.downType) {
NONSOLID -> downModel = flatBottom(roundLog.quadrants[idx])
PERPENDICULAR -> {
if (!connectPerpendicular) {
downModel = flatBottom(roundLog.quadrants[idx])
} else {
downIcon = roundLog.column.side
downModel = extendBottom(roundLog.quadrants[idx])
isLidDown = false
}
}
PARALLEL -> {
if (!continuous(roundLog.quadrants[idx], roundLog.quadrantsBottom[idx]) &&
(roundLog.quadrants[idx] == SQUARE || roundLog.quadrants[idx] == INVISIBLE)) {
downModel = bottomSquare.model
}
}
}
if (upModel != null) ctx.render(
upModel,
rotation,
icon = upIcon,
postProcess = { _, _, _, _, _ ->
if (isLidUp) {
rotateUV(idx + if (roundLog.column.axis == Axis.X) 1 else 0)
}
}
)
if (downModel != null) ctx.render(
downModel,
rotation,
icon = downIcon,
postProcess = { _, _, _, _, _ ->
if (isLidDown) {
rotateUV((if (roundLog.column.axis == Axis.X) 0 else 3) - idx)
}
}
)
}
}
}
}

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