Compare commits
30 Commits
1.14.4-For
...
1.7.2-0.9.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a05a67eda | ||
|
|
43e7fb830b | ||
|
|
c72e93aedf | ||
|
|
f236ef64ad | ||
|
|
d38f556bde | ||
|
|
fad662443d | ||
|
|
32c4dc6035 | ||
|
|
aee6e5caca | ||
|
|
5fbe2ff16f | ||
|
|
7a133c95a7 | ||
|
|
25b1d76c9e | ||
|
|
7a02179481 | ||
|
|
244907f4cd | ||
|
|
4ccd753c0c | ||
|
|
2715acf9c8 | ||
|
|
1c146fb070 | ||
|
|
44a20ceab3 | ||
|
|
1be2382fed | ||
|
|
e12b7b803c | ||
|
|
8a94867cd8 | ||
|
|
3a391f1677 | ||
|
|
d5dd1a36e3 | ||
|
|
a589c868a9 | ||
|
|
37ffa219fc | ||
|
|
220f2356d8 | ||
|
|
ec184f9916 | ||
|
|
2b1fd84cd5 | ||
|
|
30f199e9a2 | ||
|
|
0c66849175 | ||
|
|
3bd402b964 |
8
.classpath
Normal file
8
.classpath
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src/main/java"/>
|
||||||
|
<classpathentry kind="src" path="src/main/resources"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,9 +1,5 @@
|
|||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
run/
|
|
||||||
.gradle/
|
.gradle/
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
build/
|
build/
|
||||||
classes/
|
libs/
|
||||||
temp/
|
|
||||||
|
|||||||
18
.project
Normal file
18
.project
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>BetterFoliage</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
BetterFoliage
|
BetterFoliage
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Minecraft mod that alters the appearance of leaves & grass
|
Minecraft mod that alters the appearance of leaves & grass
|
||||||
|
|
||||||
|
More info: http://www.minecraftforum.net/topic/2776217-better-foliage/
|
||||||
|
|
||||||
|
Download
|
||||||
|
========
|
||||||
|
[BetterFoliage 0.9.4-beta] (http://goo.gl/pNBE23) (MC 1.7.2)
|
||||||
|
|
||||||
|
[BetterFoliage 0.9.4-beta] (http://goo.gl/ywT6Xg) (MC 1.7.10)
|
||||||
|
|||||||
45
build.gradle
Normal file
45
build.gradle
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "forge"
|
||||||
|
url = "http://files.minecraftforge.net/maven"
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = "sonatype"
|
||||||
|
url = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply plugin: 'forge'
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
version = '1.7.2-10.12.2.1147'
|
||||||
|
}
|
||||||
|
|
||||||
|
jar.baseName = 'BetterFoliage-1.7.2'
|
||||||
|
group = 'com.github.octarine-noise'
|
||||||
|
version='0.9.5b'
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +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) }
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx2G
|
|
||||||
org.gradle.daemon=false
|
|
||||||
|
|
||||||
group = com.github.octarine-noise
|
|
||||||
jarName = BetterFoliage-Forge
|
|
||||||
|
|
||||||
version = 2.5.0
|
|
||||||
|
|
||||||
mcVersion = 1.14.4
|
|
||||||
forgeVersion = 28.1.109
|
|
||||||
mappingsChannel = snapshot
|
|
||||||
mappingsVersion = 20190719-1.14.3
|
|
||||||
|
|
||||||
kottleVersion = 1.4.0
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
172
gradlew
vendored
@@ -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
84
gradlew.bat
vendored
@@ -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
|
|
||||||
@@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
48
src/main/java/mods/betterfoliage/BetterFoliage.java
Normal file
48
src/main/java/mods/betterfoliage/BetterFoliage.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package mods.betterfoliage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.config.BetterFoliageConfig;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
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]";
|
||||||
|
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
|
||||||
|
|
||||||
|
@Mod.Instance
|
||||||
|
public static BetterFoliage instance;
|
||||||
|
|
||||||
|
public static BetterFoliageConfig config = new BetterFoliageConfig();
|
||||||
|
|
||||||
|
public static Logger log;
|
||||||
|
|
||||||
|
public static File configDir;
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void preInit(FMLPreInitializationEvent event) {
|
||||||
|
log = event.getModLog();
|
||||||
|
if (event.getSide() == Side.CLIENT) {
|
||||||
|
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
|
||||||
|
configDir.mkdir();
|
||||||
|
config.load(new File(configDir, "betterfoliage.cfg"));
|
||||||
|
BetterFoliageClient.preInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NetworkCheckHandler
|
||||||
|
public boolean checkVersion(Map<String, String> mods, Side side) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
128
src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
Normal file
128
src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
|
||||||
|
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.RenderBlockBetterReed;
|
||||||
|
import mods.betterfoliage.client.resource.BlockTextureGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.HalfTextureResource;
|
||||||
|
import mods.betterfoliage.client.resource.ILeafTextureRecognizer;
|
||||||
|
import mods.betterfoliage.client.resource.LeafTextureGenerator;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockCarrot;
|
||||||
|
import net.minecraft.block.BlockCrops;
|
||||||
|
import net.minecraft.block.BlockDoublePlant;
|
||||||
|
import net.minecraft.block.BlockLeavesBase;
|
||||||
|
import net.minecraft.block.BlockPotato;
|
||||||
|
import net.minecraft.block.BlockReed;
|
||||||
|
import net.minecraft.block.BlockTallGrass;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.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 implements ILeafTextureRecognizer {
|
||||||
|
|
||||||
|
public static Map<Integer, IRenderBlockDecorator> decorators = Maps.newHashMap();
|
||||||
|
public static LeafTextureGenerator leafGenerator;
|
||||||
|
|
||||||
|
public static BlockMatcher leaves;
|
||||||
|
public static BlockMatcher crops;
|
||||||
|
|
||||||
|
public static void preInit() {
|
||||||
|
FMLCommonHandler.instance().bus().register(new KeyHandler());
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Registering renderers");
|
||||||
|
registerRenderer(new RenderBlockBetterLeaves());
|
||||||
|
registerRenderer(new RenderBlockBetterGrass());
|
||||||
|
registerRenderer(new RenderBlockBetterCactus());
|
||||||
|
registerRenderer(new RenderBlockBetterLilypad());
|
||||||
|
registerRenderer(new RenderBlockBetterReed());
|
||||||
|
registerRenderer(new RenderBlockBetterAlgae());
|
||||||
|
|
||||||
|
leaves = new BlockMatcher(BlockLeavesBase.class.getName(),
|
||||||
|
"forestry.arboriculture.gadgets.BlockLeaves",
|
||||||
|
"thaumcraft.common.blocks.BlockMagicalLeaves");
|
||||||
|
leaves.load(new File(BetterFoliage.configDir, "classesLeaves.cfg"));
|
||||||
|
|
||||||
|
crops = new BlockMatcher(BlockCrops.class.getName(),
|
||||||
|
"-" + BlockCarrot.class.getName(),
|
||||||
|
"-" + BlockPotato.class.getName(),
|
||||||
|
BlockTallGrass.class.getName(),
|
||||||
|
BlockDoublePlant.class.getName(),
|
||||||
|
BlockReed.class.getName(),
|
||||||
|
"biomesoplenty.common.blocks.BlockBOPFlower",
|
||||||
|
"biomesoplenty.common.blocks.BlockBOPFlower2",
|
||||||
|
"tconstruct.blocks.slime.SlimeTallGrass");
|
||||||
|
crops.load(new File(BetterFoliage.configDir, "classesCrops.cfg"));
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Registering leaf texture generator");
|
||||||
|
leafGenerator = new LeafTextureGenerator();
|
||||||
|
MinecraftForge.EVENT_BUS.register(leafGenerator);
|
||||||
|
leafGenerator.recognizers.add(new BetterFoliageClient());
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(new BlockTextureGenerator("bf_reed_bottom", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png")) {
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation var1) throws IOException {
|
||||||
|
return new HalfTextureResource(unwrapResource(var1), true, getMissingResource());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MinecraftForge.EVENT_BUS.register(new BlockTextureGenerator("bf_reed_top", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png")) {
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation var1) throws IOException {
|
||||||
|
return new HalfTextureResource(unwrapResource(var1), false, getMissingResource());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(new BetterFoliageClient());
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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 int getGLSLBlockIdOverride(int original, Block block) {
|
||||||
|
if (leaves.matchesID(original & 0xFFFF))
|
||||||
|
return Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | block.getRenderType() << 16;
|
||||||
|
if (crops.matchesID(original & 0xFFFF))
|
||||||
|
return Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | block.getRenderType() << 16;
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerRenderer(IRenderBlockDecorator decorator) {
|
||||||
|
int renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||||
|
decorators.put(renderId, decorator);
|
||||||
|
RenderingRegistry.registerBlockHandler(renderId, decorator);
|
||||||
|
MinecraftForge.EVENT_BUS.register(decorator);
|
||||||
|
decorator.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
116
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
116
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class BlockMatcher {
|
||||||
|
|
||||||
|
public Set<String> whiteList = Sets.newHashSet();
|
||||||
|
public Set<String> blackList = Sets.newHashSet();
|
||||||
|
public Set<Integer> blockIDs = Sets.newHashSet();
|
||||||
|
|
||||||
|
public BlockMatcher(String... defaults) {
|
||||||
|
for (String clazz : defaults) addClass(clazz);
|
||||||
|
MinecraftForge.EVENT_BUS.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addClass(String className) {
|
||||||
|
if (className.startsWith("-"))
|
||||||
|
blackList.add(className.substring(1));
|
||||||
|
else
|
||||||
|
whiteList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesClass(Block block) {
|
||||||
|
for (String className : blackList) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(className);
|
||||||
|
if (clazz.isAssignableFrom(block.getClass())) return false;
|
||||||
|
} catch(ClassNotFoundException e) {}
|
||||||
|
}
|
||||||
|
for (String className : whiteList) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(className);
|
||||||
|
if (clazz.isAssignableFrom(block.getClass())) return true;
|
||||||
|
} catch(ClassNotFoundException e) {}
|
||||||
|
}
|
||||||
|
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 load(File file) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new FileReader(file));
|
||||||
|
whiteList.clear();
|
||||||
|
blackList.clear();
|
||||||
|
String line = reader.readLine();
|
||||||
|
while(line != null) {
|
||||||
|
addClass(line.trim());
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
saveDefaults(file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
BetterFoliage.log.warn(String.format("Error reading configuration: %s", file.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveDefaults(File file) {
|
||||||
|
FileWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = new FileWriter(file);
|
||||||
|
for (String className : whiteList) {
|
||||||
|
writer.write(className);
|
||||||
|
writer.write("\n");
|
||||||
|
}
|
||||||
|
for (String className : blackList) {
|
||||||
|
writer.write("-");
|
||||||
|
writer.write(className);
|
||||||
|
writer.write("\n");
|
||||||
|
}
|
||||||
|
writer.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
saveDefaults(file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
BetterFoliage.log.warn(String.format("Error writing default configuration: %s", file.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caches block IDs on world load for fast lookup
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
blockIDs.clear();
|
||||||
|
Iterator<Block> iter = Block.blockRegistry.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Block block = iter.next();
|
||||||
|
if (matchesClass(block)) blockIDs.add(Block.blockRegistry.getIDForObject(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/main/java/mods/betterfoliage/client/KeyHandler.java
Normal file
27
src/main/java/mods/betterfoliage/client/KeyHandler.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.ConfigGuiMain;
|
||||||
|
import net.minecraft.client.settings.KeyBinding;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class KeyHandler {
|
||||||
|
|
||||||
|
public static KeyBinding guiBinding;
|
||||||
|
|
||||||
|
public KeyHandler() {
|
||||||
|
guiBinding = new KeyBinding("key.betterfoliage.gui", 66, BetterFoliage.MOD_NAME);
|
||||||
|
ClientRegistry.registerKeyBinding(guiBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleKeyPress(InputEvent.KeyInputEvent event) {
|
||||||
|
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiMain(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
|
||||||
|
public class ConfigGuiAlgae extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public ConfigGuiAlgae(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeSize, -100, -40, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMin, -100, -10, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMax, -100, 20, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionIntegerWidget(BetterFoliage.config.algaeChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.algaeChance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(0, x - 50, y + 100, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
|
||||||
|
if (BetterFoliage.config.algaeHeightMin.value > BetterFoliage.config.algaeHeightMax.value) BetterFoliage.config.algaeHeightMin.value = BetterFoliage.config.algaeHeightMax.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import cpw.mods.fml.client.IModGuiFactory;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ConfigGuiFactory implements IModGuiFactory {
|
||||||
|
|
||||||
|
public void initialize(Minecraft minecraftInstance) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
||||||
|
return ConfigGuiMain.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
|
||||||
|
return ImmutableSet.<RuntimeOptionCategoryElement>of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
|
||||||
|
public class ConfigGuiGrass extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public ConfigGuiGrass(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHOffset, -100, -40, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMin, -100, -10, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMax, -100, 20, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
|
||||||
|
if (BetterFoliage.config.grassHeightMin.value > BetterFoliage.config.grassHeightMax.value) BetterFoliage.config.grassHeightMin.value = BetterFoliage.config.grassHeightMax.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
|
||||||
|
public class ConfigGuiLeaves extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public enum Button {CLOSE, LEAVES_OFFSET_MODE}
|
||||||
|
|
||||||
|
public ConfigGuiLeaves(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesVOffset, -100, 20, 200, 50, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
buttonList.add(new GuiButton(Button.LEAVES_OFFSET_MODE.ordinal(), x - 100, y - 40, 200, 20, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateButtons() {
|
||||||
|
setButtonOptionBoolean(Button.LEAVES_OFFSET_MODE.ordinal(), "message.betterfoliage.leavesMode", BetterFoliage.config.leavesSkew ? "message.betterfoliage.leavesSkew" : "message.betterfoliage.leavesTranslate");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == Button.CLOSE.ordinal()) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
if (id == Button.LEAVES_OFFSET_MODE.ordinal()) BetterFoliage.config.leavesSkew = !BetterFoliage.config.leavesSkew;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
|
||||||
|
public class ConfigGuiLilypad extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public ConfigGuiLilypad(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.lilypadHOffset, -100, -40, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionIntegerWidget(BetterFoliage.config.lilypadChance, -100, -10, 200, 50, id++, id++, "message.betterfoliage.flowerChance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ConfigGuiMain extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public enum Button {CLOSE,
|
||||||
|
TOGGLE_LEAVES, CONFIG_LEAVES,
|
||||||
|
TOGGLE_GRASS, CONFIG_GRASS,
|
||||||
|
TOGGLE_CACTUS, CONFIG_CACTUS,
|
||||||
|
TOGGLE_LILYPAD, CONFIG_LILYPAD,
|
||||||
|
TOGGLE_REED, CONFIG_REED,
|
||||||
|
TOGGLE_ALGAE, CONFIG_ALGAE}
|
||||||
|
|
||||||
|
public ConfigGuiMain(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.close")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_LEAVES.ordinal(), x - 100, y - 100, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_LEAVES.ordinal(), x + 60, y - 100, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_GRASS.ordinal(), x - 100, y - 70, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_GRASS.ordinal(), x + 60, y - 70, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_CACTUS.ordinal(), x - 100, y - 40, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_CACTUS.ordinal(), x + 60, y - 40, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_LILYPAD.ordinal(), x - 100, y - 10, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_LILYPAD.ordinal(), x + 60, y - 10, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_REED.ordinal(), x - 100, y + 20, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_REED.ordinal(), x + 60, y + 20, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_ALGAE.ordinal(), x - 100, y + 50, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_ALGAE.ordinal(), x + 60, y + 50, 40, 20, I18n.format("message.betterfoliage.config")));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateButtons() {
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_LEAVES.ordinal(), "message.betterfoliage.betterLeaves", BetterFoliage.config.leavesEnabled);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_GRASS.ordinal(), "message.betterfoliage.betterGrass", BetterFoliage.config.grassEnabled);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_CACTUS.ordinal(), "message.betterfoliage.betterCactus", BetterFoliage.config.cactusEnabled);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_LILYPAD.ordinal(), "message.betterfoliage.betterLilypad", BetterFoliage.config.lilypadEnabled);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_REED.ordinal(), "message.betterfoliage.betterReed", BetterFoliage.config.reedEnabled);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_ALGAE.ordinal(), "message.betterfoliage.betterAlgae", BetterFoliage.config.algaeEnabled);
|
||||||
|
((GuiButton) buttonList.get(Button.CONFIG_CACTUS.ordinal())).enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == Button.CLOSE.ordinal()) {
|
||||||
|
BetterFoliage.config.save();
|
||||||
|
Minecraft.getMinecraft().renderGlobal.loadRenderers();
|
||||||
|
FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
}
|
||||||
|
if (id == Button.TOGGLE_LEAVES.ordinal()) BetterFoliage.config.leavesEnabled = !BetterFoliage.config.leavesEnabled;
|
||||||
|
if (id == Button.TOGGLE_GRASS.ordinal()) BetterFoliage.config.grassEnabled = !BetterFoliage.config.grassEnabled;
|
||||||
|
if (id == Button.TOGGLE_CACTUS.ordinal()) BetterFoliage.config.cactusEnabled = !BetterFoliage.config.cactusEnabled;
|
||||||
|
if (id == Button.TOGGLE_LILYPAD.ordinal()) BetterFoliage.config.lilypadEnabled = !BetterFoliage.config.lilypadEnabled;
|
||||||
|
if (id == Button.TOGGLE_REED.ordinal()) BetterFoliage.config.reedEnabled = !BetterFoliage.config.reedEnabled;
|
||||||
|
if (id == Button.TOGGLE_ALGAE.ordinal()) BetterFoliage.config.algaeEnabled = !BetterFoliage.config.algaeEnabled;
|
||||||
|
|
||||||
|
if (id== Button.CONFIG_LEAVES.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLeaves(this));
|
||||||
|
if (id== Button.CONFIG_GRASS.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiGrass(this));
|
||||||
|
if (id== Button.CONFIG_LILYPAD.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLilypad(this));
|
||||||
|
if (id== Button.CONFIG_REED.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiReed(this));
|
||||||
|
if (id== Button.CONFIG_ALGAE.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiAlgae(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
|
||||||
|
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
|
||||||
|
public class ConfigGuiReed extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public ConfigGuiReed(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMin, -100, -40, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMax, -100, -10, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionIntegerWidget(BetterFoliage.config.reedChance, -100, 20, 200, 50, id++, id++, "message.betterfoliage.reedChance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
|
||||||
|
if (BetterFoliage.config.reedHeightMin.value > BetterFoliage.config.reedHeightMax.value) BetterFoliage.config.reedHeightMin.value = BetterFoliage.config.reedHeightMax.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.gui.widget.IOptionWidget;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
public class ConfigGuiScreenBase extends GuiScreen {
|
||||||
|
|
||||||
|
protected GuiScreen parent;
|
||||||
|
protected List<IOptionWidget> widgets = Lists.newLinkedList();
|
||||||
|
|
||||||
|
public ConfigGuiScreenBase(GuiScreen parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int par1, int par2, float par3) {
|
||||||
|
this.drawDefaultBackground();
|
||||||
|
int x = width / 2;
|
||||||
|
int y = height / 2;
|
||||||
|
for (IOptionWidget widget : widgets) widget.drawStrings(this, fontRendererObj, x, y, 14737632, 16777120);
|
||||||
|
super.drawScreen(par1, par2, par3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void initGui() {
|
||||||
|
int x = width / 2;
|
||||||
|
int y = height / 2;
|
||||||
|
for (IOptionWidget widget : widgets) widget.addButtons(buttonList, x, y);
|
||||||
|
addButtons(x, y);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addButtons(int x, int y) {}
|
||||||
|
|
||||||
|
protected void updateButtons() {}
|
||||||
|
|
||||||
|
protected void onButtonPress(int id) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void actionPerformed(GuiButton button) {
|
||||||
|
super.actionPerformed(button);
|
||||||
|
for (IOptionWidget widget : widgets) widget.onAction(button.id);
|
||||||
|
onButtonPress(button.id);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void setButtonOptionBoolean(int id, String msgKey, boolean option) {
|
||||||
|
for (GuiButton button : (List<GuiButton>) buttonList) {
|
||||||
|
if (button.id == id) {
|
||||||
|
String optionText = option ? (EnumChatFormatting.GREEN + I18n.format("message.betterfoliage.optionOn")) : (EnumChatFormatting.RED + I18n.format("message.betterfoliage.optionOff"));
|
||||||
|
button.displayString = I18n.format(msgKey, optionText);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void setButtonOptionBoolean(int id, String msgKey, String optionKey) {
|
||||||
|
for (GuiButton button : (List<GuiButton>) buttonList) {
|
||||||
|
if (button.id == id) {
|
||||||
|
button.displayString = I18n.format(msgKey, I18n.format(optionKey));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package mods.betterfoliage.client.gui.widget;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.FontRenderer;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
|
||||||
|
public interface IOptionWidget {
|
||||||
|
|
||||||
|
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset);
|
||||||
|
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor);
|
||||||
|
public void onAction(int buttonId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package mods.betterfoliage.client.gui.widget;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.config.OptionDouble;
|
||||||
|
import net.minecraft.client.gui.FontRenderer;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class OptionDoubleWidget implements IOptionWidget {
|
||||||
|
|
||||||
|
public OptionDouble option;
|
||||||
|
public int x;
|
||||||
|
public int y;
|
||||||
|
public int width;
|
||||||
|
public int numWidth;
|
||||||
|
public int idDecrement;
|
||||||
|
public int idIncrement;
|
||||||
|
public String keyLabel;
|
||||||
|
public String formatString;
|
||||||
|
|
||||||
|
public OptionDoubleWidget(OptionDouble option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel, String formatString) {
|
||||||
|
this.option = option;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.numWidth = numWidth;
|
||||||
|
this.idDecrement = idDecrement;
|
||||||
|
this.idIncrement = idIncrement;
|
||||||
|
this.keyLabel = keyLabel;
|
||||||
|
this.formatString = formatString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset) {
|
||||||
|
buttonList.add(new GuiButton(idDecrement, xOffset + x + width - numWidth - 40, yOffset + y, 20, 20, "-"));
|
||||||
|
buttonList.add(new GuiButton(idIncrement, xOffset + x + width - 20, yOffset + y, 20, 20, "+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor) {
|
||||||
|
screen.drawString(fontRenderer, I18n.format(keyLabel), xOffset + x, yOffset + y + 5, labelColor);
|
||||||
|
screen.drawCenteredString(fontRenderer, String.format(formatString, option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAction(int buttonId) {
|
||||||
|
if (buttonId == idDecrement) option.decrement();
|
||||||
|
if (buttonId == idIncrement) option.increment();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package mods.betterfoliage.client.gui.widget;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.config.OptionInteger;
|
||||||
|
import net.minecraft.client.gui.FontRenderer;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class OptionIntegerWidget implements IOptionWidget {
|
||||||
|
|
||||||
|
public OptionInteger option;
|
||||||
|
public int x;
|
||||||
|
public int y;
|
||||||
|
public int width;
|
||||||
|
public int numWidth;
|
||||||
|
public int idDecrement;
|
||||||
|
public int idIncrement;
|
||||||
|
public String keyLabel;
|
||||||
|
|
||||||
|
public OptionIntegerWidget(OptionInteger option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel) {
|
||||||
|
this.option = option;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.numWidth = numWidth;
|
||||||
|
this.idDecrement = idDecrement;
|
||||||
|
this.idIncrement = idIncrement;
|
||||||
|
this.keyLabel = keyLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset) {
|
||||||
|
buttonList.add(new GuiButton(idDecrement, xOffset + x + width - numWidth - 40, yOffset + y, 20, 20, "-"));
|
||||||
|
buttonList.add(new GuiButton(idIncrement, xOffset + x + width - 20, yOffset + y, 20, 20, "+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor) {
|
||||||
|
screen.drawString(fontRenderer, I18n.format(keyLabel), xOffset + x, yOffset + y + 5, labelColor);
|
||||||
|
screen.drawCenteredString(fontRenderer, Integer.toString(option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAction(int buttonId) {
|
||||||
|
if (buttonId == idDecrement) option.decrement();
|
||||||
|
if (buttonId == idIncrement) option.increment();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
|
||||||
|
/** Same as {@link RenderBlockAOBase}, but does not actually render anything.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class FakeRenderBlockAOBase extends RenderBlockAOBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoZNXYPP);
|
||||||
|
saveShadingTopRight(aoZNXYNP);
|
||||||
|
saveShadingBottomLeft(aoZNXYPN);
|
||||||
|
saveShadingBottomRight(aoZNXYNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoZPXYNP);
|
||||||
|
saveShadingTopRight(aoZPXYPP);
|
||||||
|
saveShadingBottomLeft(aoZPXYNN);
|
||||||
|
saveShadingBottomRight(aoZPXYPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoXNYZPN);
|
||||||
|
saveShadingTopRight(aoXNYZPP);
|
||||||
|
saveShadingBottomLeft(aoXNYZNN);
|
||||||
|
saveShadingBottomRight(aoXNYZNP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoXPYZPP);
|
||||||
|
saveShadingTopRight(aoXPYZPN);
|
||||||
|
saveShadingBottomLeft(aoXPYZNP);
|
||||||
|
saveShadingBottomRight(aoXPYZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoYNXZNP);
|
||||||
|
saveShadingTopRight(aoYNXZPP);
|
||||||
|
saveShadingBottomLeft(aoYNXZNN);
|
||||||
|
saveShadingBottomRight(aoYNXZPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoYPXZPP);
|
||||||
|
saveShadingTopRight(aoYPXZNP);
|
||||||
|
saveShadingBottomLeft(aoYPXZPN);
|
||||||
|
saveShadingBottomRight(aoYPXZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public interface IRenderBlockDecorator extends ISimpleBlockRenderingHandler {
|
||||||
|
|
||||||
|
public void init();
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original);
|
||||||
|
|
||||||
|
}
|
||||||
52
src/main/java/mods/betterfoliage/client/render/IconSet.java
Normal file
52
src/main/java/mods/betterfoliage/client/render/IconSet.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Loads an indexed set of textures
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class IconSet {
|
||||||
|
|
||||||
|
/** Icon array */
|
||||||
|
public IIcon[] icons = new IIcon[16];
|
||||||
|
|
||||||
|
/** Number of successfully loaded icons*/
|
||||||
|
public int numLoaded = 0;
|
||||||
|
|
||||||
|
/** Resource domain of icons */
|
||||||
|
String domain;
|
||||||
|
|
||||||
|
/** Format string of icon paths */
|
||||||
|
String path;
|
||||||
|
|
||||||
|
public IconSet(String domain, String path) {
|
||||||
|
this.domain = domain;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerIcons(IIconRegister register) {
|
||||||
|
numLoaded = 0;
|
||||||
|
for (int idx = 0; idx < 16; idx++) {
|
||||||
|
icons[idx] = null;
|
||||||
|
// if the path contains a domain, use that to check if the resource exists
|
||||||
|
String resolvedDomain = path.contains(":") ? new ResourceLocation(path).getResourceDomain() : domain;
|
||||||
|
String resolvedPath = String.format("textures/blocks/" + (path.contains(":") ? new ResourceLocation(path).getResourcePath() : path) + ".png", idx);
|
||||||
|
if (Utils.resourceExists(new ResourceLocation(resolvedDomain, resolvedPath)))
|
||||||
|
icons[numLoaded++] = register.registerIcon(domain + ":" + String.format(path, idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IIcon get(int variation) {
|
||||||
|
return numLoaded == 0 ? null : icons[variation % numLoaded];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasIcons() {
|
||||||
|
return numLoaded > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.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.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Block renderer base class. Stores calculated ambient occlusion light and color values when rendering
|
||||||
|
* block sides for later use.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockAOBase extends RenderBlocks {
|
||||||
|
|
||||||
|
/** AO light and color values
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public static class ShadingValues {
|
||||||
|
public int passCounter = 0;
|
||||||
|
public int brightness;
|
||||||
|
public float red;
|
||||||
|
public float green;
|
||||||
|
public float blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
|
||||||
|
protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
|
||||||
|
|
||||||
|
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 renderStandardBlockAsItem(RenderBlocks renderer, Block p_147800_1_, int p_147800_2_, float p_147800_3_) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
boolean flag = p_147800_1_ == Blocks.grass;
|
||||||
|
|
||||||
|
float f2;
|
||||||
|
float f3;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
p_147800_1_.setBlockBoundsForItemRender();
|
||||||
|
renderer.setRenderBoundsFromBlock(p_147800_1_);
|
||||||
|
GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
|
||||||
|
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, -1.0F, 0.0F);
|
||||||
|
renderer.renderFaceYNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 0, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
if (flag && renderer.useInventoryTint)
|
||||||
|
{
|
||||||
|
k = p_147800_1_.getRenderColor(p_147800_2_);
|
||||||
|
f2 = (float)(k >> 16 & 255) / 255.0F;
|
||||||
|
f3 = (float)(k >> 8 & 255) / 255.0F;
|
||||||
|
float f4 = (float)(k & 255) / 255.0F;
|
||||||
|
GL11.glColor4f(f2 * p_147800_3_, f3 * p_147800_3_, f4 * p_147800_3_, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 1.0F, 0.0F);
|
||||||
|
renderer.renderFaceYPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 1, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
if (flag && renderer.useInventoryTint)
|
||||||
|
{
|
||||||
|
GL11.glColor4f(p_147800_3_, p_147800_3_, p_147800_3_, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 0.0F, -1.0F);
|
||||||
|
renderer.renderFaceZNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 2, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 0.0F, 1.0F);
|
||||||
|
renderer.renderFaceZPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 3, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
|
||||||
|
renderer.renderFaceXNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 4, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(1.0F, 0.0F, 0.0F);
|
||||||
|
renderer.renderFaceXPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 5, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glTranslatef(0.5F, 0.5F, 0.5F);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setShadingForFace(ForgeDirection dir) {
|
||||||
|
if (dir == ForgeDirection.DOWN) {
|
||||||
|
// dir1 WEST, dir2 NORTH
|
||||||
|
faceAOPP = aoYNXZPP; faceAOPN = aoYNXZPN; faceAONN = aoYNXZNN; faceAONP = aoYNXZNP;
|
||||||
|
} else if (dir == ForgeDirection.UP) {
|
||||||
|
// dir1 WEST, dir2 SOUTH
|
||||||
|
faceAOPP = aoYPXZPP; faceAOPN = aoYPXZPN; faceAONN = aoYPXZNN; faceAONP = aoYPXZNP;
|
||||||
|
} else if (dir == ForgeDirection.NORTH) {
|
||||||
|
// dir1 WEST, dir2 UP
|
||||||
|
faceAOPP = aoZNXYNP; faceAOPN = aoZNXYNN; faceAONN = aoZNXYPN; faceAONP = aoZNXYPP;
|
||||||
|
} else if (dir == ForgeDirection.SOUTH) {
|
||||||
|
// dir1 EAST, dir2 UP
|
||||||
|
faceAOPP = aoZPXYPP; faceAOPN = aoZPXYPN; faceAONN = aoZPXYNN; faceAONP = aoZPXYNP;
|
||||||
|
} else if (dir == ForgeDirection.WEST) {
|
||||||
|
// dir1 SOUTH, dir2 UP
|
||||||
|
faceAOPP = aoXNYZPP; faceAOPN = aoXNYZNP; faceAONN = aoXNYZNN; faceAONP = aoXNYZPN;
|
||||||
|
} else if (dir == ForgeDirection.EAST) {
|
||||||
|
// dir1 NORTH, dir2 UP
|
||||||
|
faceAOPP = aoXPYZPN; faceAOPN = aoXPYZNN; faceAONN = aoXPYZNP; faceAONP = aoXPYZPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderCrossedSideQuads(Double3 drawBase, ForgeDirection dir, double scale, double halfHeight, Double3 rendomVec, double offset, IIcon renderIcon, int uvRot, boolean noShading) {
|
||||||
|
Double3 facePP, faceNP, faceNormal, drawCenter;
|
||||||
|
|
||||||
|
if (dir == ForgeDirection.UP) {
|
||||||
|
// special case for block top, we'll be rendering a LOT of those
|
||||||
|
facePP = new Double3(-scale, 0.0, scale);
|
||||||
|
faceNP = new Double3(scale, 0.0, scale);
|
||||||
|
faceNormal = new Double3(0.0, halfHeight, 0.0);
|
||||||
|
drawCenter = drawBase.add(faceNormal);
|
||||||
|
if (rendomVec != null) {
|
||||||
|
drawCenter = drawBase.add(faceNormal).add(rendomVec.scaleAxes(-offset, 0.0, offset));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
facePP = new Double3(faceDir1[dir.ordinal()]).add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||||
|
faceNP = new Double3(faceDir1[dir.ordinal()]).inverse().add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||||
|
faceNormal = new Double3(dir).scale(halfHeight);
|
||||||
|
drawCenter = drawBase.add(faceNormal);
|
||||||
|
if (rendomVec != null) {
|
||||||
|
drawCenter = drawCenter.add(new Double3(faceDir1[dir.ordinal()]).scale(rendomVec.x).scale(offset))
|
||||||
|
.add(new Double3(faceDir2[dir.ordinal()]).scale(rendomVec.z).scale(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled() && !noShading) {
|
||||||
|
setShadingForFace(dir);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, facePP, faceNormal, uvRot, faceAOPP, faceAONN, faceAONN, faceAOPP);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot, faceAONN, faceAOPP, faceAOPP, faceAONN);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, faceNP, faceNormal, uvRot, faceAONP, faceAOPN, faceAOPN, faceAONP);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot, faceAOPN, faceAONP, faceAONP, faceAOPN);
|
||||||
|
} else {
|
||||||
|
renderQuad(renderIcon, drawCenter, facePP, faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, faceNP, faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCrossedBlockQuadsTranslate(Double3 blockCenter, double halfSize, Double3 offsetVec, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
Double3 drawCenter = blockCenter;
|
||||||
|
if (offsetVec != null) drawCenter = drawCenter.add(offsetVec);
|
||||||
|
Double3 horz1 = new Double3(halfSize, 0.0, halfSize);
|
||||||
|
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize);
|
||||||
|
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||||
|
|
||||||
|
renderCrossedBlockQuadsInternal(drawCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCrossedBlockQuadsSkew(Double3 blockCenter, double halfSize, Double3 offsetVec1, Double3 offsetVec2, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
Double3 horz1 = new Double3(halfSize, 0.0, halfSize).add(offsetVec1);
|
||||||
|
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize).add(offsetVec2);
|
||||||
|
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||||
|
|
||||||
|
renderCrossedBlockQuadsInternal(blockCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderCrossedBlockQuadsInternal(Double3 drawCenter, Double3 horz1, Double3 horz2, Double3 vert1, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz1, vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZPP : aoZPXYPP, isAirTop ? aoYPXZNN : aoXNYZPN, isAirBottom ? aoYNXZNN : aoXNYZNN, isAirBottom ? aoYNXZPP : aoZPXYPN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZNN : aoZNXYNP, isAirTop ? aoYPXZPP : aoXPYZPP, isAirBottom ? aoYNXZPP : aoXPYZNP, isAirBottom ? aoYNXZNN : aoZNXYNN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz2, vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZPN : aoXPYZPN, isAirTop ? aoYPXZNP : aoZPXYNP, isAirBottom ? aoYNXZNP : aoZPXYNN, isAirBottom ? aoYNXZPN : aoXPYZNN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZNP : aoXNYZPP, isAirTop ? aoYPXZPN : aoZNXYPP, isAirBottom ? aoYNXZPN : aoZNXYPN, isAirBottom ? aoYNXZNP : aoXNYZNP);
|
||||||
|
} else {
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz1, vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz2, vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoZNXYPP);
|
||||||
|
saveShadingTopRight(aoZNXYNP);
|
||||||
|
saveShadingBottomLeft(aoZNXYPN);
|
||||||
|
saveShadingBottomRight(aoZNXYNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoZPXYNP);
|
||||||
|
saveShadingTopRight(aoZPXYPP);
|
||||||
|
saveShadingBottomLeft(aoZPXYNN);
|
||||||
|
saveShadingBottomRight(aoZPXYPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoXNYZPN);
|
||||||
|
saveShadingTopRight(aoXNYZPP);
|
||||||
|
saveShadingBottomLeft(aoXNYZNN);
|
||||||
|
saveShadingBottomRight(aoXNYZNP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoXPYZPP);
|
||||||
|
saveShadingTopRight(aoXPYZPN);
|
||||||
|
saveShadingBottomLeft(aoXPYZNP);
|
||||||
|
saveShadingBottomRight(aoXPYZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceYNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoYNXZNP);
|
||||||
|
saveShadingTopRight(aoYNXZPP);
|
||||||
|
saveShadingBottomLeft(aoYNXZNN);
|
||||||
|
saveShadingBottomRight(aoYNXZPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceYPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoYPXZPP);
|
||||||
|
saveShadingTopRight(aoYPXZNP);
|
||||||
|
saveShadingBottomLeft(aoYPXZPN);
|
||||||
|
saveShadingBottomRight(aoYPXZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingTopLeft(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessTopLeft;
|
||||||
|
values.red = colorRedTopLeft;
|
||||||
|
values.green = colorGreenTopLeft;
|
||||||
|
values.blue = colorBlueTopLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingTopRight(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessTopRight;
|
||||||
|
values.red = colorRedTopRight;
|
||||||
|
values.green = colorGreenTopRight;
|
||||||
|
values.blue = colorBlueTopRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingBottomLeft(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessBottomLeft;
|
||||||
|
values.red = colorRedBottomLeft;
|
||||||
|
values.green = colorGreenBottomLeft;
|
||||||
|
values.blue = colorBlueBottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingBottomRight(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessBottomRight;
|
||||||
|
values.red = colorRedBottomRight;
|
||||||
|
values.green = colorGreenBottomRight;
|
||||||
|
values.blue = colorBlueBottomRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set pass counter on all shading value objects.
|
||||||
|
* Used to collect AO values from a specific draw pass
|
||||||
|
* if the underlying renderer draws overlays
|
||||||
|
* @param value pass counter
|
||||||
|
*/
|
||||||
|
protected void setPassCounters(int value) {
|
||||||
|
aoXPYZPP.passCounter = value;
|
||||||
|
aoXPYZPN.passCounter = value;
|
||||||
|
aoXPYZNP.passCounter = value;
|
||||||
|
aoXPYZNN.passCounter = value;
|
||||||
|
aoXNYZPP.passCounter = value;
|
||||||
|
aoXNYZPN.passCounter = value;
|
||||||
|
aoXNYZNP.passCounter = value;
|
||||||
|
aoXNYZNN.passCounter = value;
|
||||||
|
aoYPXZPP.passCounter = value;
|
||||||
|
aoYPXZPN.passCounter = value;
|
||||||
|
aoYPXZNP.passCounter = value;
|
||||||
|
aoYPXZNN.passCounter = value;
|
||||||
|
aoYNXZPP.passCounter = value;
|
||||||
|
aoYNXZPN.passCounter = value;
|
||||||
|
aoYNXZNP.passCounter = value;
|
||||||
|
aoYNXZNN.passCounter = value;
|
||||||
|
aoZPXYPP.passCounter = value;
|
||||||
|
aoZPXYPN.passCounter = value;
|
||||||
|
aoZPXYNP.passCounter = value;
|
||||||
|
aoZPXYNN.passCounter = value;
|
||||||
|
aoZNXYPP.passCounter = value;
|
||||||
|
aoZNXYPN.passCounter = value;
|
||||||
|
aoZNXYNP.passCounter = value;
|
||||||
|
aoZNXYNN.passCounter = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render textured quad
|
||||||
|
* @param icon texture to use
|
||||||
|
* @param center center of quad
|
||||||
|
* @param vec1 vector to the half-point of one of the sides
|
||||||
|
* @param vec2 vector to half-point of side next to vec1
|
||||||
|
* @param uvRot number of increments to rotate UV coordinates by
|
||||||
|
*/
|
||||||
|
protected void renderQuad(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render textured quad using AO information
|
||||||
|
* @param icon texture to use
|
||||||
|
* @param center center of quad
|
||||||
|
* @param vec1 vector to the half-point of one of the sides
|
||||||
|
* @param vec2 vector to half-point of side next to vec1
|
||||||
|
* @param uvRot number of increments to rotate UV coordinates by
|
||||||
|
* @param aoPP AO values for vertex at (+vec1, +vec2)
|
||||||
|
* @param aoNP AO values for vertex at (-vec1, +vec2)
|
||||||
|
* @param aoNN AO values for vertex at (-vec1, -vec2)
|
||||||
|
* @param aoPN AO values for vertex at (+vec1, -vec2)
|
||||||
|
*/
|
||||||
|
protected void renderQuadWithShading(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot, ShadingValues aoPP, ShadingValues aoNP, ShadingValues aoNN, ShadingValues aoPN) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
tessellator.setBrightness(aoPP.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoPP.red, aoPP.green, aoPP.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||||
|
tessellator.setBrightness(aoNP.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoNP.red, aoNP.green, aoNP.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||||
|
tessellator.setBrightness(aoNN.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoNN.red, aoNN.green, aoNN.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||||
|
tessellator.setBrightness(aoPN.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoPN.red, aoPN.green, aoPN.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getBrightness(Block block, int x, int y, int z) {
|
||||||
|
return block.getMixedBrightnessForBlock(blockAccess, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
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.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockDirt;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
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 (!BetterFoliage.config.algaeEnabled) return false;
|
||||||
|
if (y >= 254 || !(block instanceof BlockDirt)) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 2, z).getMaterial() != Material.water) return false;
|
||||||
|
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < BetterFoliage.config.algaeChance.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render grass block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
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 = BetterFoliage.config.algaeSize.value * 0.5;
|
||||||
|
double halfHeight = 0.5 * (BetterFoliage.config.algaeHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.algaeHeightMax.value - BetterFoliage.config.algaeHeightMin.value));
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.algaeHOffset.value, renderIcon, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
algaeIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d algae textures", algaeIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
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.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");
|
||||||
|
|
||||||
|
public static ForgeDirection[] cactusDirections = new ForgeDirection[] { ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.EAST, ForgeDirection.WEST};
|
||||||
|
public static double cactusRadius = 0.4375;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
return BetterFoliage.config.cactusEnabled && block == Blocks.cactus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render cactus center
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
|
||||||
|
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block,x, y, z));
|
||||||
|
renderCactusCore(block.getBlockTextureFromSide(ForgeDirection.UP.ordinal()),
|
||||||
|
block.getBlockTextureFromSide(ForgeDirection.NORTH.ordinal()),
|
||||||
|
blockCenter, 0);
|
||||||
|
|
||||||
|
// render side growth
|
||||||
|
ForgeDirection drawDirection = cactusDirections[getSemiRandomFromPos(x, y, z, 0) % 4];
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
Double3 drawBase = blockCenter.add(new Double3(drawDirection).scale(cactusRadius));
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
if (cactusSideIcons.hasIcons()) renderCrossedSideQuads(drawBase, drawDirection, 0.5, 0.5, pRot[iconVariation], 0.2, cactusSideIcons.get(iconVariation), 0, false);
|
||||||
|
renderCrossedBlockQuadsSkew(blockCenter, 0.65,
|
||||||
|
pRot[iconVariation].scaleAxes(0.1, 0.0, 0.1),
|
||||||
|
pRot[(iconVariation + 1) & 63].scaleAxes(0.1, 0.0, 0.1),
|
||||||
|
cactusRoundIcon, iconVariation, false, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCactusCore(IIcon topIcon, IIcon sideIcon, Double3 blockCenter, int sideUvRot) {
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXPYZPN, aoXPYZPP, aoXPYZNP, aoXPYZNN);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXNYZPP, aoXNYZPN, aoXNYZNN, aoXNYZNP);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZPXYPP, aoZPXYNP, aoZPXYNN, aoZPXYPN);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZNXYNP, aoZNXYPP, aoZNXYPN, aoZNXYNN);
|
||||||
|
renderQuadWithShading(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0, aoYPXZNP, aoYPXZPP, aoYPXZPN, aoYPXZNN);
|
||||||
|
} else {
|
||||||
|
renderQuad(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
cactusRoundIcon = event.map.registerIcon("bettergrassandleaves:better_cactus");
|
||||||
|
cactusSideIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d cactus arm textures", cactusSideIcons.numLoaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
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.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockGrass;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterGrass extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet grassIcons = new IconSet("bettergrassandleaves", "better_grass_long_%d");
|
||||||
|
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!BetterFoliage.config.grassEnabled) return false;
|
||||||
|
if (!((block instanceof BlockGrass || block == Blocks.mycelium))) return false;
|
||||||
|
if (y == 255 || !blockAccess.isAirBlock(x, y + 1, z)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render grass block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
int variation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
|
||||||
|
IIcon renderIcon = (block == Blocks.mycelium) ? myceliumIcons.get(variation) : grassIcons.get(variation);
|
||||||
|
if (renderIcon == null) return true;
|
||||||
|
|
||||||
|
double scale = BetterFoliage.config.grassSize.value * 0.5;
|
||||||
|
double halfHeight = 0.5 * (BetterFoliage.config.grassHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.grassHeightMax.value - BetterFoliage.config.grassHeightMin.value));
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.grassHOffset.value, renderIcon, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
grassIcons.registerIcons(event.map);
|
||||||
|
myceliumIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d short grass textures", grassIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!BetterFoliage.config.leavesEnabled) return false;
|
||||||
|
if (original > 0 && original < 42) return false;
|
||||||
|
return BetterFoliageClient.leaves.matchesID(block) && !isBlockSurrounded(blockAccess, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render leaves center
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
if (block.getRenderType() == 0) {
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
} else {
|
||||||
|
ISimpleBlockRenderingHandler handler = Utils.getRenderingHandler(block.getRenderType());
|
||||||
|
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find generated texture to render with, assume the
|
||||||
|
// "true" texture of the block is the one on the north size
|
||||||
|
TextureAtlasSprite blockLeafIcon = (TextureAtlasSprite) block.getIcon(world, x, y, z, ForgeDirection.NORTH.ordinal());
|
||||||
|
IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
|
||||||
|
if (crossLeafIcon == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
double halfSize = 0.5 * BetterFoliage.config.leavesSize.value;
|
||||||
|
boolean isAirTop = y == 255 || blockAccess.isAirBlock(x, y + 1, z);
|
||||||
|
boolean isAirBottom = y == 0 || blockAccess.isAirBlock(x, y - 1, z);
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(isAirTop ? getBrightness(block, x, y + 1, z) : (isAirBottom ? getBrightness(block, x, y - 1, z) : getBrightness(block, x, y, z)));
|
||||||
|
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
|
||||||
|
|
||||||
|
if (BetterFoliage.config.leavesSkew) {
|
||||||
|
renderCrossedBlockQuadsSkew(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
|
||||||
|
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
|
||||||
|
pRot[(offsetVariation + 1) & 63].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
|
||||||
|
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||||
|
} else {
|
||||||
|
renderCrossedBlockQuadsTranslate(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
|
||||||
|
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
|
||||||
|
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isBlockSurrounded(IBlockAccess blockAccess, int x, int y, int z) {
|
||||||
|
if (blockAccess.isAirBlock(x + 1, y, z)) return false;
|
||||||
|
if (blockAccess.isAirBlock(x - 1, y, z)) return false;
|
||||||
|
if (blockAccess.isAirBlock(x, y, z + 1)) return false;
|
||||||
|
if (blockAccess.isAirBlock(x, y, z - 1)) return false;
|
||||||
|
if (y == 255 || blockAccess.isAirBlock(x, y + 1, z)) return false;
|
||||||
|
if (y == 0 || blockAccess.isAirBlock(x, y - 1, z)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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.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 BetterFoliage.config.lilypadEnabled && block == Blocks.waterlily;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render grass block
|
||||||
|
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 < BetterFoliage.config.lilypadChance.value && lilypadFlowers.hasIcons())
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 0.02, z + 0.5), ForgeDirection.UP,
|
||||||
|
0.2, 0.3,
|
||||||
|
pRot[offsetVariation], BetterFoliage.config.lilypadHOffset.value,
|
||||||
|
lilypadFlowers.get(iconVariation), 0,
|
||||||
|
true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
lilypadFlowers.registerIcons(event.map);
|
||||||
|
lilypadRoots.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d lilypad flower textures", lilypadFlowers.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d lilypad root textures", lilypadRoots.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
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.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockDirt;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
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 (!BetterFoliage.config.reedEnabled) return false;
|
||||||
|
if (y >= 254 || !(block instanceof BlockDirt)) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
|
||||||
|
if (!blockAccess.isAirBlock(x, y + 2, z)) return false;
|
||||||
|
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f || blockAccess.getBiomeGenForCoords(x, z).rainfall < 0.4f) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < BetterFoliage.config.reedChance.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// render grass block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
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 * (BetterFoliage.config.reedHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.reedHeightMax.value - BetterFoliage.config.reedHeightMin.value));
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 2, z));
|
||||||
|
Tessellator.instance.setColorOpaque(255, 255, 255);
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, bottomIcon, 0, true);
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + 2.0 * quarterHeight, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, topIcon, 0, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
reedBottomIcons.registerIcons(event.map);
|
||||||
|
reedTopIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d reed textures", reedBottomIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
/** Number of textures generated in the current run */
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
this.missingResource = missingResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStitchStart(TextureStitchEvent.Pre event) {}
|
||||||
|
|
||||||
|
public void onStitchEnd(TextureStitchEvent.Post event) {}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
blockTextures = event.map;
|
||||||
|
counter = 0;
|
||||||
|
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||||
|
if (domainManagers == null) {
|
||||||
|
BetterFoliage.log.warn("Failed to inject texture generator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
domainManagers.put(domainName, this);
|
||||||
|
|
||||||
|
onStitchStart(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
blockTextures = null;
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
// don't leave a mess
|
||||||
|
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||||
|
if (domainManagers != null) domainManagers.remove(domainName);
|
||||||
|
|
||||||
|
onStitchEnd(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.client.resources.data.IMetadataSection;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
/** {@link IResource} of PNG containing one half (top or bottom) of a given texture resource
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class HalfTextureResource implements IResource {
|
||||||
|
|
||||||
|
/** Raw PNG data*/
|
||||||
|
public byte[] data = null;
|
||||||
|
|
||||||
|
/** Resource to return if generation fails */
|
||||||
|
public IResource fallbackResource;
|
||||||
|
|
||||||
|
public HalfTextureResource(ResourceLocation resource, boolean bottom, IResource fallbackResource) {
|
||||||
|
this.fallbackResource = fallbackResource;
|
||||||
|
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
try {
|
||||||
|
// load full texture
|
||||||
|
ResourceLocation origResource = new ResourceLocation(resource.getResourceDomain(), "textures/blocks/" + resource.getResourcePath());
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(origResource).getInputStream());
|
||||||
|
|
||||||
|
// draw half texture
|
||||||
|
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = result.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, bottom ? -origImage.getHeight() / 2 : 0, null);
|
||||||
|
|
||||||
|
// create PNG image
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(result, "PNG", baos);
|
||||||
|
data = baos.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// stop log spam with GLSL installed
|
||||||
|
if (e instanceof FileNotFoundException) return;
|
||||||
|
BetterFoliage.log.info(String.format("Could not load texture: %s, exception: %s", resource.toString(), e.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return data != null ? new ByteArrayInputStream(data) : fallbackResource.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMetadata() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMetadataSection getMetadata(String var1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public interface ILeafTextureRecognizer {
|
||||||
|
|
||||||
|
public boolean isLeafTexture(TextureAtlasSprite icon);
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.util.DeobfNames;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent.Post;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Generates rounded crossleaf textures for all registered normal leaf textures at stitch time.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class LeafTextureGenerator extends BlockTextureGenerator implements IIconRegister {
|
||||||
|
|
||||||
|
public String nonGeneratedDomain = "betterfoliage";
|
||||||
|
|
||||||
|
public int nonGeneratedCounter = 0;
|
||||||
|
|
||||||
|
public LeafTextureGenerator() {
|
||||||
|
super("bf_leaves_autogen", new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** List of helpers which can identify leaf textures loaded by alternate means */
|
||||||
|
public List<ILeafTextureRecognizer> recognizers = Lists.newLinkedList();
|
||||||
|
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
ResourceLocation original = unwrapResource(resourceLocation);
|
||||||
|
|
||||||
|
// check for provided texture
|
||||||
|
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format("textures/blocks/%s/%s", original.getResourceDomain(), original.getResourcePath()));
|
||||||
|
if (Utils.resourceExists(handDrawnLocation)) {
|
||||||
|
nonGeneratedCounter++;
|
||||||
|
return Minecraft.getMinecraft().getResourceManager().getResource(handDrawnLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate our own
|
||||||
|
LeafTextureResource result = new LeafTextureResource(original, getMissingResource());
|
||||||
|
if (result.data != null) counter++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Leaf blocks register their textures here. An extra texture will be registered in the atlas
|
||||||
|
* for each, with the resource domain of this generator.
|
||||||
|
* @return the originally registered {@link IIcon} already in the atlas
|
||||||
|
*/
|
||||||
|
public IIcon registerIcon(String resourceLocation) {
|
||||||
|
IIcon original = blockTextures.getTextureExtry(resourceLocation);
|
||||||
|
blockTextures.registerIcon(new ResourceLocation(domainName, resourceLocation).toString());
|
||||||
|
BetterFoliage.log.debug(String.format("Found leaf texture: %s", resourceLocation));
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Iterates through all leaf blocks in the registry and makes them register
|
||||||
|
* their textures to "sniff out" all leaf textures.
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void onStitchStart(Pre event) {
|
||||||
|
nonGeneratedCounter = 0;
|
||||||
|
BetterFoliage.log.info("Reloading leaf textures");
|
||||||
|
|
||||||
|
// register simple block textures
|
||||||
|
Iterator<Block> iter = Block.blockRegistry.iterator();
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
Block block = iter.next();
|
||||||
|
if (BetterFoliageClient.leaves.matchesClass(block)) {
|
||||||
|
BetterFoliage.log.debug(String.format("Inspecting leaf block: %s", block.getClass().getName()));
|
||||||
|
block.registerBlockIcons(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enumerate all registered textures, find leaf textures among them
|
||||||
|
Map<String, TextureAtlasSprite> mapAtlas = null;
|
||||||
|
mapAtlas = Utils.getField(blockTextures, DeobfNames.TM_MRS_SRG, Map.class);
|
||||||
|
if (mapAtlas == null) mapAtlas = Utils.getField(blockTextures, DeobfNames.TM_MRS_MCP, Map.class);
|
||||||
|
if (mapAtlas == null) {
|
||||||
|
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
|
||||||
|
} else {
|
||||||
|
Set<String> foundLeafTextures = Sets.newHashSet();
|
||||||
|
for (TextureAtlasSprite icon : mapAtlas.values())
|
||||||
|
for (ILeafTextureRecognizer recognizer : recognizers)
|
||||||
|
if (recognizer.isLeafTexture(icon))
|
||||||
|
foundLeafTextures.add(icon.getIconName());
|
||||||
|
for (String resourceLocation : foundLeafTextures) {
|
||||||
|
BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", resourceLocation));
|
||||||
|
blockTextures.registerIcon(new ResourceLocation(domainName, resourceLocation).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStitchEnd(Post event) {
|
||||||
|
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", nonGeneratedCounter));
|
||||||
|
BetterFoliage.log.info(String.format("Generated %d leaf textures", counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.client.resources.data.IMetadataSection;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** {@link IResource} containing an autogenerated round crossleaf texture
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class LeafTextureResource implements IResource {
|
||||||
|
|
||||||
|
/** Raw PNG data*/
|
||||||
|
protected byte[] data = null;
|
||||||
|
|
||||||
|
/** Name of the default alpha mask to use */
|
||||||
|
public static String defaultMask = "rough";
|
||||||
|
|
||||||
|
/** Resource to return if generation fails */
|
||||||
|
public IResource fallbackResource;
|
||||||
|
|
||||||
|
public LeafTextureResource(ResourceLocation resLeaf, IResource fallbackResource) {
|
||||||
|
this.fallbackResource = fallbackResource;
|
||||||
|
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
try {
|
||||||
|
// load normal leaf texture
|
||||||
|
ResourceLocation origResource = new ResourceLocation(resLeaf.getResourceDomain(), "textures/blocks/" + resLeaf.getResourcePath());
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(origResource).getInputStream());
|
||||||
|
if (origImage.getWidth() != origImage.getHeight()) return;
|
||||||
|
int size = origImage.getWidth();
|
||||||
|
|
||||||
|
// load alpha mask of appropriate size
|
||||||
|
BufferedImage maskImage = loadLeafMaskImage(defaultMask, size * 2);
|
||||||
|
int scale = size * 2 / maskImage.getWidth();
|
||||||
|
|
||||||
|
// tile leaf texture 2x2
|
||||||
|
BufferedImage overlayIcon = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = overlayIcon.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, 0, null);
|
||||||
|
graphics.drawImage(origImage, 0, size, null);
|
||||||
|
graphics.drawImage(origImage, size, 0, null);
|
||||||
|
graphics.drawImage(origImage, size, size, null);
|
||||||
|
|
||||||
|
// overlay mask alpha on texture
|
||||||
|
for (int x = 0; x < overlayIcon.getWidth(); x++) {
|
||||||
|
for (int y = 0; y < overlayIcon.getHeight(); y++) {
|
||||||
|
long origPixel = overlayIcon.getRGB(x, y) & 0xFFFFFFFFl;
|
||||||
|
long maskPixel = maskImage.getRGB(x / scale, y / scale) & 0xFF000000l | 0x00FFFFFF;
|
||||||
|
overlayIcon.setRGB(x, y, (int) (origPixel & maskPixel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create PNG image
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(overlayIcon, "PNG", baos);
|
||||||
|
data = baos.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// stop log spam with GLSL installed
|
||||||
|
if (e instanceof FileNotFoundException) return;
|
||||||
|
BetterFoliage.log.info(String.format("Could not create leaf texture: %s, exception: %s", resLeaf.toString(), e.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads the alpha mask of the given type and size. If a mask of the exact size can not be found,
|
||||||
|
* will try to load progressively smaller masks down to 16x16
|
||||||
|
* @param type mask type
|
||||||
|
* @param size texture size
|
||||||
|
* @return alpha mask
|
||||||
|
*/
|
||||||
|
protected BufferedImage loadLeafMaskImage(String type, int size) {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
IResource maskResource = null;
|
||||||
|
|
||||||
|
while (maskResource == null && size >= 16) {
|
||||||
|
try {
|
||||||
|
maskResource = resourceManager.getResource(new ResourceLocation(String.format("betterfoliage:textures/blocks/leafmask_%d_%s.png", size, type)));
|
||||||
|
} catch (Exception e) {}
|
||||||
|
size /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return maskResource == null ? null : ImageIO.read(maskResource.getInputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return data != null ? new ByteArrayInputStream(data) : fallbackResource.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMetadata() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMetadataSection getMetadata(String var1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package mods.betterfoliage.common.config;
|
||||||
|
|
||||||
|
public class BetterFoliageConfig extends ConfigBase {
|
||||||
|
|
||||||
|
@CfgElement(category="leaves", key="enabled")
|
||||||
|
public boolean leavesEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="leaves", key="skewMode")
|
||||||
|
public boolean leavesSkew = false;
|
||||||
|
|
||||||
|
@CfgElement(category="grass", key="enabled")
|
||||||
|
public boolean grassEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="cactus", key="enabled")
|
||||||
|
public boolean cactusEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="lilypad", key="enabled")
|
||||||
|
public boolean lilypadEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="reed", key="enabled")
|
||||||
|
public boolean reedEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="enabled")
|
||||||
|
public boolean algaeEnabled = true;
|
||||||
|
|
||||||
|
@CfgElement(category="leaves", key="horizontalOffset")
|
||||||
|
public OptionDouble leavesHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
|
||||||
|
|
||||||
|
@CfgElement(category="leaves", key="verticalOffset")
|
||||||
|
public OptionDouble leavesVOffset = new OptionDouble(0.0, 0.4, 0.025, 0.1);
|
||||||
|
|
||||||
|
@CfgElement(category="leaves", key="size")
|
||||||
|
public OptionDouble leavesSize = new OptionDouble(0.75, 1.8, 0.05, 1.4);
|
||||||
|
|
||||||
|
@CfgElement(category="grass", key="horizontalOffset")
|
||||||
|
public OptionDouble grassHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
|
||||||
|
|
||||||
|
@CfgElement(category="grass", key="heightMin")
|
||||||
|
@Limit(max="grassHeightMax")
|
||||||
|
public OptionDouble grassHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
|
||||||
|
|
||||||
|
@CfgElement(category="grass", key="heightMax")
|
||||||
|
public OptionDouble grassHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
|
||||||
|
|
||||||
|
@CfgElement(category="grass", key="size")
|
||||||
|
public OptionDouble grassSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
|
||||||
|
|
||||||
|
@CfgElement(category="lilypad", key="horizontalOffset")
|
||||||
|
public OptionDouble lilypadHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
|
||||||
|
|
||||||
|
@CfgElement(category="lilypad", key="chance")
|
||||||
|
public OptionInteger lilypadChance = new OptionInteger(0, 64, 1, 16);
|
||||||
|
|
||||||
|
@CfgElement(category="reed", key="horizontalOffset")
|
||||||
|
public OptionDouble reedHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
|
||||||
|
|
||||||
|
@CfgElement(category="reed", key="heightMin")
|
||||||
|
@Limit(max="reedHeightMax")
|
||||||
|
public OptionDouble reedHeightMin = new OptionDouble(1.5, 3.5, 0.1, 2.0);
|
||||||
|
|
||||||
|
@CfgElement(category="reed", key="heightMax")
|
||||||
|
public OptionDouble reedHeightMax = new OptionDouble(1.5, 3.5, 0.1, 2.5);
|
||||||
|
|
||||||
|
@CfgElement(category="reed", key="chance")
|
||||||
|
public OptionInteger reedChance = new OptionInteger(0, 64, 1, 32);
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="horizontalOffset")
|
||||||
|
public OptionDouble algaeHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="size")
|
||||||
|
public OptionDouble algaeSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="heightMin")
|
||||||
|
@Limit(max="algaeHeightMax")
|
||||||
|
public OptionDouble algaeHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="heightMax")
|
||||||
|
public OptionDouble algaeHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
|
||||||
|
|
||||||
|
@CfgElement(category="algae", key="chance")
|
||||||
|
public OptionInteger algaeChance = new OptionInteger(0, 64, 1, 48);
|
||||||
|
}
|
||||||
128
src/main/java/mods/betterfoliage/common/config/ConfigBase.java
Normal file
128
src/main/java/mods/betterfoliage/common/config/ConfigBase.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package mods.betterfoliage.common.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.config.Configuration;
|
||||||
|
import net.minecraftforge.common.config.Property;
|
||||||
|
|
||||||
|
public class ConfigBase {
|
||||||
|
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface CfgElement {
|
||||||
|
String category();
|
||||||
|
String key();
|
||||||
|
String comment() default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface Limit {
|
||||||
|
String min() default "";
|
||||||
|
String max() default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Configuration config;
|
||||||
|
|
||||||
|
public void load(File configFile) {
|
||||||
|
config = new Configuration(configFile);
|
||||||
|
config.load();
|
||||||
|
|
||||||
|
for (Field field : getClass().getDeclaredFields()) {
|
||||||
|
CfgElement annot = field.getAnnotation(CfgElement.class);
|
||||||
|
if (annot == null) continue;
|
||||||
|
|
||||||
|
field.setAccessible(true);
|
||||||
|
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
|
||||||
|
try {
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
|
||||||
|
field.setBoolean(this, prop.getBoolean(field.getBoolean(this)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
} else if (field.getType().equals(OptionInteger.class)) {
|
||||||
|
try {
|
||||||
|
OptionInteger option = (OptionInteger) field.get(this);
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), option.value);
|
||||||
|
option.value = prop.getInt(option.value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
} else if (field.getType().equals(OptionDouble.class)) {
|
||||||
|
try {
|
||||||
|
OptionDouble option = (OptionDouble) field.get(this);
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), option.value);
|
||||||
|
option.value = prop.getDouble(option.value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateLimits();
|
||||||
|
if (config.hasChanged()) config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateLimits() {
|
||||||
|
for (Field fieldThis : getClass().getDeclaredFields()) {
|
||||||
|
Limit annot = fieldThis.getAnnotation(Limit.class);
|
||||||
|
if (annot == null) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field fieldMin = annot.min().isEmpty() ? null : getClass().getDeclaredField(annot.min());
|
||||||
|
Field fieldMax = annot.max().isEmpty() ? null : getClass().getDeclaredField(annot.max());
|
||||||
|
fieldThis.setAccessible(true);
|
||||||
|
fieldMin.setAccessible(true);
|
||||||
|
fieldMax.setAccessible(true);
|
||||||
|
|
||||||
|
if (fieldThis.getType().equals(OptionInteger.class)) {
|
||||||
|
OptionInteger optionThis = (OptionInteger) fieldThis.get(this);
|
||||||
|
OptionInteger optionMin = fieldMin == null ? null : (OptionInteger) fieldMin.get(this);
|
||||||
|
OptionInteger optionMax = fieldMax == null ? null : (OptionInteger) fieldMax.get(this);
|
||||||
|
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
|
||||||
|
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
|
||||||
|
} else if (fieldThis.getType().equals(OptionDouble.class)) {
|
||||||
|
OptionDouble optionThis = (OptionDouble) fieldThis.get(this);
|
||||||
|
OptionDouble optionMin = fieldMin == null ? null : (OptionDouble) fieldMin.get(this);
|
||||||
|
OptionDouble optionMax = fieldMax == null ? null : (OptionDouble) fieldMax.get(this);
|
||||||
|
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
|
||||||
|
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
for (Field field : getClass().getDeclaredFields()) {
|
||||||
|
CfgElement annot = field.getAnnotation(CfgElement.class);
|
||||||
|
if (annot == null) continue;
|
||||||
|
|
||||||
|
field.setAccessible(true);
|
||||||
|
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
|
||||||
|
try {
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
|
||||||
|
prop.set(field.getBoolean(this));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
} else if (field.getType().equals(OptionInteger.class)) {
|
||||||
|
try {
|
||||||
|
OptionInteger option = (OptionInteger) field.get(this);
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), option.value);
|
||||||
|
prop.set(option.value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
} else if (field.getType().equals(OptionDouble.class)) {
|
||||||
|
try {
|
||||||
|
OptionDouble option = (OptionDouble) field.get(this);
|
||||||
|
Property prop = config.get(annot.category(), annot.key(), option.value);
|
||||||
|
prop.set(option.value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package mods.betterfoliage.common.config;
|
||||||
|
|
||||||
|
public class OptionDouble {
|
||||||
|
|
||||||
|
public double min;
|
||||||
|
public double max;
|
||||||
|
public double step;
|
||||||
|
public double value;
|
||||||
|
|
||||||
|
public OptionDouble(double min, double max, double step, double value) {
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
this.step = step;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increment() {
|
||||||
|
value += step;
|
||||||
|
if (value > max) value = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decrement() {
|
||||||
|
value -= step;
|
||||||
|
if (value < min) value = min;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package mods.betterfoliage.common.config;
|
||||||
|
|
||||||
|
public class OptionInteger {
|
||||||
|
|
||||||
|
public int min;
|
||||||
|
public int max;
|
||||||
|
public int step;
|
||||||
|
public int value;
|
||||||
|
|
||||||
|
public OptionInteger(int min, int max, int step, int value) {
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
this.step = step;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increment() {
|
||||||
|
value += step;
|
||||||
|
if (value > max) value = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decrement() {
|
||||||
|
value -= step;
|
||||||
|
if (value < min) value = min;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/main/java/mods/betterfoliage/common/util/DeobfNames.java
Normal file
71
src/main/java/mods/betterfoliage/common/util/DeobfNames.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
public class DeobfNames {
|
||||||
|
|
||||||
|
private DeobfNames() {}
|
||||||
|
|
||||||
|
/** MCP name of RenderBlocks */
|
||||||
|
public static final String RB_NAME_MCP = "net/minecraft/client/renderer/RenderBlocks";
|
||||||
|
|
||||||
|
/** Obfuscated name of RenderBlocks */
|
||||||
|
public static final String RB_NAME_OBF = "ble";
|
||||||
|
|
||||||
|
/** MCP name of RenderBlocks.blockAccess */
|
||||||
|
public static final String RB_BA_NAME_MCP = "blockAccess";
|
||||||
|
|
||||||
|
/** Obfuscated name of RenderBlocks.blockAccess */
|
||||||
|
public static final String RB_BA_NAME_OBF = "a";
|
||||||
|
|
||||||
|
/** MCP signature of RenderBlocks.blockAccess */
|
||||||
|
public static final String RB_BA_SIG_MCP = "Lnet/minecraft/world/IBlockAccess;";
|
||||||
|
|
||||||
|
/** Obfuscated signature of RenderBlocks.blockAccess */
|
||||||
|
public static final String RB_BA_SIG_OBF = "Lafx;";
|
||||||
|
|
||||||
|
/** MCP name of RenderBlocks.renderBlockByRenderType() */
|
||||||
|
public static final String RB_RBBRT_NAME_MCP = "renderBlockByRenderType";
|
||||||
|
|
||||||
|
/** Obfuscated name of RenderBlocks.renderBlockByRenderType() */
|
||||||
|
public static final String RB_RBBRT_NAME_OBF = "b";
|
||||||
|
|
||||||
|
/** MCP signature of RenderBlocks.renderBlockByRenderType() */
|
||||||
|
public static final String RB_RBBRT_SIG_MCP = "(Lnet/minecraft/block/Block;III)Z";
|
||||||
|
|
||||||
|
/** Obfuscated signature of RenderBlocks.renderBlockByRenderType() */
|
||||||
|
public static final String RB_RBBRT_SIG_OBF = "(Lahu;III)Z";
|
||||||
|
|
||||||
|
/** MCP signature of BetterFoliageClient.getRenderTypeOverride() */
|
||||||
|
public static final String BFC_GRTO_SIG_MCP = "(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I";
|
||||||
|
|
||||||
|
/** Obfuscated signature of BetterFoliageClient.getRenderTypeOverride() */
|
||||||
|
public static final String BFC_GRTO_SIG_OBF = "(Lafx;IIILahu;I)I";
|
||||||
|
|
||||||
|
/** MCP name of SimpleReloadableResourceManager.domainResourceManagers */
|
||||||
|
public static final String SRRM_DRM_MCP = "domainResourceManagers";
|
||||||
|
|
||||||
|
/** SRG name of SimpleReloadableResourceManager.domainResourceManagers */
|
||||||
|
public static final String SRRM_DRM_SRGNAME = "field_110548_a";
|
||||||
|
|
||||||
|
/** MCP name of TextureMap.mapRegisteredSprites */
|
||||||
|
public static final String TM_MRS_MCP = "mapRegisteredSprites";
|
||||||
|
|
||||||
|
/** Obfuscated name of TextureMap.mapRegisteredSprites */
|
||||||
|
public static final String TM_MRS_OBF = "bpr";
|
||||||
|
|
||||||
|
/** SRG name of TextureMap.mapRegisteredSprites */
|
||||||
|
public static final String TM_MRS_SRG = "field_110574_e";
|
||||||
|
|
||||||
|
/** MCP signature of Shaders.pushEntity() */
|
||||||
|
public static final String SHADERS_PE_SIG_MCP = "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
|
||||||
|
|
||||||
|
/** Obfuscated signature of Shaders.pushEntity() */
|
||||||
|
public static final String SHADERS_PE_SIG_OBF = "(Lble;Lahu;III)V";
|
||||||
|
|
||||||
|
/** MCP signature of BetterFoliageClient.getGLSLBlockIdOverride() */
|
||||||
|
public static final String BFC_GLSLID_SIG_MCP = "(ILnet/minecraft/block/Block;)I";
|
||||||
|
|
||||||
|
/** Obfuscated signature of BetterFoliageClient.getGLSLBlockIdOverride() */
|
||||||
|
public static final String BFC_GLSLID_SIG_OBF = "(ILahu;)I";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
42
src/main/java/mods/betterfoliage/common/util/Double3.java
Normal file
42
src/main/java/mods/betterfoliage/common/util/Double3.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/main/java/mods/betterfoliage/common/util/Utils.java
Normal file
70
src/main/java/mods/betterfoliage/common/util/Utils.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
private Utils() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Map<String, IResourceManager> getDomainResourceManagers() {
|
||||||
|
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
Map<String, IResourceManager> result = getField(manager, DeobfNames.SRRM_DRM_MCP, Map.class);
|
||||||
|
if (result == null) result = getField(manager, DeobfNames.SRRM_DRM_SRGNAME, Map.class);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getField(Object target, String fieldName, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
Field field = target.getClass().getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (T) field.get(target);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getStaticField(Class<?> clazz, String fieldName, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (T) field.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static ISimpleBlockRenderingHandler getRenderingHandler(int renderType) {
|
||||||
|
try {
|
||||||
|
Field field = RenderingRegistry.class.getDeclaredField("INSTANCE");
|
||||||
|
field.setAccessible(true);
|
||||||
|
RenderingRegistry inst = (RenderingRegistry) field.get(null);
|
||||||
|
field = RenderingRegistry.class.getDeclaredField("blockRenderers");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return ((Map<Integer, ISimpleBlockRenderingHandler>) field.get(inst)).get(renderType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean resourceExists(ResourceLocation resourceLocation) {
|
||||||
|
try {
|
||||||
|
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
|
||||||
|
if (resource != null) return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
|
@IFMLLoadingPlugin.MCVersion("1.7.2")
|
||||||
|
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
|
||||||
|
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
||||||
|
|
||||||
|
public String[] getASMTransformerClass() {
|
||||||
|
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModContainerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetupClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectData(Map<String, Object> data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessTransformerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.DeobfNames;
|
||||||
|
|
||||||
|
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 BetterFoliageTransformer extends EZTransformerBase {
|
||||||
|
|
||||||
|
@MethodTransform(className="net.minecraft.client.renderer.RenderBlocks",
|
||||||
|
obf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_OBF, signature=DeobfNames.RB_RBBRT_SIG_OBF),
|
||||||
|
deobf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_MCP, signature=DeobfNames.RB_RBBRT_SIG_MCP),
|
||||||
|
log="Applying RenderBlocks.renderBlockByRenderType() render type ovverride")
|
||||||
|
public void handleRenderBlockOverride(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
|
||||||
|
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
|
||||||
|
insertAfter(method.instructions, storeRenderType,
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
obf ? new FieldInsnNode(Opcodes.GETFIELD, DeobfNames.RB_NAME_OBF, DeobfNames.RB_BA_NAME_OBF, DeobfNames.RB_BA_SIG_OBF) :
|
||||||
|
new FieldInsnNode(Opcodes.GETFIELD, DeobfNames.RB_NAME_MCP, DeobfNames.RB_BA_NAME_MCP, DeobfNames.RB_BA_SIG_MCP),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 2),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 3),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 4),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 5),
|
||||||
|
obf ? new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", DeobfNames.BFC_GRTO_SIG_OBF) :
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", DeobfNames.BFC_GRTO_SIG_MCP),
|
||||||
|
new VarInsnNode(Opcodes.ISTORE, 5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MethodTransform(className="shadersmodcore.client.Shaders",
|
||||||
|
obf=@MethodMatch(name="pushEntity", signature=DeobfNames.SHADERS_PE_SIG_OBF),
|
||||||
|
deobf=@MethodMatch(name="pushEntity", signature=DeobfNames.SHADERS_PE_SIG_MCP),
|
||||||
|
log="Applying Shaders.pushEntity() block id ovverride")
|
||||||
|
public void handleGLSLBlockIDOverride(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
|
||||||
|
insertAfter(method.instructions, arrayStore.getPrevious(),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
obf ? new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getGLSLBlockIdOverride", DeobfNames.BFC_GLSLID_SIG_OBF) :
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getGLSLBlockIdOverride", DeobfNames.BFC_GLSLID_SIG_MCP)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/main/java/mods/betterfoliage/loader/EZTransformerBase.java
Normal file
131
src/main/java/mods/betterfoliage/loader/EZTransformerBase.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
public class EZTransformerBase implements IClassTransformer {
|
||||||
|
|
||||||
|
public static interface IInstructionMatch {
|
||||||
|
public boolean matches(AbstractInsnNode node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface MethodMatch {
|
||||||
|
public String name();
|
||||||
|
public String signature();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface MethodTransform {
|
||||||
|
public String className();
|
||||||
|
public MethodMatch deobf();
|
||||||
|
public MethodMatch obf();
|
||||||
|
public String log();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
|
||||||
|
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||||
|
// ???
|
||||||
|
if (basicClass == null) return null;
|
||||||
|
|
||||||
|
// read class
|
||||||
|
ClassNode classNode = new ClassNode();
|
||||||
|
ClassReader classReader = new ClassReader(basicClass);
|
||||||
|
classReader.accept(classNode, 0);
|
||||||
|
boolean hasTransformed = false;
|
||||||
|
|
||||||
|
for (Method classMethod : getClass().getMethods()) {
|
||||||
|
// check for annotated method with correct signature
|
||||||
|
MethodTransform annot = classMethod.getAnnotation(MethodTransform.class);
|
||||||
|
if (annot == null) continue;
|
||||||
|
if (classMethod.getParameterTypes().length != 2) continue;
|
||||||
|
if (!classMethod.getParameterTypes()[0].equals(MethodNode.class)) continue;
|
||||||
|
if (!classMethod.getParameterTypes()[1].equals(boolean.class)) continue;
|
||||||
|
|
||||||
|
// try to find specified method in class
|
||||||
|
if (!transformedName.equals(annot.className())) continue;
|
||||||
|
for (MethodNode methodNode : classNode.methods) {
|
||||||
|
Boolean obf = null;
|
||||||
|
if (methodNode.name.equals(annot.obf().name()) && methodNode.desc.equals(annot.obf().signature())) {
|
||||||
|
obf = true;
|
||||||
|
} else if (methodNode.name.equals(annot.deobf().name()) && methodNode.desc.equals(annot.deobf().signature())) {
|
||||||
|
obf = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obf != null) {
|
||||||
|
// transform
|
||||||
|
hasTransformed = true;
|
||||||
|
try {
|
||||||
|
classMethod.invoke(this, new Object[] {methodNode, obf});
|
||||||
|
logger.info(String.format("%s: SUCCESS", annot.log()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(String.format("%s: FAILURE", annot.log()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return result
|
||||||
|
ClassWriter writer = new ClassWriter(0);
|
||||||
|
if (hasTransformed) classNode.accept(writer);
|
||||||
|
return !hasTransformed ? basicClass : writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
|
||||||
|
AbstractInsnNode current = start;
|
||||||
|
while(current != null) {
|
||||||
|
if (match.matches(current)) break;
|
||||||
|
current = current.getNext();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
|
||||||
|
AbstractInsnNode current = start;
|
||||||
|
while(current != null) {
|
||||||
|
if (match.matches(current)) break;
|
||||||
|
current = current.getPrevious();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IInstructionMatch matchInvokeAny() {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node instanceof MethodInsnNode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IInstructionMatch matchOpcode(final int opcode) {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node.getOpcode() == opcode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
|
||||||
|
InsnList listAdd = new InsnList();
|
||||||
|
for (AbstractInsnNode inst : added) listAdd.add(inst);
|
||||||
|
insnList.insert(node, listAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package net.optifine.util;
|
|
||||||
|
|
||||||
public class BlockUtils {
|
|
||||||
// whyyyy?
|
|
||||||
}
|
|
||||||
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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)
|
|
||||||
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
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.resource.Identifier
|
|
||||||
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.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<Identifier, 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<Identifier, 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package mods.betterfoliage.client.integration
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.client.render.LogRegistry
|
|
||||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
|
||||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
|
||||||
import mods.betterfoliage.client.resource.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 org.apache.logging.log4j.Level
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package mods.betterfoliage.client.integration
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.client.config.BlockConfig
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import mods.betterfoliage.client.texture.GrassRegistry
|
|
||||||
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.client.renderer.BufferBuilder
|
|
||||||
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.pushNum, SVertexBuilder.pop)
|
|
||||||
|
|
||||||
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 sVertexBuilder = buffer[BufferBuilder_sVertexBuilder]
|
|
||||||
SVertexBuilder.pushState.invoke(sVertexBuilder, ctx.state, ctx.pos, ctx.world, buffer)
|
|
||||||
func()
|
|
||||||
SVertexBuilder.pop.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)
|
|
||||||
}
|
|
||||||
@@ -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) }
|
|
||||||
}
|
|
||||||
@@ -1,73 +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.resource.Identifier
|
|
||||||
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 org.apache.logging.log4j.Level.DEBUG
|
|
||||||
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(Identifier(BetterFoliageMod.MOD_ID, "soul_track"))
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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]] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +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.betterfoliage.client.resource.Identifier
|
|
||||||
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 org.apache.logging.log4j.Level.DEBUG
|
|
||||||
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(Identifier(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(Identifier(BetterFoliageMod.MOD_ID, "blocks/better_cactus"))
|
|
||||||
val iconArm = spriteSet { idx -> Identifier(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)] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +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.OptifineCustomColors
|
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
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.*
|
|
||||||
import org.apache.logging.log4j.Level.DEBUG
|
|
||||||
|
|
||||||
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 -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx") }
|
|
||||||
val snowedIcons = spriteSet { idx -> Identifier(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) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.resource.Identifier
|
|
||||||
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 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 -> Identifier(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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +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.betterfoliage.client.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
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 org.apache.logging.log4j.Level.DEBUG
|
|
||||||
|
|
||||||
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 -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_roots_$idx") }
|
|
||||||
val flowerIcon = spriteSet { idx -> Identifier(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]] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.betterfoliage.client.resource.Identifier
|
|
||||||
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 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(Identifier(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.BlockConfig
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
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 org.apache.logging.log4j.Level.DEBUG
|
|
||||||
|
|
||||||
class RenderMycelium : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
|
|
||||||
|
|
||||||
val myceliumIcon = spriteSet { idx -> Identifier(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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +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.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.octarinecore.client.render.*
|
|
||||||
import mods.octarinecore.client.render.lighting.*
|
|
||||||
import mods.octarinecore.random
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.util.Direction.Axis
|
|
||||||
import net.minecraft.util.Direction.*
|
|
||||||
import org.apache.logging.log4j.Level.DEBUG
|
|
||||||
|
|
||||||
class RenderNetherrack : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
|
|
||||||
|
|
||||||
val netherrackIcon = spriteSet { idx -> Identifier(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]] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +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.betterfoliage.client.resource.Identifier
|
|
||||||
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 org.apache.logging.log4j.Level.DEBUG
|
|
||||||
|
|
||||||
class RenderReeds : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
|
|
||||||
|
|
||||||
val noise = simplexNoise()
|
|
||||||
val reedIcons = spriteSetTransformed(
|
|
||||||
check = { idx -> Identifier(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] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
package mods.betterfoliage.client.render.column
|
|
||||||
|
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayLayer
|
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
|
||||||
import mods.betterfoliage.client.chunk.dimType
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
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.betterfoliage.client.render.rotationFromUp
|
|
||||||
import mods.octarinecore.client.render.BlockCtx
|
|
||||||
import mods.octarinecore.client.resource.ModelRenderRegistry
|
|
||||||
import mods.octarinecore.common.*
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.util.Direction.Axis
|
|
||||||
import net.minecraft.util.Direction.AxisDirection
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IEnviromentBlockReader
|
|
||||||
|
|
||||||
/** Index of SOUTH-EAST quadrant. */
|
|
||||||
const val SE = 0
|
|
||||||
/** Index of NORTH-EAST quadrant. */
|
|
||||||
const val NE = 1
|
|
||||||
/** Index of NORTH-WEST quadrant. */
|
|
||||||
const val NW = 2
|
|
||||||
/** Index of SOUTH-WEST quadrant. */
|
|
||||||
const val SW = 3
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sealed class hierarchy for all possible render outcomes
|
|
||||||
*/
|
|
||||||
sealed class ColumnLayerData {
|
|
||||||
/**
|
|
||||||
* Data structure to cache texture and world neighborhood data relevant to column rendering
|
|
||||||
*/
|
|
||||||
@Suppress("ArrayInDataClass") // not used in comparisons anywhere
|
|
||||||
data class SpecialRender(
|
|
||||||
val column: ColumnTextureInfo,
|
|
||||||
val upType: BlockType,
|
|
||||||
val downType: BlockType,
|
|
||||||
val quadrants: Array<QuadrantType>,
|
|
||||||
val quadrantsTop: Array<QuadrantType>,
|
|
||||||
val quadrantsBottom: Array<QuadrantType>
|
|
||||||
) : ColumnLayerData() {
|
|
||||||
enum class BlockType { SOLID, NONSOLID, PARALLEL, PERPENDICULAR }
|
|
||||||
enum class QuadrantType { SMALL_RADIUS, LARGE_RADIUS, SQUARE, INVISIBLE }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Column block should not be rendered at all */
|
|
||||||
object SkipRender : ColumnLayerData()
|
|
||||||
|
|
||||||
/** Column block must be rendered normally */
|
|
||||||
object NormalRender : ColumnLayerData()
|
|
||||||
|
|
||||||
/** Error while resolving render data, column block must be rendered normally */
|
|
||||||
object ResolveError : ColumnLayerData()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
|
||||||
|
|
||||||
abstract val registry: ModelRenderRegistry<ColumnTextureInfo>
|
|
||||||
abstract val blockPredicate: (BlockState)->Boolean
|
|
||||||
abstract val connectSolids: Boolean
|
|
||||||
abstract val lenientConnect: Boolean
|
|
||||||
abstract val defaultToY: Boolean
|
|
||||||
|
|
||||||
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
|
|
||||||
|
|
||||||
override fun onBlockUpdate(world: IEnviromentBlockReader, pos: BlockPos) {
|
|
||||||
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(world.dimType, this, pos + offset) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
|
||||||
if (allDirections.all { ctx.offset(it).isNormalCube }) return ColumnLayerData.SkipRender
|
|
||||||
val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
|
||||||
|
|
||||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
|
||||||
val logAxis = columnTextures.axis ?: if (defaultToY) Axis.Y else return ColumnLayerData.NormalRender
|
|
||||||
|
|
||||||
// check log neighborhood
|
|
||||||
val baseRotation = rotationFromUp[(logAxis to AxisDirection.POSITIVE).face.ordinal]
|
|
||||||
|
|
||||||
val upType = ctx.blockType(baseRotation, logAxis, Int3(0, 1, 0))
|
|
||||||
val downType = ctx.blockType(baseRotation, logAxis, Int3(0, -1, 0))
|
|
||||||
|
|
||||||
val quadrants = Array(4) { SMALL_RADIUS }.checkNeighbors(ctx, baseRotation, logAxis, 0)
|
|
||||||
val quadrantsTop = Array(4) { SMALL_RADIUS }
|
|
||||||
if (upType == PARALLEL) quadrantsTop.checkNeighbors(ctx, baseRotation, logAxis, 1)
|
|
||||||
val quadrantsBottom = Array(4) { SMALL_RADIUS }
|
|
||||||
if (downType == PARALLEL) quadrantsBottom.checkNeighbors(ctx, baseRotation, logAxis, -1)
|
|
||||||
return ColumnLayerData.SpecialRender(columnTextures, upType, downType, quadrants, quadrantsTop, quadrantsBottom)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the type of the given quadrant only if the new value is "stronger" (larger ordinal). */
|
|
||||||
inline fun Array<QuadrantType>.upgrade(idx: Int, value: QuadrantType) {
|
|
||||||
if (this[idx].ordinal < value.ordinal) this[idx] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fill the array of [QuadrantType]s based on the blocks to the sides of this one. */
|
|
||||||
fun Array<QuadrantType>.checkNeighbors(ctx: BlockCtx, rotation: Rotation, logAxis: Axis, yOff: Int): Array<QuadrantType> {
|
|
||||||
val blkS = ctx.blockType(rotation, logAxis, Int3(0, yOff, 1))
|
|
||||||
val blkE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 0))
|
|
||||||
val blkN = ctx.blockType(rotation, logAxis, Int3(0, yOff, -1))
|
|
||||||
val blkW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 0))
|
|
||||||
|
|
||||||
// a solid block on one side will make the 2 neighboring quadrants SQUARE
|
|
||||||
// if there are solid blocks to both sides of a quadrant, it is INVISIBLE
|
|
||||||
if (connectSolids) {
|
|
||||||
if (blkS == SOLID) {
|
|
||||||
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkE == SOLID) {
|
|
||||||
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkN == SOLID) {
|
|
||||||
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkW == SOLID) {
|
|
||||||
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkS == SOLID && blkE == SOLID) upgrade(SE, INVISIBLE)
|
|
||||||
if (blkN == SOLID && blkE == SOLID) upgrade(NE, INVISIBLE)
|
|
||||||
if (blkN == SOLID && blkW == SOLID) upgrade(NW, INVISIBLE)
|
|
||||||
if (blkS == SOLID && blkW == SOLID) upgrade(SW, INVISIBLE)
|
|
||||||
}
|
|
||||||
val blkSE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 1))
|
|
||||||
val blkNE = ctx.blockType(rotation, logAxis, Int3(1, yOff, -1))
|
|
||||||
val blkNW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, -1))
|
|
||||||
val blkSW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 1))
|
|
||||||
|
|
||||||
if (lenientConnect) {
|
|
||||||
// if the block forms the tip of an L-shape, connect to its neighbor with SQUARE quadrants
|
|
||||||
if (blkE == PARALLEL && (blkSE == PARALLEL || blkNE == PARALLEL)) {
|
|
||||||
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkN == PARALLEL && (blkNE == PARALLEL || blkNW == PARALLEL)) {
|
|
||||||
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkW == PARALLEL && (blkNW == PARALLEL || blkSW == PARALLEL)) {
|
|
||||||
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
|
|
||||||
}
|
|
||||||
if (blkS == PARALLEL && (blkSE == PARALLEL || blkSW == PARALLEL)) {
|
|
||||||
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the block forms the middle of an L-shape, or is part of a 2x2 configuration,
|
|
||||||
// connect to its neighbors with SQUARE quadrants, INVISIBLE on the inner corner, and LARGE_RADIUS on the outer corner
|
|
||||||
if (blkN == PARALLEL && blkW == PARALLEL && (lenientConnect || blkNW == PARALLEL)) {
|
|
||||||
upgrade(SE, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(NW, INVISIBLE)
|
|
||||||
}
|
|
||||||
if (blkS == PARALLEL && blkW == PARALLEL && (lenientConnect || blkSW == PARALLEL)) {
|
|
||||||
upgrade(NE, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(SW, INVISIBLE)
|
|
||||||
}
|
|
||||||
if (blkS == PARALLEL && blkE == PARALLEL && (lenientConnect || blkSE == PARALLEL)) {
|
|
||||||
upgrade(NW, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(SE, INVISIBLE)
|
|
||||||
}
|
|
||||||
if (blkN == PARALLEL && blkE == PARALLEL && (lenientConnect || blkNE == PARALLEL)) {
|
|
||||||
upgrade(SW, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(NE, INVISIBLE)
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the type of the block at the given offset in a rotated reference frame.
|
|
||||||
*/
|
|
||||||
fun BlockCtx.blockType(rotation: Rotation, axis: Axis, offset: Int3): ColumnLayerData.SpecialRender.BlockType {
|
|
||||||
val offsetRot = offset.rotate(rotation)
|
|
||||||
val state = state(offsetRot)
|
|
||||||
return if (!blockPredicate(state)) {
|
|
||||||
if (offset(offsetRot).isNormalCube) SOLID else NONSOLID
|
|
||||||
} else {
|
|
||||||
(registry[state, world, pos + offsetRot]?.axis ?: if (Config.roundLogs.defaultY) Axis.Y else null)?.let {
|
|
||||||
if (it == axis) PARALLEL else PERPENDICULAR
|
|
||||||
} ?: SOLID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package mods.betterfoliage.client.render.column
|
|
||||||
|
|
||||||
import mods.octarinecore.client.render.lighting.QuadIconResolver
|
|
||||||
import mods.octarinecore.common.rotate
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.Direction.*
|
|
||||||
|
|
||||||
interface ColumnTextureInfo {
|
|
||||||
val axis: Axis?
|
|
||||||
val top: QuadIconResolver
|
|
||||||
val bottom: QuadIconResolver
|
|
||||||
val side: QuadIconResolver
|
|
||||||
}
|
|
||||||
|
|
||||||
open class SimpleColumnInfo(
|
|
||||||
override val axis: Axis?,
|
|
||||||
val topTexture: TextureAtlasSprite,
|
|
||||||
val bottomTexture: TextureAtlasSprite,
|
|
||||||
val sideTextures: List<TextureAtlasSprite>
|
|
||||||
) : ColumnTextureInfo {
|
|
||||||
|
|
||||||
// index offsets for EnumFacings, to make it less likely for neighboring faces to get the same bark texture
|
|
||||||
val dirToIdx = arrayOf(0, 1, 2, 4, 3, 5)
|
|
||||||
|
|
||||||
override val top: QuadIconResolver = { _, _, _ -> topTexture }
|
|
||||||
override val bottom: QuadIconResolver = { _, _, _ -> bottomTexture }
|
|
||||||
override val side: QuadIconResolver = { ctx, idx, _ ->
|
|
||||||
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.modelRotation)
|
|
||||||
val sideIdx = if (sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0
|
|
||||||
sideTextures[sideIdx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package mods.betterfoliage.client.resource
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.model.ModelResourceLocation
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
|
|
||||||
typealias Identifier = ResourceLocation
|
|
||||||
typealias ModelIdentifier = ModelResourceLocation
|
|
||||||
|
|
||||||
typealias Sprite = TextureAtlasSprite
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.octarinecore.client.resource.*
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate Short Grass textures from [Blocks.tallgrass] block textures.
|
|
||||||
* The bottom 3/8 of the base texture is chopped off.
|
|
||||||
*
|
|
||||||
* @param[domain] Resource domain of generator
|
|
||||||
*/
|
|
||||||
data class GeneratedGrass(val sprite: Identifier, val isSnowed: Boolean, val atlas: Atlas = Atlas.BLOCKS) {
|
|
||||||
constructor(sprite: String, isSnowed: Boolean) : this(Identifier(sprite), isSnowed)
|
|
||||||
|
|
||||||
fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw)
|
|
||||||
|
|
||||||
fun draw(resourceManager: IResourceManager): ByteArray {
|
|
||||||
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
|
|
||||||
|
|
||||||
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
|
|
||||||
val graphics = result.createGraphics()
|
|
||||||
|
|
||||||
val size = baseTexture.width
|
|
||||||
val frames = baseTexture.height / size
|
|
||||||
|
|
||||||
// iterate all frames
|
|
||||||
for (frame in 0 until frames) {
|
|
||||||
val baseFrame = baseTexture.getSubimage(0, size * frame, size, size)
|
|
||||||
val grassFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR)
|
|
||||||
|
|
||||||
// draw bottom half of texture
|
|
||||||
grassFrame.createGraphics().apply {
|
|
||||||
drawImage(baseFrame, 0, 3 * size / 8, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to animated png
|
|
||||||
graphics.drawImage(grassFrame, 0, size * frame, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blend with white if snowed
|
|
||||||
if (isSnowed) {
|
|
||||||
for (x in 0..result.width - 1) for (y in 0..result.height - 1) {
|
|
||||||
result[x, y] = blendRGB(result[x, y], 16777215, 2, 3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.octarinecore.client.resource.*
|
|
||||||
import net.minecraft.resources.IResource
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate round leaf textures from leaf block textures.
|
|
||||||
* The base texture is tiled 2x2, then parts of it are made transparent by applying a mask to the alpha channel.
|
|
||||||
*
|
|
||||||
* Different leaf types may have their own alpha mask.
|
|
||||||
*
|
|
||||||
* @param[domain] Resource domain of generator
|
|
||||||
*/
|
|
||||||
data class GeneratedLeaf(val sprite: ResourceLocation, val leafType: String, val atlas: Atlas = Atlas.BLOCKS) {
|
|
||||||
|
|
||||||
fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw)
|
|
||||||
|
|
||||||
fun draw(resourceManager: IResourceManager): ByteArray {
|
|
||||||
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
|
|
||||||
|
|
||||||
val size = baseTexture.width
|
|
||||||
val frames = baseTexture.height / size
|
|
||||||
|
|
||||||
val maskTexture = (getLeafMask(leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage()
|
|
||||||
fun scale(i: Int) = i * maskTexture!!.width / (size * 2)
|
|
||||||
|
|
||||||
val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR)
|
|
||||||
val graphics = leafTexture.createGraphics()
|
|
||||||
|
|
||||||
// iterate all frames
|
|
||||||
for (frame in 0 until frames) {
|
|
||||||
val baseFrame = baseTexture.getSubimage(0, size * frame, size, size)
|
|
||||||
val leafFrame = BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR)
|
|
||||||
|
|
||||||
// tile leaf texture 2x2
|
|
||||||
leafFrame.createGraphics().apply {
|
|
||||||
drawImage(baseFrame, 0, 0, null)
|
|
||||||
drawImage(baseFrame, 0, size, null)
|
|
||||||
drawImage(baseFrame, size, 0, null)
|
|
||||||
drawImage(baseFrame, size, size, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overlay alpha mask
|
|
||||||
if (maskTexture != null) {
|
|
||||||
for (x in 0 until size * 2) for (y in 0 until size * 2) {
|
|
||||||
val basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL
|
|
||||||
val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL
|
|
||||||
leafFrame[x, y] = (basePixel and maskPixel).toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to animated png
|
|
||||||
graphics.drawImage(leafFrame, 0, size * frame * 2, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
return leafTexture.bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the alpha mask to use
|
|
||||||
*
|
|
||||||
* @param[type] Alpha mask type.
|
|
||||||
* @param[maxSize] Preferred mask size.
|
|
||||||
*/
|
|
||||||
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "textures/blocks/leafmask_${size}_${type}.png")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a texture resource when multiple sizes may exist.
|
|
||||||
*
|
|
||||||
* @param[maxSize] Maximum size to consider. This value is progressively halved when searching for smaller versions.
|
|
||||||
* @param[maskPath] Location of the texture of the given size
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
fun getMultisizeTexture(maxSize: Int, maskPath: (Int)->ResourceLocation): IResource? {
|
|
||||||
var size = maxSize
|
|
||||||
val sizes = mutableListOf<Int>()
|
|
||||||
while(size > 2) { sizes.add(size); size /= 2 }
|
|
||||||
return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.client.config.BlockConfig
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.octarinecore.client.render.lighting.HSB
|
|
||||||
import mods.octarinecore.client.resource.*
|
|
||||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import java.lang.Math.min
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
const val defaultGrassColor = 0
|
|
||||||
|
|
||||||
/** Rendering-related information for a grass block. */
|
|
||||||
class GrassInfo(
|
|
||||||
/** Top texture of the grass block. */
|
|
||||||
val grassTopTexture: TextureAtlasSprite,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Color to use for Short Grass rendering instead of the biome color.
|
|
||||||
*
|
|
||||||
* Value is null if the texture is mostly grey (the saturation of its average color is under a configurable limit),
|
|
||||||
* the average color of the texture otherwise.
|
|
||||||
*/
|
|
||||||
val overrideColor: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>()
|
|
||||||
|
|
||||||
object AsyncGrassDiscovery : ConfigurableModelDiscovery<GrassInfo>() {
|
|
||||||
override val logger = BetterFoliage.logDetail
|
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
|
||||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
|
|
||||||
|
|
||||||
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<GrassInfo> {
|
|
||||||
val textureName = textures[0]
|
|
||||||
val spriteF = atlas.sprite(Identifier(textureName))
|
|
||||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
|
||||||
return atlas.mapAfter {
|
|
||||||
val sprite = spriteF.get()
|
|
||||||
logger.log(Level.DEBUG, "$logName: block state $state")
|
|
||||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
|
||||||
val hsb = HSB.fromColor(sprite.averageColor)
|
|
||||||
val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
|
|
||||||
logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}")
|
|
||||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color")
|
|
||||||
hsb.copy(brightness = min(0.9f, hsb.brightness * 2.0f)).asColor
|
|
||||||
} else {
|
|
||||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
GrassInfo(sprite, overrideColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
GrassRegistry.registries.add(this)
|
|
||||||
BetterFoliage.blockSprites.providers.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.betterfoliage.client.resource.Sprite
|
|
||||||
import mods.octarinecore.client.resource.*
|
|
||||||
import mods.octarinecore.stripStart
|
|
||||||
import mods.octarinecore.client.resource.Atlas
|
|
||||||
import mods.octarinecore.common.sinkAsync
|
|
||||||
import net.minecraft.client.particle.ParticleManager
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
class FixedSpriteSet(val sprites: List<Sprite>) : SpriteSet {
|
|
||||||
override val num = sprites.size
|
|
||||||
override fun get(idx: Int) = sprites[idx % num]
|
|
||||||
}
|
|
||||||
|
|
||||||
object LeafParticleRegistry : AsyncSpriteProvider<ParticleManager> {
|
|
||||||
val targetAtlas = Atlas.PARTICLES
|
|
||||||
val typeMappings = TextureMatcher()
|
|
||||||
val particles = hashMapOf<String, SpriteSet>()
|
|
||||||
|
|
||||||
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
|
||||||
|
|
||||||
override fun setup(manager: IResourceManager, particleF: CompletableFuture<ParticleManager>, atlasFuture: AtlasFuture): StitchPhases {
|
|
||||||
particles.clear()
|
|
||||||
val futures = mutableMapOf<String, List<CompletableFuture<Sprite>>>()
|
|
||||||
|
|
||||||
return StitchPhases(
|
|
||||||
discovery = particleF.sinkAsync {
|
|
||||||
typeMappings.loadMappings(Identifier(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
|
||||||
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
|
||||||
val ids = (0 until 16).map { idx -> Identifier(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") }
|
|
||||||
val wids = ids.map { Atlas.PARTICLES.wrap(it) }
|
|
||||||
futures[leafType] = (0 until 16).map { idx -> Identifier(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") }
|
|
||||||
.filter { manager.hasResource(Atlas.PARTICLES.wrap(it)) }
|
|
||||||
.map { atlasFuture.sprite(it) }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cleanup = atlasFuture.runAfter {
|
|
||||||
futures.forEach { leafType, spriteFutures ->
|
|
||||||
val sprites = spriteFutures.filter { !it.isCompletedExceptionally }.map { it.get() }
|
|
||||||
if (sprites.isNotEmpty()) particles[leafType] = FixedSpriteSet(sprites)
|
|
||||||
}
|
|
||||||
if (particles["default"] == null) particles["default"] = FixedSpriteSet(listOf(atlasFuture.missing.get()!!))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
BetterFoliage.particleSprites.providers.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextureMatcher {
|
|
||||||
|
|
||||||
data class Mapping(val domain: String?, val path: String, val type: String) {
|
|
||||||
fun matches(iconLocation: Identifier): Boolean {
|
|
||||||
return (domain == null || domain == iconLocation.namespace) &&
|
|
||||||
iconLocation.path.stripStart("blocks/").contains(path, ignoreCase = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mappings: MutableList<Mapping> = mutableListOf()
|
|
||||||
|
|
||||||
fun getType(resource: Identifier) = mappings.filter { it.matches(resource) }.map { it.type }.firstOrNull()
|
|
||||||
fun getType(iconName: String) = Identifier(iconName).let { getType(it) }
|
|
||||||
|
|
||||||
fun loadMappings(mappingLocation: Identifier) {
|
|
||||||
mappings.clear()
|
|
||||||
resourceManager[mappingLocation]?.getLines()?.let { lines ->
|
|
||||||
lines.filter { !it.startsWith("//") }.filter { !it.isEmpty() }.forEach { line ->
|
|
||||||
val line2 = line.trim().split('=')
|
|
||||||
if (line2.size == 2) {
|
|
||||||
val mapping = line2[0].trim().split(':')
|
|
||||||
if (mapping.size == 1) mappings.add(Mapping(null, mapping[0].trim(), line2[1].trim()))
|
|
||||||
else if (mapping.size == 2) mappings.add(Mapping(mapping[0].trim(), mapping[1].trim(), line2[1].trim()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.client.config.BlockConfig
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.octarinecore.HasLogger
|
|
||||||
import mods.octarinecore.client.resource.*
|
|
||||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
const val defaultLeafColor = 0
|
|
||||||
|
|
||||||
/** Rendering-related information for a leaf block. */
|
|
||||||
class LeafInfo(
|
|
||||||
/** The generated round leaf texture. */
|
|
||||||
val roundLeafTexture: TextureAtlasSprite,
|
|
||||||
|
|
||||||
/** Type of the leaf block (configurable by user). */
|
|
||||||
val leafType: String,
|
|
||||||
|
|
||||||
/** Average color of the round leaf texture. */
|
|
||||||
val averageColor: Int = roundLeafTexture.averageColor
|
|
||||||
) {
|
|
||||||
/** [IconSet] of the textures to use for leaf particles emitted from this block. */
|
|
||||||
val particleTextures: SpriteSet get() = LeafParticleRegistry[leafType]
|
|
||||||
}
|
|
||||||
|
|
||||||
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
|
|
||||||
|
|
||||||
object AsyncLeafDiscovery : ConfigurableModelDiscovery<LeafInfo>() {
|
|
||||||
override val logger = BetterFoliage.logDetail
|
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
|
||||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
|
|
||||||
|
|
||||||
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture) = defaultRegisterLeaf(Identifier(textures[0]), atlas)
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
LeafRegistry.registries.add(this)
|
|
||||||
BetterFoliage.blockSprites.providers.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture<LeafInfo> {
|
|
||||||
val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default"
|
|
||||||
val generated = GeneratedLeaf(sprite, leafType).register(BetterFoliage.asyncPack)
|
|
||||||
val roundLeaf = atlas.sprite(generated)
|
|
||||||
|
|
||||||
log(" leaf texture $sprite")
|
|
||||||
log(" particle $leafType")
|
|
||||||
return atlas.mapAfter {
|
|
||||||
LeafInfo(roundLeaf.get(), leafType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
@file:JvmName("Utils")
|
|
||||||
package mods.betterfoliage.client.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.client.resource.Identifier
|
|
||||||
import mods.octarinecore.client.resource.Atlas
|
|
||||||
import mods.octarinecore.client.resource.get
|
|
||||||
import mods.octarinecore.client.resource.loadImage
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
|
||||||
val r = (((rgb1 shr 16) and 255) * weight1 + ((rgb2 shr 16) and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val g = (((rgb1 shr 8) and 255) * weight1 + ((rgb2 shr 8) and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val b = ((rgb1 and 255) * weight1 + (rgb2 and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val a = (rgb1 shr 24) and 255
|
|
||||||
val result = ((a shl 24) or (r shl 16) or (g shl 8) or b).toInt()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun IResourceManager.loadSprite(id: Identifier) = this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id")
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package mods.octarinecore
|
|
||||||
|
|
||||||
import mods.octarinecore.metaprog.ClassRef
|
|
||||||
import mods.octarinecore.metaprog.ClassRef.Companion.void
|
|
||||||
import mods.octarinecore.metaprog.FieldRef
|
|
||||||
import mods.octarinecore.metaprog.MethodRef
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
|
||||||
import net.minecraft.client.renderer.model.BakedQuad
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.BlockRenderLayer
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockReader
|
|
||||||
import net.minecraft.world.IEnviromentBlockReader
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
// Java
|
|
||||||
val String = ClassRef<String>("java.lang.String")
|
|
||||||
val Map = ClassRef<Map<*, *>>("java.util.Map")
|
|
||||||
val List = ClassRef<List<*>>("java.util.List")
|
|
||||||
val Random = ClassRef<Random>("java.util.Random")
|
|
||||||
|
|
||||||
// Minecraft
|
|
||||||
val IBlockReader = ClassRef<IBlockReader>("net.minecraft.world.IBlockReader")
|
|
||||||
val IEnvironmentBlockReader = ClassRef<IEnviromentBlockReader>("net.minecraft.world.IEnviromentBlockReader")
|
|
||||||
val BlockState = ClassRef<BlockState>("net.minecraft.block.BlockState")
|
|
||||||
val BlockPos = ClassRef<BlockPos>("net.minecraft.util.math.BlockPos")
|
|
||||||
val BlockRenderLayer = ClassRef<BlockRenderLayer>("net.minecraft.util.BlockRenderLayer")
|
|
||||||
val Block = ClassRef<Block>("net.minecraft.block.Block")
|
|
||||||
|
|
||||||
val TextureAtlasSprite = ClassRef<TextureAtlasSprite>("net.minecraft.client.renderer.texture.TextureAtlasSprite")
|
|
||||||
val BufferBuilder = ClassRef<BufferBuilder>("net.minecraft.client.renderer.BufferBuilder")
|
|
||||||
val BufferBuilder_setSprite = MethodRef(BufferBuilder, "setSprite", void, TextureAtlasSprite)
|
|
||||||
val BufferBuilder_sVertexBuilder = FieldRef(BufferBuilder, "sVertexBuilder", SVertexBuilder)
|
|
||||||
val BlockRendererDispatcher = ClassRef<BlockRendererDispatcher>("net.minecraft.client.renderer.BlockRendererDispatcher")
|
|
||||||
val ChunkRenderCache = ClassRef<ChunkRenderCache>("net.minecraft.client.renderer.chunk.ChunkRenderCache")
|
|
||||||
val ResourceLocation = ClassRef<ResourceLocation>("net.minecraft.util.ResourceLocation")
|
|
||||||
val BakedQuad = ClassRef<BakedQuad>("net.minecraft.client.renderer.model.BakedQuad")
|
|
||||||
|
|
||||||
// Optifine
|
|
||||||
val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer")
|
|
||||||
val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM")
|
|
||||||
object ChunkCacheOF : ClassRef<Any>("net.optifine.override.ChunkCacheOF") {
|
|
||||||
val chunkCache = FieldRef(this, "chunkCache", ChunkRenderCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
object RenderEnv : ClassRef<Any>("net.optifine.render.RenderEnv") {
|
|
||||||
val reset = MethodRef(this, "reset", void, BlockState, BlockPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optifine custom colors
|
|
||||||
val IColorizer = ClassRef<Any>("net.optifine.CustomColors\$IColorizer")
|
|
||||||
object CustomColors : ClassRef<Any>("net.optifine.CustomColors") {
|
|
||||||
val getColorMultiplier = MethodRef(this, "getColorMultiplier", int, BakedQuad, BlockState, IEnvironmentBlockReader, BlockPos, RenderEnv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optifine shaders
|
|
||||||
object SVertexBuilder : ClassRef<Any>("net.optifine.shaders.SVertexBuilder") {
|
|
||||||
val pushState = MethodRef(this, "pushEntity", void, BlockState, BlockPos, IEnvironmentBlockReader, BufferBuilder)
|
|
||||||
val pushNum = MethodRef(this, "pushEntity", void, long)
|
|
||||||
val pop = MethodRef(this, "popEntity", void)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user