Compare commits
58 Commits
1.16.5-For
...
0.9.9b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d9d32b32a | ||
|
|
6623bee39f | ||
|
|
ffa8dd724e | ||
|
|
bd4a4885fe | ||
|
|
9d59105af1 | ||
|
|
7cdddef1bf | ||
|
|
168fad883d | ||
|
|
572d517828 | ||
|
|
d4963ec173 | ||
|
|
ad83a511fb | ||
|
|
e351af2369 | ||
|
|
32a88fa824 | ||
|
|
ec723113d3 | ||
|
|
4e42f63c36 | ||
|
|
0daf61583a | ||
|
|
504f033c2e | ||
|
|
fa099b1b97 | ||
|
|
b0c0cd0d1b | ||
|
|
df69605521 | ||
|
|
d3b1d138ba | ||
|
|
07369159b8 | ||
|
|
6700e724a5 | ||
|
|
5b34da3e61 | ||
|
|
fc7c9a5381 | ||
|
|
cf7ae0efa1 | ||
|
|
04bb240d36 | ||
|
|
91fda1522c | ||
|
|
7ca25c0da7 | ||
|
|
0a05a67eda | ||
|
|
43e7fb830b | ||
|
|
c72e93aedf | ||
|
|
f236ef64ad | ||
|
|
d38f556bde | ||
|
|
fad662443d | ||
|
|
32c4dc6035 | ||
|
|
aee6e5caca | ||
|
|
5fbe2ff16f | ||
|
|
7a133c95a7 | ||
|
|
25b1d76c9e | ||
|
|
7a02179481 | ||
|
|
244907f4cd | ||
|
|
4ccd753c0c | ||
|
|
2715acf9c8 | ||
|
|
1c146fb070 | ||
|
|
44a20ceab3 | ||
|
|
1be2382fed | ||
|
|
e12b7b803c | ||
|
|
8a94867cd8 | ||
|
|
3a391f1677 | ||
|
|
d5dd1a36e3 | ||
|
|
a589c868a9 | ||
|
|
37ffa219fc | ||
|
|
220f2356d8 | ||
|
|
ec184f9916 | ||
|
|
2b1fd84cd5 | ||
|
|
30f199e9a2 | ||
|
|
0c66849175 | ||
|
|
3bd402b964 |
8
.classpath
Normal file
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>
|
||||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,11 +1,5 @@
|
|||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
run/
|
|
||||||
.gradle/
|
.gradle/
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
build/
|
build/
|
||||||
classes/
|
libs/
|
||||||
temp/
|
|
||||||
logs
|
|
||||||
src/main/javacc/mods
|
|
||||||
|
|||||||
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,9 @@
|
|||||||
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.7-beta] (http://goo.gl/xNVloR) (MC 1.7.2 & 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'
|
||||||
|
group = 'com.github.octarine-noise'
|
||||||
|
version='0.9.9b'
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
inputs.property "version", project.version
|
||||||
|
inputs.property "mcversion", project.minecraft.version
|
||||||
|
|
||||||
|
from(sourceSets.main.resources.srcDirs) {
|
||||||
|
include 'mcmod.info'
|
||||||
|
expand 'version':project.version, 'mcversion':project.minecraft.version
|
||||||
|
}
|
||||||
|
|
||||||
|
from(sourceSets.main.resources.srcDirs) {
|
||||||
|
exclude 'mcmod.info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm").version("1.4.20")
|
|
||||||
id("net.minecraftforge.gradle").version("4.1.12")
|
|
||||||
id("org.spongepowered.mixin").version("0.7-SNAPSHOT")
|
|
||||||
id("com.intershop.gradle.javacc").version("4.0.0")
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven("https://files.minecraftforge.net/maven")
|
|
||||||
maven("https://repo.spongepowered.org/maven")
|
|
||||||
maven("https://www.cursemaven.com")
|
|
||||||
maven("https://thedarkcolour.github.io/KotlinForForge/")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
"minecraft"("net.minecraftforge:forge:${properties["mcVersion"]}-${properties["forgeVersion"]}")
|
|
||||||
"implementation"("thedarkcolour:kotlinforforge:1.7.0")
|
|
||||||
"api"(fg.deobf("curse.maven:clothconfig-348521:3311352"))
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations["annotationProcessor"].extendsFrom(configurations["implementation"])
|
|
||||||
sourceSets {
|
|
||||||
get("main").ext["refMap"] = "betterfoliage.refmap.json"
|
|
||||||
get("main").java.srcDir("src/main/javacc/")
|
|
||||||
}
|
|
||||||
|
|
||||||
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"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
javacc {
|
|
||||||
configs {
|
|
||||||
create("blockconfig") {
|
|
||||||
staticParam = "false"
|
|
||||||
inputFile = file("src/main/javacc/BlockConfig.jj")
|
|
||||||
outputDir = file("src/main/javacc/")
|
|
||||||
packageName = "mods.betterfoliage.config.match.parser"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
target.compilations.configureEach {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.getByName<Jar>("jar") {
|
|
||||||
archiveName = "BetterFoliage-${project.version}-Forge-${properties["mcVersion"]}.jar"
|
|
||||||
manifest {
|
|
||||||
from(file("src/main/resources/META-INF/MANIFEST.MF"))
|
|
||||||
attributes["Implementation-Version"] = project.version
|
|
||||||
}
|
|
||||||
exclude("net")
|
|
||||||
filesMatching("META-INF/mods.toml") { expand(project.properties) }
|
|
||||||
filesMatching("mcmod.info") { expand(project.properties) }
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx2G
|
|
||||||
org.gradle.daemon=false
|
|
||||||
|
|
||||||
group = com.github.octarine-noise
|
|
||||||
jarName = BetterFoliage-Forge
|
|
||||||
|
|
||||||
version = 2.7.0
|
|
||||||
|
|
||||||
mcVersion = 1.16.5
|
|
||||||
forgeVersion = 36.1.17
|
|
||||||
mappingsChannel = official
|
|
||||||
mappingsVersion = 1.16.5
|
|
||||||
|
|
||||||
#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-6.8.1-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
185
gradlew
vendored
185
gradlew
vendored
@@ -1,185 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright 2015 the original author or authors.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
##
|
|
||||||
## 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='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# 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 or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
|
||||||
89
gradlew.bat
vendored
89
gradlew.bat
vendored
@@ -1,89 +0,0 @@
|
|||||||
@rem
|
|
||||||
@rem Copyright 2015 the original author or authors.
|
|
||||||
@rem
|
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
@rem you may not use this file except in compliance with the License.
|
|
||||||
@rem You may obtain a copy of the License at
|
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@rem
|
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
@rem See the License for the specific language governing permissions and
|
|
||||||
@rem limitations under the License.
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@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 Resolve any "." and ".." in APP_HOME to make it shorter.
|
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
|
||||||
|
|
||||||
@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="-Xmx64m" "-Xms64m"
|
|
||||||
|
|
||||||
@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 execute
|
|
||||||
|
|
||||||
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 execute
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
: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 %*
|
|
||||||
|
|
||||||
: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("https://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,1.7.10]";
|
||||||
|
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
|
||||||
|
|
||||||
|
@Mod.Instance
|
||||||
|
public static BetterFoliage instance;
|
||||||
|
|
||||||
|
public static BetterFoliageConfig config = new BetterFoliageConfig();
|
||||||
|
|
||||||
|
public static Logger log;
|
||||||
|
|
||||||
|
public static File configDir;
|
||||||
|
|
||||||
|
@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,18 +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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
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.RenderBlockBetterCoral;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterGrass;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterReed;
|
||||||
|
import mods.betterfoliage.client.resource.LeafGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.LeafTextureEnumerator;
|
||||||
|
import mods.betterfoliage.client.resource.ReedGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.ShortGrassGenerator;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
public static Map<Integer, IRenderBlockDecorator> decorators = Maps.newHashMap();
|
||||||
|
public static LeafGenerator leafGenerator;
|
||||||
|
|
||||||
|
public static BlockMatcher leaves = new BlockMatcher();
|
||||||
|
public static BlockMatcher crops = new BlockMatcher();
|
||||||
|
|
||||||
|
public static ResourceLocation missingTexture = new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png");
|
||||||
|
|
||||||
|
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());
|
||||||
|
registerRenderer(new RenderBlockBetterCoral());
|
||||||
|
|
||||||
|
leaves.load(new File(BetterFoliage.configDir, "classesLeaves.cfg"), new ResourceLocation("betterfoliage:classesLeavesDefault.cfg"));
|
||||||
|
MinecraftForge.EVENT_BUS.register(leaves);
|
||||||
|
|
||||||
|
crops.load(new File(BetterFoliage.configDir, "classesCrops.cfg"), new ResourceLocation("betterfoliage:classesCropsDefault.cfg"));
|
||||||
|
MinecraftForge.EVENT_BUS.register(crops);
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator("bf_leaves"));
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Registering texture generators");
|
||||||
|
leafGenerator = new LeafGenerator("bf_leaves", missingTexture);
|
||||||
|
MinecraftForge.EVENT_BUS.register(leafGenerator);
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_top", missingTexture, false));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass", missingTexture, false));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass_snow", missingTexture, true));
|
||||||
|
|
||||||
|
ShadersModIntegration.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeafTexture(TextureAtlasSprite icon) {
|
||||||
|
String resourceLocation = icon.getIconName();
|
||||||
|
if (resourceLocation.startsWith("forestry:leaves/")) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRenderTypeOverride(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
// universal sign for DON'T RENDER ME!
|
||||||
|
if (original == -1) return original;
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, IRenderBlockDecorator> entry : decorators.entrySet())
|
||||||
|
if (entry.getValue().isBlockAccepted(blockAccess, x, y, z, block, original))
|
||||||
|
return entry.getKey();
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerRenderer(IRenderBlockDecorator decorator) {
|
||||||
|
int renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||||
|
decorators.put(renderId, decorator);
|
||||||
|
RenderingRegistry.registerBlockHandler(renderId, decorator);
|
||||||
|
MinecraftForge.EVENT_BUS.register(decorator);
|
||||||
|
decorator.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
83
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
83
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.multiplayer.WorldClient;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class BlockMatcher {
|
||||||
|
|
||||||
|
public Set<Class<?>> whiteList = Sets.newHashSet();
|
||||||
|
public Set<Class<?>> blackList = Sets.newHashSet();
|
||||||
|
public Set<Integer> blockIDs = Sets.newHashSet();
|
||||||
|
|
||||||
|
public void addClass(String className) {
|
||||||
|
try {
|
||||||
|
if (className.startsWith("-"))
|
||||||
|
blackList.add(Class.forName(className.substring(1)));
|
||||||
|
else
|
||||||
|
whiteList.add(Class.forName(className));
|
||||||
|
} catch(ClassNotFoundException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesClass(Block block) {
|
||||||
|
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
|
||||||
|
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesID(int blockId) {
|
||||||
|
return blockIDs.contains(blockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesID(Block block) {
|
||||||
|
return blockIDs.contains(Block.blockRegistry.getIDForObject(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(File file, ResourceLocation defaults) {
|
||||||
|
if (!file.exists()) Utils.copyFromTextResource(defaults, 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 (Exception e) {
|
||||||
|
BetterFoliage.log.warn(String.format("Error reading configuration: %s", file.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caches block IDs on world load for fast lookup
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
if (!(event.world instanceof WorldClient)) return;
|
||||||
|
|
||||||
|
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,60 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
public class ShadersModIntegration {
|
||||||
|
|
||||||
|
private static boolean hasShadersMod = false;
|
||||||
|
private static int tallGrassEntityData;
|
||||||
|
private static int leavesEntityData;
|
||||||
|
private static Field shadersEntityData;
|
||||||
|
private static Field shadersEntityDataIndex;
|
||||||
|
|
||||||
|
private ShadersModIntegration() {}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
tallGrassEntityData = Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | Blocks.tallgrass.getRenderType() << 16;
|
||||||
|
leavesEntityData = Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | Blocks.leaves.getRenderType() << 16;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> classShaders = Class.forName("shadersmodcore.client.Shaders");
|
||||||
|
shadersEntityData = classShaders.getDeclaredField("entityData");
|
||||||
|
shadersEntityDataIndex = classShaders.getDeclaredField("entityDataIndex");
|
||||||
|
hasShadersMod = true;
|
||||||
|
} catch(Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startGrassQuads() {
|
||||||
|
if (!hasShadersMod) return;
|
||||||
|
setShadersEntityData(tallGrassEntityData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startLeavesQuads() {
|
||||||
|
if (!hasShadersMod) return;
|
||||||
|
setShadersEntityData(leavesEntityData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setShadersEntityData(int data) {
|
||||||
|
try {
|
||||||
|
int[] entityData = (int[]) shadersEntityData.get(null);
|
||||||
|
int entityDataIndex = shadersEntityDataIndex.getInt(null);
|
||||||
|
entityData[(entityDataIndex * 2)] = data;
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getBlockIdOverride(int original, Block block) {
|
||||||
|
if (BetterFoliageClient.leaves.matchesID(original & 0xFFFF)) return leavesEntityData;
|
||||||
|
if (BetterFoliageClient.crops.matchesID(original & 0xFFFF)) return tallGrassEntityData;
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSpecialTexture(ResourceLocation resource) {
|
||||||
|
return resource.getResourcePath().toLowerCase().endsWith("_n.png") || resource.getResourcePath().toLowerCase().endsWith("_s.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,34 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class ConfigGuiCoral extends ConfigGuiScreenBase {
|
||||||
|
|
||||||
|
public ConfigGuiCoral(GuiScreen parent) {
|
||||||
|
super(parent);
|
||||||
|
int id = 10;
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralCrustSize, -100, -100, 200, 50, id++, id++, "message.betterfoliage.crustSize", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralVOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralSize, -100, -40, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionIntegerWidget(BetterFoliage.config.coralPopulation, -100, 20, 200, 50, id++, id++, "message.betterfoliage.coralPopulation"));
|
||||||
|
widgets.add(new OptionIntegerWidget(BetterFoliage.config.coralChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.coralChance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(0, x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,42 @@
|
|||||||
|
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 enum Button {CLOSE, GRASS_USE_GENERATED}
|
||||||
|
|
||||||
|
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, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMin, -100, 20, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
|
||||||
|
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMax, -100, 50, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void addButtons(int x, int y) {
|
||||||
|
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.back")));
|
||||||
|
buttonList.add(new GuiButton(Button.GRASS_USE_GENERATED.ordinal(), x - 100, y - 40, 200, 20, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateButtons() {
|
||||||
|
setButtonOptionBoolean(Button.GRASS_USE_GENERATED.ordinal(), "message.betterfoliage.genShortgrass", BetterFoliage.config.grassUseGenerated);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onButtonPress(int id) {
|
||||||
|
if (id == Button.CLOSE.ordinal()) FMLClientHandler.instance().showGuiScreen(parent);
|
||||||
|
if (id == Button.GRASS_USE_GENERATED.ordinal()) BetterFoliage.config.grassUseGenerated = !BetterFoliage.config.grassUseGenerated;
|
||||||
|
|
||||||
|
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,90 @@
|
|||||||
|
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,
|
||||||
|
TOGGLE_CORAL, CONFIG_CORAL}
|
||||||
|
|
||||||
|
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 + 110, 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")));
|
||||||
|
|
||||||
|
buttonList.add(new GuiButton(Button.TOGGLE_CORAL.ordinal(), x - 100, y + 80, 150, 20, ""));
|
||||||
|
buttonList.add(new GuiButton(Button.CONFIG_CORAL.ordinal(), x + 60, y + 80, 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);
|
||||||
|
setButtonOptionBoolean(Button.TOGGLE_CORAL.ordinal(), "message.betterfoliage.betterCoral", BetterFoliage.config.coralEnabled);
|
||||||
|
((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.TOGGLE_CORAL.ordinal()) BetterFoliage.config.coralEnabled = !BetterFoliage.config.coralEnabled;
|
||||||
|
|
||||||
|
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));
|
||||||
|
if (id== Button.CONFIG_CORAL.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiCoral(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,440 @@
|
|||||||
|
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;
|
||||||
|
public void setGray(float value) {
|
||||||
|
red = value; green = value; blue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
|
||||||
|
protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
|
||||||
|
|
||||||
|
protected ForgeDirection[] faceDir1 = new ForgeDirection[] {ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.EAST, ForgeDirection.SOUTH, ForgeDirection.NORTH};
|
||||||
|
protected ForgeDirection[] faceDir2 = new ForgeDirection[] {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP};
|
||||||
|
|
||||||
|
/** Random vector pool. Unit rotation vectors in the XZ plane, Y coord goes between [-1.0, 1.0].
|
||||||
|
* Filled at init time */
|
||||||
|
public Double3[] pRot = new Double3[64];
|
||||||
|
|
||||||
|
/** Pool of random double values. Filled at init time. */
|
||||||
|
public double[] pRand = new double[64];
|
||||||
|
|
||||||
|
public ShadingValues aoXPYZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYPP = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYPN = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYNP = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYNN = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYPP = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYPN = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYNP = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYNN = new ShadingValues();
|
||||||
|
|
||||||
|
// temporary shading values for a single face
|
||||||
|
public ShadingValues faceAOPP, faceAOPN, faceAONN, faceAONP;
|
||||||
|
|
||||||
|
/** Initialize random values */
|
||||||
|
public void init() {
|
||||||
|
List<Double3> perturbs = new ArrayList<Double3>(64);
|
||||||
|
for (int idx = 0; idx < 64; idx++) {
|
||||||
|
double angle = (double) idx * Math.PI * 2.0 / 64.0;
|
||||||
|
perturbs.add(new Double3(Math.cos(angle), Math.random() * 2.0 - 1.0, Math.sin(angle)));
|
||||||
|
pRand[idx] = Math.random();
|
||||||
|
}
|
||||||
|
Collections.shuffle(perturbs);
|
||||||
|
Iterator<Double3> iter = perturbs.iterator();
|
||||||
|
for (int idx = 0; idx < 64; idx++) pRot[idx] = iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a semi-random value depending on block position.
|
||||||
|
* @param x block X coord
|
||||||
|
* @param y block Y coord
|
||||||
|
* @param z block Z coord
|
||||||
|
* @param seed additional seed
|
||||||
|
* @return semirandom value
|
||||||
|
*/
|
||||||
|
protected int getSemiRandomFromPos(double x, double y, double z, int seed) {
|
||||||
|
long lx = MathHelper.floor_double(x);
|
||||||
|
long ly = MathHelper.floor_double(y);
|
||||||
|
long lz = MathHelper.floor_double(z);
|
||||||
|
long value = (lx * lx + ly * ly + lz * lz + lx * ly + ly * lz + lz * lx + seed * seed) & 63;
|
||||||
|
value = (3 * lx * value + 5 * ly * value + 7 * lz * value + 11 * seed) & 63;
|
||||||
|
return (int) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
|
||||||
|
renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldRender3DInInventory(int modelId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRenderId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void 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,85 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render dirt block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
int variation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
|
||||||
|
IIcon renderIcon = algaeIcons.get(variation);
|
||||||
|
if (renderIcon == null) return true;
|
||||||
|
|
||||||
|
double scale = 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,92 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.renderBlockCactus(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render cactus center
|
||||||
|
setPassCounters(1);
|
||||||
|
|
||||||
|
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,105 @@
|
|||||||
|
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.material.Material;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.gen.NoiseGeneratorSimplex;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class RenderBlockBetterCoral extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet coralCrustIcons = new IconSet("bettergrassandleaves", "better_crust_%d");
|
||||||
|
public IconSet coralCrossIcons = new IconSet("bettergrassandleaves", "better_coral_%d");
|
||||||
|
public NoiseGeneratorSimplex noise;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!BetterFoliage.config.coralEnabled) return false;
|
||||||
|
if (block != Blocks.sand) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x * 0.1, z * 0.1) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < BetterFoliage.config.coralPopulation.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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render sand block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
double offset = pRand[getSemiRandomFromPos(x, y, z, 6)] * BetterFoliage.config.coralVOffset.value;
|
||||||
|
double halfSize = BetterFoliage.config.coralSize.value * 0.5;
|
||||||
|
double halfCrustSize = BetterFoliage.config.coralCrustSize.value * 0.5;
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
|
||||||
|
if (blockAccess.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ).getMaterial() != Material.water) continue;
|
||||||
|
if (blockAccess.isAirBlock(x + dir.offsetX, y + dir.offsetY + 1, z + dir.offsetZ)) continue;
|
||||||
|
|
||||||
|
int variation = getSemiRandomFromPos(x, y, z, dir.ordinal());
|
||||||
|
if (variation < BetterFoliage.config.coralChance.value) {
|
||||||
|
IIcon crustIcon = coralCrustIcons.get(variation);
|
||||||
|
IIcon coralIcon = coralCrossIcons.get(variation);
|
||||||
|
if (crustIcon != null) renderCoralCrust(blockCenter, dir, offset, halfCrustSize, crustIcon, variation);
|
||||||
|
if (coralIcon != null) renderCrossedSideQuads(blockCenter.add(new Double3(dir).scale(0.5)), dir,
|
||||||
|
halfSize, halfSize,
|
||||||
|
pRot[variation], BetterFoliage.config.coralHOffset.value,
|
||||||
|
coralIcon, 0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCoralCrust(Double3 blockCenter, ForgeDirection dir, double offset, double scale, IIcon icon, int uvRot) {
|
||||||
|
Double3 face1 = new Double3(faceDir1[dir.ordinal()]).scale(scale);
|
||||||
|
Double3 face2 = new Double3(faceDir2[dir.ordinal()]).scale(scale);
|
||||||
|
Double3 drawCenter = blockCenter.add(new Double3(dir).scale(0.5 + offset));
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
setShadingForFace(dir);
|
||||||
|
renderQuadWithShading(icon, drawCenter, face1, face2, uvRot, faceAOPP, faceAONP, faceAONN, faceAOPN);
|
||||||
|
} else {
|
||||||
|
renderQuad(icon, drawCenter, face1, face2, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
coralCrustIcons.registerIcons(event.map);
|
||||||
|
coralCrossIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d coral crust textures", coralCrustIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d coral textures", coralCrossIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.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 snowGrassIcons = new IconSet("bettergrassandleaves", "better_grass_snowed_%d");
|
||||||
|
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
|
||||||
|
public IIcon grassGenIcon;
|
||||||
|
public IIcon snowGrassGenIcon;
|
||||||
|
|
||||||
|
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 (!blockAccess.isAirBlock(x, y + 1, z) && blockAccess.getBlock(x, y + 1, z) != Blocks.snow_layer) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render 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);
|
||||||
|
boolean isSnowed = blockAccess.getBlock(x, y + 1, z) == Blocks.snow_layer;
|
||||||
|
|
||||||
|
IIcon renderIcon = null;
|
||||||
|
if (block instanceof BlockGrass) {
|
||||||
|
if (BetterFoliage.config.grassUseGenerated) {
|
||||||
|
renderIcon = isSnowed ? snowGrassGenIcon : grassGenIcon;
|
||||||
|
} else {
|
||||||
|
renderIcon = isSnowed ? snowGrassIcons.get(variation) : grassIcons.get(variation);
|
||||||
|
}
|
||||||
|
} else if (block == Blocks.mycelium && !isSnowed) {
|
||||||
|
renderIcon = myceliumIcons.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));
|
||||||
|
|
||||||
|
if (isSnowed) {
|
||||||
|
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
|
||||||
|
Tessellator.instance.setColorOpaque(230, 230, 230);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render short grass
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowed ? 0.0625 : 0.0), 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);
|
||||||
|
snowGrassIcons.registerIcons(event.map);
|
||||||
|
myceliumIcons.registerIcons(event.map);
|
||||||
|
grassGenIcon = event.map.registerIcon("bf_shortgrass:minecraft:tallgrass");
|
||||||
|
snowGrassGenIcon = event.map.registerIcon("bf_shortgrass_snow:minecraft:tallgrass");
|
||||||
|
BetterFoliage.log.info(String.format("Found %d short grass textures", grassIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d snowy grass textures", snowGrassIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
if (blockLeafIcon == null) {
|
||||||
|
BetterFoliage.log.debug(String.format("null leaf texture, x:%d, y:%d, z:%d, meta:%d, block:%s", x, y, z, blockAccess.getBlockMetadata(x, y, z), block.getClass().getName()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
|
||||||
|
if (crossLeafIcon == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
double halfSize = 0.5 * 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,73 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.renderBlockLilyPad(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render lilypad block
|
||||||
|
renderBlockLilyPad(block, x, y, z);
|
||||||
|
|
||||||
|
int chanceVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
int offsetVariation = getSemiRandomFromPos(x, y, z, 2);
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
Tessellator.instance.setColorOpaque(255, 255, 255);
|
||||||
|
if (lilypadRoots.hasIcons()) renderCrossedSideQuads(new Double3(x + 0.5, y + 0.015, z + 0.5), ForgeDirection.DOWN,
|
||||||
|
0.2, 0.3,
|
||||||
|
null, 0.0,
|
||||||
|
lilypadRoots.get(iconVariation), 2,
|
||||||
|
true);
|
||||||
|
if (chanceVariation < 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,93 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.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;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render dirt block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
int 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);
|
||||||
|
|
||||||
|
// render reeds
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
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,88 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
this.missingResource = missingResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = event.map;
|
||||||
|
|
||||||
|
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||||
|
if (domainManagers == null) {
|
||||||
|
BetterFoliage.log.warn("Failed to inject texture generator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
domainManagers.put(domainName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
blockTextures = null;
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
// don't leave a mess
|
||||||
|
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
|
||||||
|
if (domainManagers != null) domainManagers.remove(domainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getResourceDomains() {
|
||||||
|
return ImmutableSet.<String>of(domainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IResource> getAllResources(ResourceLocation resource) throws IOException {
|
||||||
|
return ImmutableList.<IResource>of(getResource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResource getMissingResource() throws IOException {
|
||||||
|
return Minecraft.getMinecraft().getResourceManager().getResource(missingResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLocation unwrapResource(ResourceLocation wrapped) {
|
||||||
|
return new ResourceLocation(wrapped.getResourcePath().substring(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int blendRGB(int rgbOrig, int rgbBlend, int weightOrig, int weightBlend) {
|
||||||
|
int r = ((rgbOrig & 0xFF) * weightOrig + (rgbBlend & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int g = (((rgbOrig >> 8) & 0xFF) * weightOrig + ((rgbBlend >> 8) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int b = (((rgbOrig >> 16) & 0xFF) * weightOrig + ((rgbBlend >> 16) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int a = (rgbOrig >> 24) & 0xFF;
|
||||||
|
int result = (int) (a << 24 | b << 16 | g << 8 | r);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.data.IMetadataSection;
|
||||||
|
|
||||||
|
public class BufferedImageResource implements IResource {
|
||||||
|
|
||||||
|
/** Raw PNG data*/
|
||||||
|
protected byte[] data = null;
|
||||||
|
|
||||||
|
public BufferedImageResource(BufferedImage image) {
|
||||||
|
// create PNG image
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(image, "PNG", baos);
|
||||||
|
data = baos.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return data == null ? null : new ByteArrayInputStream(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMetadata() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMetadataSection getMetadata(String var1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent.Post;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
|
||||||
|
|
||||||
|
public class LeafGenerator extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
/** Resource domain name of pre-drawn textures */
|
||||||
|
public String nonGeneratedDomain = "betterfoliage";
|
||||||
|
|
||||||
|
/** Number of textures generated in the current run */
|
||||||
|
public int generatedCounter = 0;
|
||||||
|
|
||||||
|
/** Number of pre-drawn textures found in the current run */
|
||||||
|
public int drawnCounter = 0;
|
||||||
|
|
||||||
|
/** Name of the default alpha mask to use */
|
||||||
|
public static String defaultMask = "rough";
|
||||||
|
|
||||||
|
public LeafGenerator(String domainName, ResourceLocation missingResource) {
|
||||||
|
super(domainName, missingResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// check for provided texture
|
||||||
|
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format("textures/blocks/%s/%s", originalNoDirs.getResourceDomain(), originalNoDirs.getResourcePath()));
|
||||||
|
if (Utils.resourceExists(handDrawnLocation)) {
|
||||||
|
drawnCounter++;
|
||||||
|
return resourceManager.getResource(handDrawnLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate our own
|
||||||
|
if (!Utils.resourceExists(originalWithDirs)) return getMissingResource();
|
||||||
|
|
||||||
|
// load normal leaf texture
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
|
||||||
|
if (origImage.getWidth() != origImage.getHeight()) return getMissingResource();
|
||||||
|
int size = origImage.getWidth();
|
||||||
|
|
||||||
|
// tile leaf texture 2x2
|
||||||
|
BufferedImage overlayIcon = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = overlayIcon.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, 0, null);
|
||||||
|
graphics.drawImage(origImage, 0, size, null);
|
||||||
|
graphics.drawImage(origImage, size, 0, null);
|
||||||
|
graphics.drawImage(origImage, size, size, null);
|
||||||
|
|
||||||
|
// overlay mask alpha on texture
|
||||||
|
if (!ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
|
||||||
|
// load alpha mask of appropriate size
|
||||||
|
BufferedImage maskImage = loadLeafMaskImage(defaultMask, size * 2);
|
||||||
|
int scale = size * 2 / maskImage.getWidth();
|
||||||
|
|
||||||
|
for (int x = 0; x < overlayIcon.getWidth(); x++) {
|
||||||
|
for (int y = 0; y < overlayIcon.getHeight(); y++) {
|
||||||
|
long origPixel = overlayIcon.getRGB(x, y) & 0xFFFFFFFFl;
|
||||||
|
long maskPixel = maskImage.getRGB(x / scale, y / scale) & 0xFF000000l | 0x00FFFFFF;
|
||||||
|
overlayIcon.setRGB(x, y, (int) (origPixel & maskPixel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedCounter++;
|
||||||
|
return new BufferedImageResource(overlayIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(Pre event) {
|
||||||
|
super.handleTextureReload(event);
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
generatedCounter = 0;
|
||||||
|
drawnCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(Post event) {
|
||||||
|
super.endTextureReload(event);
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", drawnCounter));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d leaf textures", generatedCounter));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.util.Utils;
|
||||||
|
import mods.betterfoliage.loader.DeobfHelper;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class LeafTextureEnumerator implements IIconRegister {
|
||||||
|
|
||||||
|
/** Resource domain name of generated textures */
|
||||||
|
public String domainName;
|
||||||
|
|
||||||
|
/** Texture atlas for block textures used in the current run */
|
||||||
|
public TextureMap blockTextures;
|
||||||
|
|
||||||
|
public LeafTextureEnumerator(String domainName) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
*/
|
||||||
|
@SubscribeEvent
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = event.map;
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Reloading leaf textures");
|
||||||
|
|
||||||
|
// register simple block textures
|
||||||
|
Iterator<Block> iter = Block.blockRegistry.iterator();
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
Block block = iter.next();
|
||||||
|
if (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, DeobfHelper.transformElementSearge("mapRegisteredSprites"), Map.class);
|
||||||
|
if (mapAtlas == null) mapAtlas = Utils.getField(blockTextures, "mapRegisteredSprites", Map.class);
|
||||||
|
if (mapAtlas == null) {
|
||||||
|
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
|
||||||
|
} else {
|
||||||
|
Set<String> foundLeafTextures = Sets.newHashSet();
|
||||||
|
for (TextureAtlasSprite icon : mapAtlas.values())
|
||||||
|
if (BetterFoliageClient.isLeafTexture(icon))
|
||||||
|
foundLeafTextures.add(icon.getIconName());
|
||||||
|
for (String resourceLocation : foundLeafTextures) {
|
||||||
|
BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", resourceLocation));
|
||||||
|
blockTextures.registerIcon(new ResourceLocation(domainName, resourceLocation).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
public class ReedGenerator extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
public boolean isBottom;
|
||||||
|
|
||||||
|
public ReedGenerator(String domainName, ResourceLocation missingResource, boolean isBottom) {
|
||||||
|
super(domainName, missingResource);
|
||||||
|
this.isBottom = isBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// load full texture
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
|
||||||
|
|
||||||
|
// draw half texture
|
||||||
|
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = result.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, isBottom ? -origImage.getHeight() / 2 : 0, null);
|
||||||
|
|
||||||
|
return new BufferedImageResource(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
public class ShortGrassGenerator extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
protected boolean isSnowed = false;
|
||||||
|
|
||||||
|
protected int snowOriginalWeight = 2;
|
||||||
|
|
||||||
|
protected int snowWhiteWeight = 3;
|
||||||
|
|
||||||
|
public ShortGrassGenerator(String domainName, ResourceLocation missingResource, boolean isSnowed) {
|
||||||
|
super(domainName, missingResource);
|
||||||
|
this.isSnowed = isSnowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// load full texture
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
|
||||||
|
|
||||||
|
// draw bottom half of texture
|
||||||
|
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = result.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, 3 * origImage.getHeight() / 8, null);
|
||||||
|
|
||||||
|
// blend with white if snowed
|
||||||
|
if (isSnowed && !ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
|
||||||
|
for (int x = 0; x < result.getWidth(); x++) for (int y = 0; y < result.getHeight(); y++) {
|
||||||
|
result.setRGB(x, y, blendRGB(result.getRGB(x, y), 0xFFFFFF, 2, 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferedImageResource(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
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="grass", key="useGenerated")
|
||||||
|
public boolean grassUseGenerated = false;
|
||||||
|
|
||||||
|
@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="coral", key="enabled")
|
||||||
|
public boolean coralEnabled = 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);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="population")
|
||||||
|
public OptionInteger coralPopulation = new OptionInteger(0, 64, 1, 32);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="chance")
|
||||||
|
public OptionInteger coralChance = new OptionInteger(0, 64, 1, 32);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="verticalOffset")
|
||||||
|
public OptionDouble coralVOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="horizontalOffset")
|
||||||
|
public OptionDouble coralHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="crustSize")
|
||||||
|
public OptionDouble coralCrustSize = new OptionDouble(0.75, 1.75, 0.05, 1.4);
|
||||||
|
|
||||||
|
@CfgElement(category="coral", key="size")
|
||||||
|
public OptionDouble coralSize = new OptionDouble(0.25, 1.0, 0.05, 0.7);
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
src/main/java/mods/betterfoliage/common/util/Utils.java
Normal file
94
src/main/java/mods/betterfoliage/common/util/Utils.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
|
import mods.betterfoliage.loader.DeobfHelper;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.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, "domainResourceManagers", Map.class);
|
||||||
|
if (result == null) result = getField(manager, DeobfHelper.transformElementSearge("domainResourceManagers"), Map.class);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getField(Object target, String fieldName, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
Field field = target.getClass().getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (T) field.get(target);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getStaticField(Class<?> clazz, String fieldName, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (T) field.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyFromTextResource(ResourceLocation resourceLocation, File target) {
|
||||||
|
try {
|
||||||
|
IResource defaults = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(defaults.getInputStream(), Charsets.UTF_8));
|
||||||
|
FileWriter writer = new FileWriter(target);
|
||||||
|
|
||||||
|
String line = reader.readLine();
|
||||||
|
while(line != null) {
|
||||||
|
writer.write(line + System.lineSeparator());
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
writer.close();
|
||||||
|
} catch(IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
|
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
|
||||||
|
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
||||||
|
|
||||||
|
public String[] getASMTransformerClass() {
|
||||||
|
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModContainerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetupClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectData(Map<String, Object> data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessTransformerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||||
|
|
||||||
|
public class BetterFoliageTransformer implements IClassTransformer {
|
||||||
|
|
||||||
|
protected Iterable<MethodTransformerBase> transformers = ImmutableList.<MethodTransformerBase>of(
|
||||||
|
new TransformRenderBlockOverride(),
|
||||||
|
new TransformShaderModBlockOverride()
|
||||||
|
);
|
||||||
|
|
||||||
|
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
|
||||||
|
|
||||||
|
public BetterFoliageTransformer() {
|
||||||
|
String mcVersion = FMLInjectionData.data()[4].toString();
|
||||||
|
if (!ImmutableList.<String>of("1.7.2", "1.7.10").contains(mcVersion))
|
||||||
|
logger.warn(String.format("Unsupported Minecraft version %s", mcVersion));
|
||||||
|
|
||||||
|
DeobfHelper.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||||
|
// ???
|
||||||
|
if (basicClass == null) return null;
|
||||||
|
|
||||||
|
// read class
|
||||||
|
ClassNode classNode = new ClassNode();
|
||||||
|
ClassReader classReader = new ClassReader(basicClass);
|
||||||
|
classReader.accept(classNode, 0);
|
||||||
|
boolean hasTransformed = false;
|
||||||
|
|
||||||
|
for (MethodTransformerBase transformer : transformers) {
|
||||||
|
// try to find specified method in class
|
||||||
|
if (!transformedName.equals(transformer.getClassName())) continue;
|
||||||
|
|
||||||
|
logger.debug(String.format("Found class: %s -> %s", name, transformedName));
|
||||||
|
for (MethodNode methodNode : classNode.methods) {
|
||||||
|
logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc));
|
||||||
|
Boolean isObfuscated = null;
|
||||||
|
if (methodNode.name.equals(DeobfHelper.transformElementName(transformer.getMethodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(transformer.getSignature()))) {
|
||||||
|
isObfuscated = true;
|
||||||
|
} else if (methodNode.name.equals(transformer.getMethodName()) && methodNode.desc.equals(transformer.getSignature())) {
|
||||||
|
isObfuscated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObfuscated != null) {
|
||||||
|
// transform
|
||||||
|
hasTransformed = true;
|
||||||
|
try {
|
||||||
|
transformer.transform(methodNode, isObfuscated);
|
||||||
|
logger.info(String.format("%s: SUCCESS", transformer.getLogMessage()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(String.format("%s: FAILURE", transformer.getLogMessage()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return result
|
||||||
|
ClassWriter writer = new ClassWriter(0);
|
||||||
|
if (hasTransformed) classNode.accept(writer);
|
||||||
|
return !hasTransformed ? basicClass : writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
src/main/java/mods/betterfoliage/loader/DeobfHelper.java
Normal file
62
src/main/java/mods/betterfoliage/loader/DeobfHelper.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||||
|
|
||||||
|
public class DeobfHelper {
|
||||||
|
|
||||||
|
private static Map<String, String> obfClasses = Maps.newHashMap();
|
||||||
|
private static Map<String, String> obfElements = Maps.newHashMap();
|
||||||
|
private static Map<String, String> srgElements = Maps.newHashMap();
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
String mcVersion = FMLInjectionData.data()[4].toString();
|
||||||
|
srgElements.put("domainResourceManagers", "field_110548_a");
|
||||||
|
srgElements.put("mapRegisteredSprites", "field_110574_e");
|
||||||
|
if ("1.7.2".equals(mcVersion)) {
|
||||||
|
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "ble");
|
||||||
|
obfClasses.put("net/minecraft/world/IBlockAccess", "afx");
|
||||||
|
obfClasses.put("net/minecraft/block/Block", "ahu");
|
||||||
|
|
||||||
|
obfElements.put("blockAccess", "a");
|
||||||
|
obfElements.put("renderBlockByRenderType", "b");
|
||||||
|
obfElements.put("mapRegisteredSprites", "bpr");
|
||||||
|
} else if ("1.7.10".equals(mcVersion)) {
|
||||||
|
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "blm");
|
||||||
|
obfClasses.put("net/minecraft/world/IBlockAccess", "ahl");
|
||||||
|
obfClasses.put("net/minecraft/block/Block", "aji");
|
||||||
|
|
||||||
|
obfElements.put("blockAccess", "a");
|
||||||
|
obfElements.put("renderBlockByRenderType", "b");
|
||||||
|
obfElements.put("mapRegisteredSprites", "bpr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String transformClassName(String className) {
|
||||||
|
return obfClasses.containsKey(className) ? obfClasses.get(className) : className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String transformElementName(String elementName) {
|
||||||
|
return obfElements.containsKey(elementName) ? obfElements.get(elementName) : elementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String transformElementSearge(String elementName) {
|
||||||
|
return srgElements.containsKey(elementName) ? srgElements.get(elementName) : elementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String transformSignature(String signature) {
|
||||||
|
String result = signature;
|
||||||
|
boolean hasChanged = false;
|
||||||
|
do {
|
||||||
|
hasChanged = false;
|
||||||
|
for (Map.Entry<String, String> entry : obfClasses.entrySet()) if (result.contains("L" + entry.getKey() + ";")) {
|
||||||
|
result = result.replace("L" + entry.getKey() + ";", "L" + entry.getValue() + ";");
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
} while(hasChanged);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
public abstract class MethodTransformerBase {
|
||||||
|
|
||||||
|
public static interface IInstructionMatch {
|
||||||
|
public boolean matches(AbstractInsnNode node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getClassName();
|
||||||
|
public abstract String getMethodName();
|
||||||
|
public abstract String getSignature();
|
||||||
|
public abstract String getLogMessage();
|
||||||
|
|
||||||
|
public abstract void transform(MethodNode method, boolean obf);
|
||||||
|
|
||||||
|
protected static String className(String className, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformClassName(className) : className;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String element(String fieldName, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String signature(String signature, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformSignature(signature) : signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 IInstructionMatch matchInvokeAny() {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node instanceof MethodInsnNode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IInstructionMatch matchOpcode(final int opcode) {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node.getOpcode() == opcode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
|
||||||
|
InsnList listAdd = new InsnList();
|
||||||
|
for (AbstractInsnNode inst : added) listAdd.add(inst);
|
||||||
|
insnList.insert(node, listAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.FieldInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
public class TransformRenderBlockOverride extends MethodTransformerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return "net.minecraft.client.renderer.RenderBlocks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return "renderBlockByRenderType";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignature() {
|
||||||
|
return "(Lnet/minecraft/block/Block;III)Z";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogMessage() {
|
||||||
|
return "Applying RenderBlocks.renderBlockByRenderType() render type ovverride";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
|
||||||
|
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
|
||||||
|
insertAfter(method.instructions, storeRenderType,
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks", obf), element("blockAccess", obf), signature("Lnet/minecraft/world/IBlockAccess;", obf)),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 2),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 3),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 4),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 5),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I", obf)),
|
||||||
|
new VarInsnNode(Opcodes.ISTORE, 5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
public class TransformShaderModBlockOverride extends MethodTransformerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return "shadersmodcore.client.Shaders";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return "pushEntity";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignature() {
|
||||||
|
return "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogMessage() {
|
||||||
|
return "Applying Shaders.pushEntity() block id ovverride";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
|
||||||
|
insertAfter(method.instructions, arrayStore.getPrevious(),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I", obf))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.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.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;shouldRenderFace(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;";
|
|
||||||
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
|
||||||
private static final String isOpaqueCube = "Lnet/minecraft/block/Block;isOpaqueCube(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)Z";
|
|
||||||
|
|
||||||
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getFaceOcclusionShape, ordinal = 1))
|
|
||||||
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
|
||||||
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel;
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.renderer.BlockModelRenderer;
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.IBlockDisplayReader;
|
|
||||||
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(BlockModelRenderer.class)
|
|
||||||
public class MixinBlockModelRenderer {
|
|
||||||
|
|
||||||
private static final String renderModel = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModel(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
private static final String renderModelFlat = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelFlat(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
private static final String renderModelSmooth = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelSmooth(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
|
|
||||||
@Redirect(method = renderModel, at = @At(value = "INVOKE", target = renderModelSmooth), remap = false)
|
|
||||||
public boolean onRenderModelSmooth(BlockModelRenderer renderer, IBlockDisplayReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
|
||||||
if (model instanceof SpecialRenderModel)
|
|
||||||
return RenderCtxVanilla.render(renderer, world, (SpecialRenderModel) model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData, true);
|
|
||||||
else
|
|
||||||
return renderer.renderModelSmooth(world, model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = renderModel, at = @At(value = "INVOKE", target = renderModelFlat), remap = false)
|
|
||||||
public boolean onRenderModelFlat(BlockModelRenderer renderer, IBlockDisplayReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
|
||||||
if (model instanceof SpecialRenderModel)
|
|
||||||
return RenderCtxVanilla.render(renderer, world, (SpecialRenderModel) model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData, false);
|
|
||||||
else
|
|
||||||
return renderer.renderModelFlat(world, model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.Hooks;
|
|
||||||
import net.minecraft.block.AbstractBlock;
|
|
||||||
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.Debug;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(AbstractBlock.AbstractBlockState.class)
|
|
||||||
@SuppressWarnings({"deprecation"})
|
|
||||||
public class MixinBlockState {
|
|
||||||
private static final String callFrom = "Lnet/minecraft/block/AbstractBlock$AbstractBlockState;getShadeBrightness(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
|
||||||
// why is the INVOKEVIRTUAL target class Block in the bytecode, not AbstractBlock?
|
|
||||||
private static final String callTo = "Lnet/minecraft/block/Block;getShadeBrightness(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.getShadeBrightness(state, reader, pos), state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.Hooks;
|
|
||||||
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 org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
@Mixin(ClientWorld.class)
|
|
||||||
public class MixinClientWorld {
|
|
||||||
|
|
||||||
private static final String worldAnimateTick = "Lnet/minecraft/client/world/ClientWorld;doAnimateTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)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;sendBlockUpdated(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;blockChanged(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.
|
|
||||||
*/
|
|
||||||
@Inject(method = worldAnimateTick, at = @At(value = "INVOKE", target = blockAnimateTick))
|
|
||||||
void onAnimateTick(int x, int y, int z, int range, Random random, boolean doBarrier, BlockPos.Mutable pos, CallbackInfo ci) {
|
|
||||||
Hooks.onRandomDisplayTick((ClientWorld) (Object) this, pos, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject callback to get notified of client-side blockstate changes.
|
|
||||||
* Used to invalidate caches in the {@link mods.betterfoliage.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.blockChanged(world, pos, oldState, newState, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel;
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxForge;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.IBlockDisplayReader;
|
|
||||||
import net.minecraftforge.client.model.data.IModelData;
|
|
||||||
import net.minecraftforge.client.model.pipeline.ForgeBlockModelRenderer;
|
|
||||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
|
|
||||||
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(ForgeBlockModelRenderer.class)
|
|
||||||
public class MixinForgeBlockModelRenderer {
|
|
||||||
|
|
||||||
private static final String renderModelFlat = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;renderModelFlat(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
private static final String renderModelSmooth = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;renderModelSmooth(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
private static final String render = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;render(Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z";
|
|
||||||
|
|
||||||
@Redirect(method = {renderModelFlat, renderModelSmooth}, at = @At(value = "INVOKE", target = render), remap = false)
|
|
||||||
public boolean render(
|
|
||||||
VertexLighterFlat lighter,
|
|
||||||
IBlockDisplayReader world,
|
|
||||||
IBakedModel model,
|
|
||||||
BlockState state,
|
|
||||||
BlockPos pos,
|
|
||||||
MatrixStack matrixStack,
|
|
||||||
boolean checkSides,
|
|
||||||
Random rand,
|
|
||||||
long seed,
|
|
||||||
IModelData modelData
|
|
||||||
) {
|
|
||||||
if (model instanceof SpecialRenderModel)
|
|
||||||
return RenderCtxForge.render(lighter, world, (SpecialRenderModel) model, state, pos, matrixStack, checkSides, rand, seed, modelData);
|
|
||||||
else
|
|
||||||
return ForgeBlockModelRenderer.render(lighter, world, model, state, pos, matrixStack, checkSides, rand, seed, modelData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.render.lighting.ForgeVertexLighter;
|
|
||||||
import mods.betterfoliage.render.lighting.ForgeVertexLighterAccess;
|
|
||||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(VertexLighterFlat.class)
|
|
||||||
abstract public class MixinForgeCustomVertexLighting implements ForgeVertexLighter, ForgeVertexLighterAccess {
|
|
||||||
|
|
||||||
private static final String processQuad = "Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;processQuad()V";
|
|
||||||
private static final String updateLightmap = "Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;updateLightmap([F[FFFF)V";
|
|
||||||
private static final String updateColor = "Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;updateColor([F[FFFFFI)V";
|
|
||||||
private static final String resetBlockInfo = "Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;resetBlockInfo()V";
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ForgeVertexLighter vertexLighter = this;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ForgeVertexLighter getVertexLighter() {
|
|
||||||
return vertexLighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVertexLighter(@NotNull ForgeVertexLighter vertexLighter) {
|
|
||||||
this.vertexLighter = vertexLighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected abstract void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z);
|
|
||||||
@Shadow
|
|
||||||
protected abstract void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateVertexLightmap(@NotNull float[] normal, @NotNull float[] lightmap, float x, float y, float z) {
|
|
||||||
updateLightmap(normal, lightmap, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateVertexColor(@NotNull float[] normal, @NotNull float[] color, float x, float y, float z, float tint, int multiplier) {
|
|
||||||
updateColor(normal, color, x, y, z, tint, multiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Redirect(method = processQuad, at = @At(value = "INVOKE", target = updateColor), remap = false)
|
|
||||||
void onUpdateColor(VertexLighterFlat self, float[] normal, float[] color, float x, float y, float z, float tint, int multiplier) {
|
|
||||||
vertexLighter.updateVertexColor(normal, color, x, y, z, tint, multiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = processQuad, at = @At(value = "INVOKE", target = updateLightmap), remap = false)
|
|
||||||
void onUpdateLightmap(VertexLighterFlat self, float[] normal, float[] lightmap, float x, float y, float z) {
|
|
||||||
vertexLighter.updateVertexLightmap(normal, lightmap, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = resetBlockInfo, at = @At("RETURN"), remap = false)
|
|
||||||
void onReset(CallbackInfo ci) {
|
|
||||||
vertexLighter = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage;
|
|
||||||
import mods.betterfoliage.BetterFoliageMod;
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent;
|
|
||||||
import net.minecraft.client.renderer.model.*;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.profiler.IProfiler;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(ModelBakery.class)
|
|
||||||
abstract public class MixinModelBakery {
|
|
||||||
|
|
||||||
private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;I)V";
|
|
||||||
private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/util/stream/Stream;Lnet/minecraft/profiler/IProfiler;I)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;";
|
|
||||||
private static final String profilerSection = "Lnet/minecraft/profiler/IProfiler;popPush(Ljava/lang/String;)V";
|
|
||||||
private static final String getBakedModel = "Lnet/minecraft/client/renderer/model/ModelBakery;getBakedModel(Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/model/IModelTransform;Ljava/util/function/Function;)Lnet/minecraft/client/renderer/model/IBakedModel;";
|
|
||||||
private static final String bakeModel = "Lnet/minecraft/client/renderer/model/IUnbakedModel;bake(Lnet/minecraft/client/renderer/model/ModelBakery;Ljava/util/function/Function;Lnet/minecraft/client/renderer/model/IModelTransform;Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/model/IBakedModel;";
|
|
||||||
|
|
||||||
@Inject(method = processLoading, at = @At(value = "INVOKE", target = profilerSection, ordinal = 4))
|
|
||||||
void onBeforeTextures(IProfiler profiler, int maxMipmapLevel, CallbackInfo ci) {
|
|
||||||
profiler.popPush("betterfoliage");
|
|
||||||
BetterFoliageMod.INSTANCE.getBus().post(new ModelDefinitionsLoadedEvent(ModelBakery.class.cast(this)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = getBakedModel, at = @At(value = "INVOKE", target = bakeModel))
|
|
||||||
IBakedModel onBakeModel(
|
|
||||||
IUnbakedModel unbaked,
|
|
||||||
ModelBakery bakery,
|
|
||||||
Function<RenderMaterial, TextureAtlasSprite> spriteGetter,
|
|
||||||
IModelTransform transform,
|
|
||||||
ResourceLocation locationIn
|
|
||||||
) {
|
|
||||||
return BetterFoliage.INSTANCE.getModelManager().onBake(unbaked, bakery, spriteGetter, transform, locationIn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.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 org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Pseudo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
@Pseudo
|
|
||||||
@Mixin(targets = "net.optifine.util.BlockUtils")
|
|
||||||
public class MixinOptifineBlockUtils {
|
|
||||||
private static final String shouldSideBeRendered = "shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Lnet/optifine/render/RenderEnv;)Z";
|
|
||||||
|
|
||||||
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 getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(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 = getFaceOcclusionShape, ordinal = 1))
|
|
||||||
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
|
||||||
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnresolvedMixinReference")
|
|
||||||
@Inject(method = shouldSideBeRendered, at = @At(value = "HEAD"), cancellable = true)
|
|
||||||
private static void shouldForceSideRender(BlockState state, IBlockReader reader, BlockPos pos, Direction face, @Coerce Object renderEnv, CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
if (Hooks.shouldForceSideRenderOF(state, reader, pos, face)) {
|
|
||||||
cir.setReturnValue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
|
||||||
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
|
||||||
import net.minecraft.client.renderer.chunk.VisGraph;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.IBlockDisplayReader;
|
|
||||||
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.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask")
|
|
||||||
public class MixinOptifineChunkRendererDispatcher {
|
|
||||||
|
|
||||||
private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;";
|
|
||||||
private static final String getBlockStateMcp = "Lnet/optifine/override/ChunkCacheOF;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
|
||||||
private static final String getBlockStateSrg = "Lnet/optifine/override/ChunkCacheOF;func_180495_p(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
|
||||||
|
|
||||||
@Inject(method = compile, locals = LocalCapture.CAPTURE_FAILHARD, require = 1, at = {
|
|
||||||
@At(value = "INVOKE", target = getBlockStateMcp),
|
|
||||||
@At(value = "INVOKE", target = getBlockStateSrg),
|
|
||||||
})
|
|
||||||
void onStartBlockRender(
|
|
||||||
float xIn, float yIn, float zIn,
|
|
||||||
ChunkRenderDispatcher.CompiledChunk compiledChunkIn, RegionRenderCacheBuilder builderIn,
|
|
||||||
CallbackInfoReturnable<Set> cir,
|
|
||||||
int i, BlockPos blockpos, BlockPos blockpos1, VisGraph visgraph, Set set, MatrixStack matrixstack,
|
|
||||||
@Coerce IBlockDisplayReader chunkrendercache, RenderType[] singleLayer,
|
|
||||||
boolean shaders, boolean shadersMidBlock, Random random,
|
|
||||||
BlockRendererDispatcher blockrendererdispatcher, Iterator var18,
|
|
||||||
@Coerce BlockPos blockpos2
|
|
||||||
) {
|
|
||||||
RenderCtxBase.reset(chunkrendercache, blockrendererdispatcher, blockpos2, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Inject(method = compile, locals = LocalCapture.PRINT, require = 1, at = {
|
|
||||||
// @At(value = "INVOKE", target = getBlockStateMcp),
|
|
||||||
// @At(value = "INVOKE", target = getBlockStateSrg),
|
|
||||||
// })
|
|
||||||
// void printLocals(
|
|
||||||
// float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
|
||||||
// ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
|
||||||
// CallbackInfoReturnable<BlockState> ci) {
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase;
|
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
|
||||||
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache;
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
|
||||||
import net.minecraft.client.renderer.chunk.VisGraph;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask")
|
|
||||||
public class MixinVanillaChunkRendererDispatcher {
|
|
||||||
|
|
||||||
private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;";
|
|
||||||
private static final String getBlockState = "Lnet/minecraft/client/renderer/chunk/ChunkRenderCache;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
|
||||||
|
|
||||||
@Inject(method = compile, at = @At(value = "INVOKE", target = getBlockState), locals = LocalCapture.CAPTURE_FAILHARD)
|
|
||||||
void onStartBlockRender(
|
|
||||||
float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
|
||||||
ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
|
||||||
CallbackInfoReturnable ci,
|
|
||||||
int i, BlockPos blockpos, BlockPos blockpos1, VisGraph visgraph, Set set,
|
|
||||||
ChunkRenderCache chunkrendercache, MatrixStack matrixstack,
|
|
||||||
Random random,
|
|
||||||
BlockRendererDispatcher blockrendererdispatcher, Iterator var15,
|
|
||||||
BlockPos blockpos2) {
|
|
||||||
RenderCtxBase.reset(chunkrendercache, blockrendererdispatcher, blockpos2, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Inject(method = compile, at = @At(value = "INVOKE", target = getBlockState), locals = LocalCapture.PRINT)
|
|
||||||
// void printLocals(
|
|
||||||
// float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
|
||||||
// ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
|
||||||
// CallbackInfoReturnable ci) {
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
PARSER_BEGIN(BlockConfigParser)
|
|
||||||
package mods.betterfoliage.config.match.parser;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import mods.betterfoliage.config.match.*;
|
|
||||||
|
|
||||||
public class BlockConfigParser {
|
|
||||||
public String configFile;
|
|
||||||
|
|
||||||
ConfigSource getSource(Token t) {
|
|
||||||
return new ConfigSource(configFile, t.beginLine, t.beginColumn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PARSER_END(BlockConfigParser)
|
|
||||||
|
|
||||||
// Whitespace definition
|
|
||||||
SKIP : { " " | "\n" | "\t" | "\r" }
|
|
||||||
|
|
||||||
// Single-line comment
|
|
||||||
SPECIAL_TOKEN : { <lineComment: "//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")> }
|
|
||||||
|
|
||||||
// Lexical state for string literal in quotes
|
|
||||||
SPECIAL_TOKEN : { < quoteStart : "\"" > : withinQuotes }
|
|
||||||
<withinQuotes> SPECIAL_TOKEN : { < quoteEnd : "\"" > : DEFAULT }
|
|
||||||
<withinQuotes> TOKEN : { < stringLiteral : (["a"-"z"] | ["0"-"9"] | "/" | "." | "_" | "-" | ":" )* > }
|
|
||||||
|
|
||||||
// Symbol tokens
|
|
||||||
TOKEN : {
|
|
||||||
< parenStart : "(" > |
|
|
||||||
< parenEnd : ")" > |
|
|
||||||
< dot : "." > |
|
|
||||||
< comma : "," > |
|
|
||||||
< exclamation : "!" >
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Node.MatchAll> matchFile() : {
|
|
||||||
Token t; Node n; List<Node.MatchAll> rules = new LinkedList<Node.MatchAll>();
|
|
||||||
} {
|
|
||||||
(
|
|
||||||
t = "match"
|
|
||||||
{ List<Node> nodes = new LinkedList<Node>(); }
|
|
||||||
(n = match() { nodes.add(n); })*
|
|
||||||
"end"
|
|
||||||
{ rules.add(new Node.MatchAll(getSource(t), nodes)); }
|
|
||||||
)*
|
|
||||||
{ return rules; }
|
|
||||||
}
|
|
||||||
|
|
||||||
Node match() : {
|
|
||||||
Token t; Token t2; MatchMethod mm; List<Node.Value> values; Node.Value v; Node n;
|
|
||||||
} {
|
|
||||||
<exclamation> n = match() { return new Node.Negate(n); }
|
|
||||||
|
|
|
||||||
t = "block.class." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
|
||||||
{ return new Node.MatchValueList(Node.MatchSource.BLOCK_CLASS, mm, getSource(t), values); }
|
|
||||||
|
|
|
||||||
t = "block.name." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
|
||||||
{ return new Node.MatchValueList(Node.MatchSource.BLOCK_NAME, mm, getSource(t), values); }
|
|
||||||
|
|
|
||||||
t = "model." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
|
||||||
{ return new Node.MatchValueList(Node.MatchSource.MODEL_LOCATION, mm, getSource(t), values); }
|
|
||||||
|
|
|
||||||
t = "isParam" <parenStart> t2 = <stringLiteral> <comma> values = matchValueList() <parenEnd>
|
|
||||||
{ return new Node.MatchParam(t2.image, values, getSource(t)); }
|
|
||||||
|
|
|
||||||
t = "setParam" <parenStart> t2 = <stringLiteral> <comma> v = matchValue() <parenEnd>
|
|
||||||
{ return new Node.SetParam(t2.image, v, getSource(t)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
MatchMethod matchMethod() : {} {
|
|
||||||
"matches" { return MatchMethod.EXACT_MATCH; } |
|
|
||||||
"extends" { return MatchMethod.EXTENDS; } |
|
|
||||||
"contains" { return MatchMethod.CONTAINS; }
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Node.Value> matchValueList() : {
|
|
||||||
List<Node.Value> values = new LinkedList<Node.Value>();
|
|
||||||
Node.Value v;
|
|
||||||
} {
|
|
||||||
v = matchValue() { values.add(v); }
|
|
||||||
(<comma> v = matchValue() { values.add(v); } )*
|
|
||||||
{ return values; }
|
|
||||||
}
|
|
||||||
|
|
||||||
Node.Value matchValue() : {
|
|
||||||
Token t;
|
|
||||||
} {
|
|
||||||
t = <stringLiteral>
|
|
||||||
{ return new Node.Value.Literal(getSource(t), t.image); }
|
|
||||||
|
|
|
||||||
"classOf" <parenStart> t = <stringLiteral> <parenEnd>
|
|
||||||
{ return new Node.Value.ClassOf(getSource(t), t.image); }
|
|
||||||
|
|
|
||||||
"model.texture" <parenStart> t = <stringLiteral> <parenEnd>
|
|
||||||
{ return new Node.Value.Texture(getSource(t), t.image); }
|
|
||||||
|
|
|
||||||
"model.tint" <parenStart> t = <stringLiteral> <parenEnd>
|
|
||||||
{ return new Node.Value.Tint(getSource(t), t.image); }
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
|
||||||
import mods.betterfoliage.config.BlockConfig
|
|
||||||
import mods.betterfoliage.integration.OptifineCustomColors
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.block.vanilla.RoundLogOverlayLayer
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardCactusDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardCactusModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardGrassDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardGrassModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLeafDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLeafModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardNetherrackDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardNetherrackModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardRoundLogDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardRoundLogModel
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardSandDiscovery
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardSandModel
|
|
||||||
import mods.betterfoliage.render.lighting.AoSideHelper
|
|
||||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.render.particle.LeafWindTracker
|
|
||||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
|
||||||
import mods.betterfoliage.resource.discovery.RuleBasedDiscovery
|
|
||||||
import mods.betterfoliage.resource.generated.GeneratedTexturePack
|
|
||||||
import mods.betterfoliage.util.resourceManager
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
|
||||||
* except for the call hooks.
|
|
||||||
*/
|
|
||||||
object BetterFoliage {
|
|
||||||
/** Resource pack holding generated assets */
|
|
||||||
val generatedPack = GeneratedTexturePack("bf_gen", "Better Foliage generated assets")
|
|
||||||
|
|
||||||
/** List of recognized [BlockState]s */
|
|
||||||
var blockTypes = BlockTypeCache()
|
|
||||||
|
|
||||||
val blockConfig = BlockConfig()
|
|
||||||
|
|
||||||
val standardModelSupport = RuleBasedDiscovery().apply {
|
|
||||||
discoverers["cactus"] = StandardCactusDiscovery
|
|
||||||
discoverers["dirt"] = StandardDirtDiscovery
|
|
||||||
discoverers["grass"] = StandardGrassDiscovery
|
|
||||||
discoverers["leaf"] = StandardLeafDiscovery
|
|
||||||
discoverers["lilypad"] = StandardLilypadDiscovery
|
|
||||||
discoverers["mycelium"] = StandardMyceliumDiscovery
|
|
||||||
discoverers["netherrack"] = StandardNetherrackDiscovery
|
|
||||||
discoverers["round-log"] = StandardRoundLogDiscovery
|
|
||||||
discoverers["sand"] = StandardSandDiscovery
|
|
||||||
}
|
|
||||||
val modelManager = BakeWrapperManager().apply {
|
|
||||||
discoverers.add(standardModelSupport)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
// discoverers
|
|
||||||
BetterFoliageMod.bus.register(modelManager)
|
|
||||||
BetterFoliageMod.bus.register(LeafParticleRegistry)
|
|
||||||
resourceManager.registerReloadListener(LeafParticleRegistry)
|
|
||||||
|
|
||||||
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
|
|
||||||
|
|
||||||
listOf(
|
|
||||||
StandardLeafDiscovery,
|
|
||||||
StandardSandDiscovery,
|
|
||||||
StandardRoundLogDiscovery,
|
|
||||||
).forEach {
|
|
||||||
}
|
|
||||||
|
|
||||||
// init singletons
|
|
||||||
val singletons = listOf(
|
|
||||||
AoSideHelper,
|
|
||||||
ChunkOverlayManager,
|
|
||||||
LeafWindTracker
|
|
||||||
)
|
|
||||||
|
|
||||||
val modelSingletons = listOf(
|
|
||||||
StandardLeafModel.Companion,
|
|
||||||
StandardGrassModel.Companion,
|
|
||||||
StandardDirtModel.Companion,
|
|
||||||
StandardMyceliumModel.Companion,
|
|
||||||
StandardSandModel.Companion,
|
|
||||||
StandardLilypadModel.Companion,
|
|
||||||
StandardCactusModel.Companion,
|
|
||||||
StandardNetherrackModel.Companion,
|
|
||||||
StandardRoundLogModel.Companion,
|
|
||||||
RisingSoulParticle.Companion
|
|
||||||
)
|
|
||||||
|
|
||||||
// init mod integrations
|
|
||||||
val integrations = listOf(
|
|
||||||
ShadersModIntegration,
|
|
||||||
OptifineCustomColors
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.config.MainConfig
|
|
||||||
import mods.betterfoliage.config.clothGuiRoot
|
|
||||||
import mods.betterfoliage.config.forgeSpecRoot
|
|
||||||
import mods.betterfoliage.util.tryDefault
|
|
||||||
import net.minecraft.client.Minecraft
|
|
||||||
import net.minecraft.client.gui.screen.Screen
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.fml.ExtensionPoint.CONFIGGUIFACTORY
|
|
||||||
import net.minecraftforge.fml.ExtensionPoint.DISPLAYTEST
|
|
||||||
import net.minecraftforge.fml.ModLoadingContext
|
|
||||||
import net.minecraftforge.fml.common.Mod
|
|
||||||
import net.minecraftforge.fml.config.ModConfig
|
|
||||||
import org.apache.commons.lang3.tuple.Pair
|
|
||||||
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 thedarkcolour.kotlinforforge.forge.MOD_BUS
|
|
||||||
import java.io.File
|
|
||||||
import java.io.PrintStream
|
|
||||||
import java.util.Properties
|
|
||||||
import java.util.function.BiFunction
|
|
||||||
import java.util.function.BiPredicate
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
@Mod(BetterFoliageMod.MOD_ID)
|
|
||||||
object BetterFoliageMod {
|
|
||||||
const val MOD_ID = "betterfoliage"
|
|
||||||
|
|
||||||
val bus = MOD_BUS
|
|
||||||
val config = MainConfig()
|
|
||||||
|
|
||||||
val detailLogStream = PrintStream(File("logs/betterfoliage.log").apply {
|
|
||||||
parentFile.mkdirs()
|
|
||||||
if (!exists()) createNewFile()
|
|
||||||
})
|
|
||||||
|
|
||||||
fun logger(obj: Any) = LogManager.getLogger(obj)
|
|
||||||
fun detailLogger(obj: Any) = SimpleLogger(
|
|
||||||
obj::class.java.simpleName, Level.DEBUG, false, true, true, false, "yyyy-MM-dd HH:mm:ss", null, PropertiesUtil(Properties()), detailLogStream
|
|
||||||
)
|
|
||||||
|
|
||||||
init {
|
|
||||||
val ctx = ModLoadingContext.get()
|
|
||||||
|
|
||||||
val configSpec = config.forgeSpecRoot()
|
|
||||||
ctx.registerConfig(ModConfig.Type.CLIENT, configSpec)
|
|
||||||
|
|
||||||
// Add config GUI extension if Cloth Config is available
|
|
||||||
val clothLoaded = tryDefault(false) { Class.forName("me.shedaniel.clothconfig2.forge.api.ConfigBuilder"); true }
|
|
||||||
if (clothLoaded) {
|
|
||||||
logger(this).log(Level.INFO, "Cloth Config found, registering GUI")
|
|
||||||
ctx.registerExtensionPoint(CONFIGGUIFACTORY) { BiFunction<Minecraft, Screen, Screen> { client, parent ->
|
|
||||||
config.clothGuiRoot(
|
|
||||||
parentScreen = parent,
|
|
||||||
prefix = listOf(MOD_ID),
|
|
||||||
background = ResourceLocation("minecraft:textures/block/spruce_log.png"),
|
|
||||||
saveAction = { configSpec.save() }
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept-all version tester (we are client-only)
|
|
||||||
ctx.registerExtensionPoint(DISPLAYTEST) {
|
|
||||||
Pair.of(
|
|
||||||
Supplier { "Honk if you see this!" },
|
|
||||||
BiPredicate<String, Boolean> { _, _ -> true }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Minecraft.getInstance().resourcePackRepository.addPackFinder(BetterFoliage.generatedPack.finder)
|
|
||||||
BetterFoliage.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.ClassRef
|
|
||||||
import mods.betterfoliage.util.ClassRef.Companion.float
|
|
||||||
import mods.betterfoliage.util.ClassRef.Companion.void
|
|
||||||
import mods.betterfoliage.util.FieldRef
|
|
||||||
import mods.betterfoliage.util.MethodRef
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.BlockModelRenderer
|
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
|
||||||
import net.minecraft.client.renderer.model.BakedQuad
|
|
||||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockDisplayReader
|
|
||||||
import net.minecraft.world.IBlockReader
|
|
||||||
import net.minecraftforge.client.model.pipeline.BlockInfo
|
|
||||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat
|
|
||||||
import net.minecraftforge.registries.IRegistryDelegate
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Predicate
|
|
||||||
|
|
||||||
typealias Sprite = TextureAtlasSprite
|
|
||||||
|
|
||||||
// Java
|
|
||||||
val String = ClassRef<String>("java.lang.String")
|
|
||||||
val List = ClassRef<List<*>>("java.util.List")
|
|
||||||
val Random = ClassRef<Random>("java.util.Random")
|
|
||||||
fun <K, V> mapRef() = ClassRef<Map<K, V>>("java.util.Map")
|
|
||||||
fun <K, V> mapRefMutable() = ClassRef<MutableMap<K, V>>("java.util.Map")
|
|
||||||
|
|
||||||
// Minecraft
|
|
||||||
val IBlockReader = ClassRef<IBlockReader>("net.minecraft.world.IBlockReader")
|
|
||||||
val ILightReader = ClassRef<IBlockDisplayReader>("net.minecraft.world.IBlockDisplayReader")
|
|
||||||
val BlockState = ClassRef<BlockState>("net.minecraft.block.BlockState")
|
|
||||||
val BlockPos = ClassRef<BlockPos>("net.minecraft.util.math.BlockPos")
|
|
||||||
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")
|
|
||||||
val BlockModelRenderer = ClassRef<BlockModelRenderer>("net.minecraft.client.renderer.BlockModelRenderer")
|
|
||||||
|
|
||||||
val VertexLighterFlat = ClassRef<VertexLighterFlat>("net.minecraftforge.client.model.pipeline.VertexLighterFlat")
|
|
||||||
val BlockInfo = ClassRef<BlockInfo>("net.minecraftforge.client.model.pipeline.BlockInfo")
|
|
||||||
val VertexLighterFlat_blockInfo = FieldRef(VertexLighterFlat, "blockInfo", BlockInfo)
|
|
||||||
val BlockInfo_shx = FieldRef(BlockInfo, "shx", float)
|
|
||||||
val BlockInfo_shy = FieldRef(BlockInfo, "shy", float)
|
|
||||||
val BlockInfo_shz = FieldRef(BlockInfo, "shz", float)
|
|
||||||
|
|
||||||
object ModelBakery : ClassRef<ModelBakery>("net.minecraft.client.renderer.model.ModelBakery") {
|
|
||||||
val unbakedModels = FieldRef(this, "unbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>())
|
|
||||||
val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>())
|
|
||||||
}
|
|
||||||
|
|
||||||
object RenderTypeLookup : ClassRef<RenderTypeLookup>("net.minecraft.client.renderer.RenderTypeLookup") {
|
|
||||||
val blockRenderChecks = FieldRef(this, "blockRenderChecks", mapRefMutable<IRegistryDelegate<Block>, Predicate<RenderType>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, ILightReader, BlockPos, RenderEnv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optifine shaders
|
|
||||||
object Shaders : ClassRef<Any>("net.optifine.shaders.Shaders") {
|
|
||||||
val shaderPackLoaded = FieldRef(this, "shaderPackLoaded", boolean)
|
|
||||||
val blockLightLevel05 = FieldRef(this, "blockLightLevel05", float)
|
|
||||||
val blockLightLevel06 = FieldRef(this, "blockLightLevel06", float)
|
|
||||||
val blockLightLevel08 = FieldRef(this, "blockLightLevel08", float)
|
|
||||||
}
|
|
||||||
|
|
||||||
object SVertexBuilder : ClassRef<Any>("net.optifine.shaders.SVertexBuilder") {
|
|
||||||
val pushState = MethodRef(this, "pushEntity", void, long)
|
|
||||||
val popState = MethodRef(this, "popEntity", void)
|
|
||||||
}
|
|
||||||
|
|
||||||
object BlockAliases : ClassRef<Any>("net.optifine.shaders.BlockAliases") {
|
|
||||||
val getAliasBlockId = MethodRef(this, "getAliasBlockId", int, BlockState)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.eventbus.api.Event
|
|
||||||
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
@file:JvmName("Hooks")
|
|
||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.WeightedModelWrapper
|
|
||||||
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
|
||||||
import mods.betterfoliage.render.particle.FallingLeafParticle
|
|
||||||
import mods.betterfoliage.render.particle.LeafBlockModel
|
|
||||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.Minecraft
|
|
||||||
import net.minecraft.client.world.ClientWorld
|
|
||||||
import net.minecraft.util.Direction
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.shapes.VoxelShape
|
|
||||||
import net.minecraft.util.math.shapes.VoxelShapes
|
|
||||||
import net.minecraft.world.IBlockDisplayReader
|
|
||||||
import net.minecraft.world.IBlockReader
|
|
||||||
import net.minecraft.world.World
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
|
||||||
if (Config.enabled && Config.roundLogs.enabled && BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state))
|
|
||||||
return Config.roundLogs.dimming.toFloat()
|
|
||||||
return original
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState, flags: Int) {
|
|
||||||
ChunkOverlayManager.onBlockChange(worldClient, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRandomDisplayTick(world: ClientWorld, pos: BlockPos, random: Random) {
|
|
||||||
val state = world.getBlockState(pos)
|
|
||||||
if (Config.enabled &&
|
|
||||||
Config.risingSoul.enabled &&
|
|
||||||
state.block == Blocks.SOUL_SAND &&
|
|
||||||
world.getBlockState(pos.relative(UP)).isAir &&
|
|
||||||
Math.random() < Config.risingSoul.chance) {
|
|
||||||
RisingSoulParticle(world, pos).addIfValid()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.enabled &&
|
|
||||||
Config.fallingLeaves.enabled &&
|
|
||||||
random.nextDouble() < Config.fallingLeaves.chance &&
|
|
||||||
world.getBlockState(pos.relative(DOWN)).isAir
|
|
||||||
) {
|
|
||||||
(getActualRenderModel(world, pos, state, random) as? LeafBlockModel)?.let { leafModel ->
|
|
||||||
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
|
|
||||||
FallingLeafParticle(world, pos, leafModel.key, blockColor, random).addIfValid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
|
||||||
if (Config.enabled && Config.roundLogs.enabled && BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state))
|
|
||||||
return VoxelShapes.empty()
|
|
||||||
return state.getFaceOcclusionShape(reader, pos, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun shouldForceSideRenderOF(state: BlockState, world: IBlockReader, pos: BlockPos, face: Direction) =
|
|
||||||
world.getBlockState(pos.relative(face)).let { neighbor -> BetterFoliage.blockTypes.hasTyped<RoundLogKey>(neighbor) }
|
|
||||||
|
|
||||||
fun getActualRenderModel(world: IBlockDisplayReader, pos: BlockPos, state: BlockState, random: Random): SpecialRenderModel? {
|
|
||||||
val model = Minecraft.getInstance().blockRenderer.blockModelShaper.getBlockModel(state) as? SpecialRenderModel
|
|
||||||
?: return null
|
|
||||||
if (model is WeightedModelWrapper) {
|
|
||||||
random.setSeed(state.getSeed(pos))
|
|
||||||
return model.getModel(random).model
|
|
||||||
}
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package mods.betterfoliage.chunk
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.Int3
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.plus
|
|
||||||
import mods.betterfoliage.util.semiRandom
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
|
||||||
import net.minecraft.util.Direction
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockDisplayReader
|
|
||||||
import net.minecraft.world.IWorldReader
|
|
||||||
import net.minecraft.world.biome.Biome
|
|
||||||
import net.minecraft.world.level.ColorResolver
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
|
|
||||||
* block-relative coordinates.
|
|
||||||
*/
|
|
||||||
interface BlockCtx {
|
|
||||||
val world: IBlockDisplayReader
|
|
||||||
val pos: BlockPos
|
|
||||||
|
|
||||||
val seed: Long get() = state.getSeed(pos)
|
|
||||||
|
|
||||||
fun offset(dir: Direction) = offset(dir.offset)
|
|
||||||
fun offset(offset: Int3): BlockCtx
|
|
||||||
|
|
||||||
val state: BlockState get() = world.getBlockState(pos)
|
|
||||||
fun state(offset: Int3) = world.getBlockState(pos + offset)
|
|
||||||
fun state(dir: Direction) = state(dir.offset)
|
|
||||||
|
|
||||||
fun isAir(offset: Int3) = (pos + offset).let { world.getBlockState(it).isAir(world, it) }
|
|
||||||
fun isAir(dir: Direction) = isAir(dir.offset)
|
|
||||||
|
|
||||||
val biome: Biome? get() =
|
|
||||||
(world as? IWorldReader)?.getBiome(pos) ?:
|
|
||||||
(world as? ChunkRenderCache)?.level?.getBiome(pos)
|
|
||||||
|
|
||||||
val isFullBlock: Boolean get() = state.isCollisionShapeFullBlock(world, pos)
|
|
||||||
|
|
||||||
fun isNeighborSturdy(dir: Direction) = offset(dir).let { it.state.isFaceSturdy(it.world, it.pos, dir.opposite) }
|
|
||||||
|
|
||||||
fun shouldSideBeRendered(side: Direction) = Block.shouldRenderFace(state, world, pos, side)
|
|
||||||
|
|
||||||
/** Get a semi-random value based on the block coordinate and the given seed. */
|
|
||||||
fun semiRandom(seed: Int) = pos.semiRandom(seed)
|
|
||||||
|
|
||||||
/** Get an array of semi-random values based on the block coordinate. */
|
|
||||||
fun semiRandomArray(num: Int): Array<Int> = Array(num) { semiRandom(it) }
|
|
||||||
|
|
||||||
fun color(resolver: ColorResolver) = world.getBlockTint(pos, resolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
class BasicBlockCtx(
|
|
||||||
override val world: IBlockDisplayReader,
|
|
||||||
override val pos: BlockPos
|
|
||||||
) : BlockCtx {
|
|
||||||
override val state: BlockState = world.getBlockState(pos)
|
|
||||||
override fun offset(offset: Int3) = BasicBlockCtx(world, pos + offset)
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package mods.betterfoliage.chunk
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.isInstance
|
|
||||||
import mods.betterfoliage.ChunkCacheOF
|
|
||||||
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.DimensionType
|
|
||||||
import net.minecraft.world.IBlockDisplayReader
|
|
||||||
import net.minecraft.world.IWorldReader
|
|
||||||
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.set
|
|
||||||
|
|
||||||
val IBlockDisplayReader.dimType: DimensionType get() = when {
|
|
||||||
this is IWorldReader -> dimensionType()
|
|
||||||
this is ChunkRenderCache -> level.dimensionType()
|
|
||||||
this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].level.dimensionType()
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
abstract class ChunkOverlayLayer<T> {
|
|
||||||
val dimData = IdentityHashMap<DimensionType, SparseChunkedMap<T>>()
|
|
||||||
abstract fun calculate(ctx: BlockCtx): T
|
|
||||||
abstract fun onBlockUpdate(world: IBlockDisplayReader, pos: BlockPos)
|
|
||||||
|
|
||||||
operator fun get(ctx: BlockCtx): T {
|
|
||||||
return dimData
|
|
||||||
.getOrPut(ctx.world.dimType) { SparseChunkedMap() }
|
|
||||||
.getOrPut(ctx.pos) { calculate(ctx) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(world: IBlockDisplayReader, pos: BlockPos) {
|
|
||||||
dimData[world.dimType]?.remove(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event forwarder for multiple layers of chunk overlay data.
|
|
||||||
*/
|
|
||||||
object ChunkOverlayManager {
|
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
|
||||||
val layers = mutableListOf<ChunkOverlayLayer<*>>()
|
|
||||||
|
|
||||||
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
|
|
||||||
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
|
||||||
layers.forEach { layer -> layer.dimData.remove(world.dimType) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleUnloadChunk(event: ChunkEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
|
||||||
layers.forEach { layer -> layer.dimData[world.dimType]?.removeChunk(event.chunk.pos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DoubleMap<K1, K2, V> {
|
|
||||||
val map1: MutableMap<K1, MutableMap<K2, V>>
|
|
||||||
fun createMap2(): MutableMap<K2, V>
|
|
||||||
|
|
||||||
fun remove(key1: K1) {
|
|
||||||
map1.remove(key1)
|
|
||||||
}
|
|
||||||
fun remove(key1: K1, key2: K2) {
|
|
||||||
map1[key1]?.remove(key2)
|
|
||||||
}
|
|
||||||
fun contains(key1: K1) = map1.contains(key1)
|
|
||||||
|
|
||||||
fun getOrSet(key1: K1, key2: K2, factory: () -> V) =
|
|
||||||
(map1[key1] ?: createMap2().apply { map1[key1] = this }).let { subMap ->
|
|
||||||
subMap[key2] ?: factory().apply { subMap[key2] = this }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SparseChunkedMap<V> {
|
|
||||||
val map = object : DoubleMap<ChunkPos, BlockPos, V> {
|
|
||||||
override val map1 = mutableMapOf<ChunkPos, MutableMap<BlockPos, V>>()
|
|
||||||
override fun createMap2() = mutableMapOf<BlockPos, V>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getOrPut(pos: BlockPos, factory: () -> V) = map.getOrSet(ChunkPos(pos), pos, factory)
|
|
||||||
fun remove(pos: BlockPos) = map.remove(ChunkPos(pos), pos)
|
|
||||||
fun removeChunk(pos: ChunkPos) = map.map1.remove(pos)
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import mods.betterfoliage.config.match.Node
|
|
||||||
import mods.betterfoliage.config.match.parser.BlockConfigParser
|
|
||||||
import mods.betterfoliage.config.match.parser.ParseException
|
|
||||||
import mods.betterfoliage.config.match.parser.TokenMgrError
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
|
||||||
import mods.betterfoliage.util.stripStart
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.apache.logging.log4j.Level.ERROR
|
|
||||||
|
|
||||||
class BlockConfig : HasLogger() {
|
|
||||||
lateinit var rules: List<Node.MatchAll>
|
|
||||||
|
|
||||||
fun readConfig(manager: IResourceManager) {
|
|
||||||
val configs = manager.listResources("config/betterfoliage") { it.endsWith(".rules") }
|
|
||||||
rules = configs.flatMap { configLocation ->
|
|
||||||
val resource = manager.getResource(configLocation)
|
|
||||||
val parser = BlockConfigParser(resource.inputStream)
|
|
||||||
.apply { configFile = configLocation.stripStart("config/betterfoliage/").toString() }
|
|
||||||
try {
|
|
||||||
parser.matchFile()
|
|
||||||
} catch (e: ParseException) {
|
|
||||||
parseError(e, configLocation)
|
|
||||||
} catch (e: TokenMgrError) {
|
|
||||||
parseError(e, configLocation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseError(e: Throwable, location: ResourceLocation): List<Node.MatchAll> {
|
|
||||||
"Error parsing block config $location: ${e.message}".let {
|
|
||||||
logger.log(ERROR, it)
|
|
||||||
detailLogger.log(ERROR, it)
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import me.shedaniel.clothconfig2.forge.api.AbstractConfigListEntry
|
|
||||||
import me.shedaniel.clothconfig2.forge.api.ConfigBuilder
|
|
||||||
import me.shedaniel.clothconfig2.forge.api.ConfigEntryBuilder
|
|
||||||
import me.shedaniel.clothconfig2.forge.gui.entries.SubCategoryListEntry
|
|
||||||
import mods.betterfoliage.util.asText
|
|
||||||
import net.minecraft.client.gui.screen.Screen
|
|
||||||
import net.minecraft.client.resources.I18n
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.common.ForgeConfigSpec
|
|
||||||
import java.util.Optional
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
const val MAX_LINE_LEN = 30
|
|
||||||
|
|
||||||
fun DelegatingConfigGroup.forgeSpecRoot() =
|
|
||||||
ForgeConfigSpec.Builder()
|
|
||||||
.also { createForgeNode(it) }
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun DelegatingConfigGroup.clothGuiRoot(
|
|
||||||
parentScreen: Screen,
|
|
||||||
prefix: List<String>,
|
|
||||||
background: ResourceLocation,
|
|
||||||
saveAction: ()->Unit
|
|
||||||
) = ConfigBuilder.create()
|
|
||||||
.setParentScreen(parentScreen)
|
|
||||||
.setTitle(I18n.get((prefix + "title").joinToString(".")).asText())
|
|
||||||
.setDefaultBackgroundTexture(background)
|
|
||||||
.setSavingRunnable(saveAction)
|
|
||||||
.also { builder ->
|
|
||||||
createClothNode(prefix).value.forEach { rootCategory ->
|
|
||||||
builder.getOrCreateCategory("main".asText()).addEntry(rootCategory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
|
|
||||||
sealed class DelegatingConfigNode {
|
|
||||||
abstract fun createClothNode(path: List<String>): AbstractConfigListEntry<*>
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DelegatingConfigValue<T> : DelegatingConfigNode(), ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
lateinit var forgeValue: ForgeConfigSpec.ConfigValue<T>
|
|
||||||
abstract fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
open class DelegatingConfigGroup : DelegatingConfigNode() {
|
|
||||||
val children = mutableMapOf<String, DelegatingConfigNode>()
|
|
||||||
|
|
||||||
fun createForgeNode(builder: ForgeConfigSpec.Builder) {
|
|
||||||
children.forEach { (name, node) ->
|
|
||||||
when(node) {
|
|
||||||
is DelegatingConfigGroup -> {
|
|
||||||
builder.push(name)
|
|
||||||
node.createForgeNode(builder)
|
|
||||||
builder.pop()
|
|
||||||
}
|
|
||||||
is DelegatingConfigValue<*> -> node.createForgeNode(builder, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createClothNode(path: List<String>): SubCategoryListEntry {
|
|
||||||
val builder = ConfigEntryBuilder.create()
|
|
||||||
.startSubCategory(path.joinToString(".").translate())
|
|
||||||
.setTooltip(*path.joinToString(".").translateTooltip())
|
|
||||||
.setExpanded(false)
|
|
||||||
children.forEach { (name, node) -> builder.add(node.createClothNode(path + name)) }
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DelegatingConfigGroupFactory<T> {
|
|
||||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T: DelegatingConfigGroup> subNode(factory: ()->T) = object : DelegatingConfigGroupFactory<T> {
|
|
||||||
override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
val child = factory()
|
|
||||||
parent.children[property.name] = child
|
|
||||||
return ReadOnlyProperty { _, _ -> child }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DelegatingConfigValueFactory<T> {
|
|
||||||
fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String): ForgeConfigSpec.ConfigValue<T>
|
|
||||||
fun createClothNode(prop: CachingConfigProperty<T>, path: List<String>): AbstractConfigListEntry<T>
|
|
||||||
|
|
||||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
return object : CachingConfigProperty<T>(parent, property) {
|
|
||||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) {
|
|
||||||
forgeValue = this@DelegatingConfigValueFactory.createForgeNode(builder, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createClothNode(path: List<String>): AbstractConfigListEntry<*> = createClothNode(this, path)
|
|
||||||
|
|
||||||
}.apply { parent.children[property.name] = this }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class CachingConfigProperty<T>(parent: DelegatingConfigGroup, property: KProperty<*>) : DelegatingConfigValue<T>() {
|
|
||||||
var value: T? = null
|
|
||||||
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) =
|
|
||||||
value ?: forgeValue.get().apply { value = this }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.translate() = I18n.get(this).asText()
|
|
||||||
fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) =
|
|
||||||
I18n.get("$this.tooltip").splitToSequence(" ").fold(mutableListOf("")) { tooltips, word ->
|
|
||||||
if (tooltips.last().length + word.length < lineLength) {
|
|
||||||
tooltips[tooltips.lastIndex] += "$word "
|
|
||||||
} else {
|
|
||||||
tooltips.add("$word ")
|
|
||||||
}
|
|
||||||
tooltips
|
|
||||||
}.map { it.trim().asText() }.toTypedArray()
|
|
||||||
|
|
||||||
fun boolean(
|
|
||||||
default: Boolean,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Boolean)->Boolean = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Boolean> {
|
|
||||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
|
||||||
builder.define(name, default)
|
|
||||||
|
|
||||||
override fun createClothNode(prop: CachingConfigProperty<Boolean>, path: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startBooleanToggle(langKey(path).translate(), prop.forgeValue.get())
|
|
||||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
|
||||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun integer(
|
|
||||||
default: Int = 0, min: Int = 0, max: Int,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Int)->Int = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Int> {
|
|
||||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
|
||||||
builder.defineInRange(name, default, min, max)
|
|
||||||
|
|
||||||
override fun createClothNode(prop: CachingConfigProperty<Int>, path: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startIntField(langKey(path).translate(), prop.forgeValue.get())
|
|
||||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
|
||||||
.setMin(min).setMax(max)
|
|
||||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun double(
|
|
||||||
default: Double = 0.0, min: Double = 0.0, max: Double = 1.0,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Double)->Double = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Double> {
|
|
||||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
|
||||||
builder.defineInRange(name, default, min, max)
|
|
||||||
|
|
||||||
override fun createClothNode(prop: CachingConfigProperty<Double>, path: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startDoubleField(langKey(path).translate(), prop.forgeValue.get())
|
|
||||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
|
||||||
.setMin(min).setMax(max)
|
|
||||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
val recurring = { path: List<String> -> "${path.first()}.${path.last()}" }
|
|
||||||
fun fakeCategory(name: String) = { names: List<String> ->
|
|
||||||
(listOf(names.first(), name) + names.drop(1)).joinToString(".")
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
fun featureEnable(default: Boolean = true) = boolean(default, langKey = recurring)
|
|
||||||
|
|
||||||
val Config get() = BetterFoliageMod.config
|
|
||||||
|
|
||||||
abstract class PopulationConfigGroup : DelegatingConfigGroup() {
|
|
||||||
abstract val enabled: Boolean
|
|
||||||
abstract val population: Int
|
|
||||||
|
|
||||||
fun enabled(random: Random) = random.nextInt(64) < population && enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
class MainConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by boolean(true, langKey = { "betterfoliage.global.enabled" })
|
|
||||||
val nVidia by boolean(false)
|
|
||||||
|
|
||||||
val leaves by subNode(::LeavesConfig)
|
|
||||||
val shortGrass by subNode(::ShortGrassConfig)
|
|
||||||
val connectedGrass by subNode(::ConnectedGrassConfig)
|
|
||||||
val roundLogs by subNode(::RoundLogConfig)
|
|
||||||
val cactus by subNode(::CactusConfig)
|
|
||||||
val lilypad by subNode(::LilypadConfig)
|
|
||||||
val reed by subNode(::ReedConfig)
|
|
||||||
val algae by subNode(::AlgaeConfig)
|
|
||||||
val coral by subNode(::CoralConfig)
|
|
||||||
val netherrack by subNode(::NetherrackConfig)
|
|
||||||
val fallingLeaves by subNode(::FallingLeavesConfig)
|
|
||||||
val risingSoul by subNode(::RisingSoulConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LeavesConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by featureEnable()
|
|
||||||
val snowEnabled by boolean(true)
|
|
||||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
|
||||||
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
|
|
||||||
val size by double(min=0.75, max=2.5, default=1.4, langKey = recurring)
|
|
||||||
val dense by boolean(false)
|
|
||||||
val hideInternal by boolean(true)
|
|
||||||
val saturationThreshold by double(default=0.1, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ShortGrassConfig : PopulationConfigGroup() {
|
|
||||||
override val enabled by featureEnable()
|
|
||||||
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, langKey = recurring)
|
|
||||||
val heightMin by double(min=0.1, max=2.5, default=0.6, langKey = recurring)
|
|
||||||
val heightMax by double(min=0.1, max=2.5, default=0.8, langKey = recurring)
|
|
||||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
|
||||||
override val population by integer(max=64, default=64, langKey = recurring)
|
|
||||||
val useGenerated by boolean(false)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
val saturationThreshold by double(default=0.1, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectedGrassConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by boolean(true)
|
|
||||||
val snowEnabled by boolean(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoundLogConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by featureEnable()
|
|
||||||
val plantsOnly by boolean(true)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
class CactusConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by featureEnable()
|
|
||||||
val size by double(min=0.5, max=1.5, default=0.8, langKey = recurring)
|
|
||||||
val sizeVariation by double(max=0.5, default=0.1)
|
|
||||||
val hOffset by double(max=0.5, default=0.1, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LilypadConfig : PopulationConfigGroup() {
|
|
||||||
override val enabled by featureEnable()
|
|
||||||
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
|
|
||||||
override val population by integer(max=64, default=16, min=0, langKey = recurring)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReedConfig : PopulationConfigGroup() {
|
|
||||||
override val enabled by featureEnable()
|
|
||||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
|
||||||
val heightMin by double(min=1.5, max=3.5, default=1.7, langKey = recurring)
|
|
||||||
val heightMax by double(min=1.5, max=3.5, default=2.2, langKey = recurring)
|
|
||||||
override val population by integer(max=64, default=32, langKey = recurring)
|
|
||||||
val minBiomeTemp by double(default=0.4)
|
|
||||||
val minBiomeRainfall by double(default=0.4)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlgaeConfig : PopulationConfigGroup() {
|
|
||||||
override val enabled by featureEnable()
|
|
||||||
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
|
|
||||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
|
||||||
val heightMin by double(min=0.1, max=1.5, default=0.5, langKey = recurring)
|
|
||||||
val heightMax by double(min=0.1, max=1.5, default=1.0, langKey = recurring)
|
|
||||||
override val population by integer(max=64, default=48, langKey = recurring)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoralConfig : PopulationConfigGroup() {
|
|
||||||
override val enabled by featureEnable()
|
|
||||||
val shallowWater by boolean(false)
|
|
||||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
|
||||||
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
|
|
||||||
val size by double(min=0.5, max=1.5, default=0.7, langKey = recurring)
|
|
||||||
val crustSize by double(min=0.5, max=1.5, default=1.4)
|
|
||||||
val chance by integer(max=64, default=32)
|
|
||||||
override val population by integer(max=64, default=48, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetherrackConfig : DelegatingConfigGroup() {
|
|
||||||
val enabled by featureEnable()
|
|
||||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
|
||||||
val heightMin by double(min=0.1, max=1.5, default=0.6, langKey = recurring)
|
|
||||||
val heightMax by double(min=0.1, max=1.5, default=0.8, langKey = recurring)
|
|
||||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FallingLeavesConfig : DelegatingConfigGroup() {
|
|
||||||
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, langKey = recurring)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RisingSoulConfig : DelegatingConfigGroup() {
|
|
||||||
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 integer(min=2, max=128, default=48)
|
|
||||||
val trailDensity by integer(min=1, max=16, default=3)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.block.material.Material
|
|
||||||
import net.minecraft.world.biome.Biome
|
|
||||||
|
|
||||||
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
|
|
||||||
|
|
||||||
val SNOW_MATERIALS = listOf(Material.TOP_SNOW, Material.SNOW)
|
|
||||||
val BlockState.isSnow: Boolean get() = material in SNOW_MATERIALS
|
|
||||||
|
|
||||||
val ACCEPTED_ROUND_LOG_MATERIALS = listOf(Material.WOOD, Material.GRASS)
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package mods.betterfoliage.config.match
|
|
||||||
|
|
||||||
enum class MatchMethod {
|
|
||||||
EXACT_MATCH, EXTENDS, CONTAINS;
|
|
||||||
|
|
||||||
fun description(isSuccess: Boolean) = when (this) {
|
|
||||||
EXACT_MATCH -> if (isSuccess) "matches" else "does not match"
|
|
||||||
EXTENDS -> if (isSuccess) "extends" else "does not extend"
|
|
||||||
CONTAINS -> if (isSuccess) "contains" else "does not contain"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic Either monad implementation
|
|
||||||
*/
|
|
||||||
sealed class Either<out L, out R> {
|
|
||||||
class Left<L>(val left: L) : Either<L, Nothing>()
|
|
||||||
class Right<R>(val right: R) : Either<Nothing, R>()
|
|
||||||
|
|
||||||
fun leftOrNull() = if (this is Left) left else null
|
|
||||||
fun rightOrNull() = if (this is Right) right else null
|
|
||||||
|
|
||||||
fun <R2> map(func: (R) -> R2): Either<L, R2> = when (this) {
|
|
||||||
is Left<L> -> this
|
|
||||||
is Right<R> -> Right(func(right))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <L2> mapLeft(func: (L) -> L2): Either<L2, R> = when (this) {
|
|
||||||
is Left<L> -> Left(func(left))
|
|
||||||
is Right<R> -> this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ifRight(action: (R) -> Unit) {
|
|
||||||
if (this is Right) action(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun <L> ofLeft(left: L) = Left(left)
|
|
||||||
fun <R> ofRight(right: R) = Right(right)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this cannot be inside the class for variance reasons
|
|
||||||
fun <L, R, R2> Either<L, R>.flatMap(func: (R) -> Either<L, R2>) = when (this) {
|
|
||||||
is Either.Left<L> -> this
|
|
||||||
is Either.Right<R> -> func(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <L, R, L2> Either<L, R>.flatMapLeft(func: (L) -> Either<L2, R>) = when (this) {
|
|
||||||
is Either.Left<L> -> func(left)
|
|
||||||
is Either.Right<R> -> this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Either<T, T>.flatten() = when (this) {
|
|
||||||
is Either.Left -> left
|
|
||||||
is Either.Right -> right
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MAnything<out T> {
|
|
||||||
val value: T
|
|
||||||
val immutable: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
class MListAll(val list: List<MAnything<Boolean>>) : MAnything<Boolean> {
|
|
||||||
override val value get() = list.all { it.value }
|
|
||||||
override val immutable get() = list.all { it.immutable }
|
|
||||||
}
|
|
||||||
|
|
||||||
class MListAny(val list: List<MValue<Boolean>>) : MAnything<Boolean> {
|
|
||||||
override val value get() = list.any { it.value }
|
|
||||||
override val immutable get() = list.all { it.immutable }
|
|
||||||
}
|
|
||||||
|
|
||||||
class MNegated(val inner: MAnything<Boolean>) : MAnything<Boolean> {
|
|
||||||
override val value get() = !inner.value
|
|
||||||
override val immutable get() = inner.immutable
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value with metadata related to rule matching applied.
|
|
||||||
*
|
|
||||||
* @param value the wrapped value
|
|
||||||
* @param description human-readable description of what the value represents
|
|
||||||
* @param configSource identifies where the value is described in the config
|
|
||||||
* @param immutable true if the value never changes
|
|
||||||
* (another [MValue] constructed in the same way will have the same value)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class MValue<out T>(
|
|
||||||
override val value: T,
|
|
||||||
val description: String,
|
|
||||||
val configSource: ConfigSource,
|
|
||||||
override val immutable: Boolean,
|
|
||||||
) : MAnything<T> {
|
|
||||||
companion object {
|
|
||||||
fun <T> right(value: T, description: String, configSource: ConfigSource, immutable: Boolean = true) =
|
|
||||||
Either.ofRight(MValue(value, description, configSource, immutable))
|
|
||||||
|
|
||||||
fun left(description: String, configSource: ConfigSource, immutable: Boolean = true) =
|
|
||||||
Either.ofLeft(MValue(false, description, configSource, immutable))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias MEither<T> = Either<MValue<Boolean>, MValue<T>>
|
|
||||||
|
|
||||||
val Node.Value.asEither get() = MValue.right(value, value, configSource, true)
|
|
||||||
fun Node.Value.left(description: String) = MValue.left(description, configSource)
|
|
||||||
fun Node.invalidTypeFor(type: String) = MValue.left("invalid type for $type: [${this::class.java.name}]", configSource)
|
|
||||||
fun Node.error(description: String) = MValue.left(description, configSource)
|
|
||||||
|
|
||||||
fun <T, R> MEither<T>.mapValue(func: (T) -> R) = map {
|
|
||||||
MValue(func(it.value), it.description, it.configSource, it.immutable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> MEither<T>.mapDescription(func: (MValue<T>) -> String) = map {
|
|
||||||
MValue(it.value, func(it), it.configSource, it.immutable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T, R> MEither<T>.map(
|
|
||||||
func: (T) -> R,
|
|
||||||
description: (MValue<T>, R) -> String
|
|
||||||
) = map { t -> func(t.value).let { r -> MValue(r, description(t, r), t.configSource, t.immutable) } }
|
|
||||||
|
|
||||||
fun <T, R> MEither<T>.mapNotNull(
|
|
||||||
func: (T) -> R?,
|
|
||||||
dLeft: (MValue<T>) -> String = { it.description },
|
|
||||||
dRight: (MValue<T>, R) -> String = { m, _ -> m.description }
|
|
||||||
) = flatMap { t ->
|
|
||||||
func(t.value)?.let { r ->
|
|
||||||
MValue.right(r, dRight(t, r), t.configSource, t.immutable)
|
|
||||||
} ?: MValue.left(dLeft(t), t.configSource, t.immutable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> MEither<T>.toRight(value: T) =
|
|
||||||
flatMapLeft { MValue.right(value, it.description, it.configSource, it.immutable) }
|
|
||||||
|
|
||||||
data class MComparison<T1, T2>(
|
|
||||||
private val opTrue: String,
|
|
||||||
private val opFalse: String,
|
|
||||||
val testFunc: (T1, T2) -> Boolean
|
|
||||||
) {
|
|
||||||
fun compare(value1: MEither<T1>, value2: MEither<T2>) = when {
|
|
||||||
value1 is Either.Left -> value1
|
|
||||||
value2 is Either.Left -> value2
|
|
||||||
else -> {
|
|
||||||
val isSuccess = testFunc((value1 as Either.Right).right.value, (value2 as Either.Right).right.value)
|
|
||||||
MValue.right(
|
|
||||||
isSuccess,
|
|
||||||
"${value1.right.description} ${if (isSuccess) opTrue else opFalse} ${value2.right.description}",
|
|
||||||
value2.right.configSource,
|
|
||||||
value1.right.immutable && value2.right.immutable
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.flatten()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun <T1, T2> of(matchMethod: MatchMethod, testFunc: (T1, T2) -> Boolean) =
|
|
||||||
MComparison(matchMethod.description(true), matchMethod.description(false), testFunc)
|
|
||||||
|
|
||||||
val equals = of(MatchMethod.EXACT_MATCH) { t1: Any, t2: Any -> t1 == t2 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package mods.betterfoliage.config.match
|
|
||||||
|
|
||||||
data class ConfigSource(
|
|
||||||
val configFile: String,
|
|
||||||
val line: Int,
|
|
||||||
val column: Int
|
|
||||||
) {
|
|
||||||
override fun toString() = "$configFile @ R$line,C$column"
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Node {
|
|
||||||
enum class MatchSource { BLOCK_CLASS, BLOCK_NAME, MODEL_LOCATION }
|
|
||||||
abstract val configSource: ConfigSource
|
|
||||||
|
|
||||||
class MatchValueList(
|
|
||||||
val matchSource: MatchSource,
|
|
||||||
val matchMethod: MatchMethod,
|
|
||||||
override val configSource: ConfigSource,
|
|
||||||
val values: List<Value>
|
|
||||||
) : Node()
|
|
||||||
|
|
||||||
class MatchParam(
|
|
||||||
val name: String,
|
|
||||||
val values: List<Value>,
|
|
||||||
override val configSource: ConfigSource,
|
|
||||||
) : Node()
|
|
||||||
|
|
||||||
class Negate(val node: Node) : Node() {
|
|
||||||
override val configSource get() = node.configSource
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetParam(val name: String, val value: Value, override val configSource: ConfigSource) : Node()
|
|
||||||
|
|
||||||
class MatchAll(override val configSource: ConfigSource, val list: List<Node>) : Node()
|
|
||||||
|
|
||||||
abstract class Value(override val configSource: ConfigSource, val value: String) : Node() {
|
|
||||||
class Literal(configSource: ConfigSource, value: String) : Value(configSource, value)
|
|
||||||
class ClassOf(configSource: ConfigSource, value: String) : Value(configSource, value)
|
|
||||||
class Texture(configSource: ConfigSource, value: String) : Value(configSource, value)
|
|
||||||
class Tint(configSource: ConfigSource, value: String) : Value(configSource, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
package mods.betterfoliage.config.match
|
|
||||||
|
|
||||||
import mods.betterfoliage.config.match.MatchMethod.CONTAINS
|
|
||||||
import mods.betterfoliage.config.match.MatchMethod.EXACT_MATCH
|
|
||||||
import mods.betterfoliage.config.match.MatchMethod.EXTENDS
|
|
||||||
import mods.betterfoliage.resource.discovery.RuleProcessingContext
|
|
||||||
import mods.betterfoliage.util.findFirst
|
|
||||||
import mods.betterfoliage.util.quoted
|
|
||||||
import mods.betterfoliage.util.tryDefault
|
|
||||||
import net.minecraft.client.renderer.model.BlockModel
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.registries.ForgeRegistries
|
|
||||||
|
|
||||||
typealias PartialLocation = Pair<String?, String>
|
|
||||||
|
|
||||||
object MatchRules {
|
|
||||||
fun visitRoot(ctx: RuleProcessingContext, node: Node.MatchAll): MListAll {
|
|
||||||
val results = mutableListOf<MAnything<Boolean>>()
|
|
||||||
for (rule in node.list) {
|
|
||||||
val result = mNode(ctx, rule)
|
|
||||||
results.add(result)
|
|
||||||
if (!result.value) break
|
|
||||||
}
|
|
||||||
return MListAll(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mNode(ctx: RuleProcessingContext, node: Node): MAnything<Boolean> = when(node) {
|
|
||||||
is Node.MatchValueList -> mMatchList(ctx, node)
|
|
||||||
is Node.MatchParam -> mParam(ctx, node)
|
|
||||||
is Node.SetParam -> mParamSet(ctx, node)
|
|
||||||
is Node.Negate -> mNegate(ctx, node)
|
|
||||||
else -> node.error("match type not implemented: ${node::class.java.name.quoted}").left
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mNegate(ctx: RuleProcessingContext, node: Node.Negate) = MNegated(mNode(ctx, node.node))
|
|
||||||
|
|
||||||
fun mMatchList(ctx: RuleProcessingContext, node: Node.MatchValueList) = node.values.map { value ->
|
|
||||||
when (node.matchSource) {
|
|
||||||
Node.MatchSource.BLOCK_CLASS -> mBlockClass(ctx, node, value)
|
|
||||||
Node.MatchSource.BLOCK_NAME -> mBlockName(ctx, node, value)
|
|
||||||
Node.MatchSource.MODEL_LOCATION -> mModel(ctx, node, value)
|
|
||||||
}
|
|
||||||
}.let { MListAny(it) }
|
|
||||||
|
|
||||||
fun mBlockClass(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
|
||||||
val blockClass = ctx.discovery.blockState.block::class.java.let {
|
|
||||||
MValue.right(it, "block class ${it.name.quoted}", node.configSource)
|
|
||||||
}
|
|
||||||
val target = when(value) {
|
|
||||||
is Node.Value.Literal -> value.asEither.mapNotNull(
|
|
||||||
func = { tryDefault(null) { Class.forName(it) }},
|
|
||||||
dLeft = { "missing class ${it.value}" },
|
|
||||||
dRight = { m, _ -> " class ${m.value}" }
|
|
||||||
)
|
|
||||||
is Node.Value.ClassOf -> value.asEither.mapValue(::ResourceLocation).mapNotNull(
|
|
||||||
func = { loc -> ForgeRegistries.BLOCKS.getValue(loc)?.let { it::class.java } },
|
|
||||||
dLeft = { "missing block ${it.value.toString().quoted}" },
|
|
||||||
dRight = { m, r -> "class ${r.name.quoted} of block ${m.value}" }
|
|
||||||
)
|
|
||||||
else -> value.invalidTypeFor("block class")
|
|
||||||
}
|
|
||||||
return when(node.matchMethod) {
|
|
||||||
EXACT_MATCH -> MComparison.equals.compare(blockClass, target)
|
|
||||||
EXTENDS -> classExtends.compare(blockClass, target)
|
|
||||||
CONTAINS -> node.error("invalid match type for block class: contains").left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mBlockName(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
|
||||||
val blockName = MValue.right(ctx.discovery.blockState.block, "", node.configSource).mapNotNull(
|
|
||||||
func = { it.registryName }, dLeft = { "missing block name" }, dRight = { _, r -> "block name ${r.toString().quoted}" }
|
|
||||||
)
|
|
||||||
val target = when(value) {
|
|
||||||
is Node.Value.Literal -> value.asEither.map(::splitLocation, ::quoteString)
|
|
||||||
else -> value.invalidTypeFor("block name")
|
|
||||||
}
|
|
||||||
return when(node.matchMethod) {
|
|
||||||
EXACT_MATCH -> blockNameExact.compare(blockName, target)
|
|
||||||
CONTAINS -> blockNameContains.compare(blockName, target)
|
|
||||||
EXTENDS -> node.error("invalid match type for block name: extends").left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mModel(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
|
||||||
val model = (ctx.discovery.getUnbaked() as? BlockModel)?.let {
|
|
||||||
MValue.right(it, "model ${it.name.quoted}", node.configSource)
|
|
||||||
} ?: node.error("unsupported model type: ${ctx.discovery.getUnbaked()::class.java.name.quoted}")
|
|
||||||
|
|
||||||
val target = when(value) {
|
|
||||||
is Node.Value.Literal -> value.asEither.map(::splitLocation, ::quoteString)
|
|
||||||
else -> value.invalidTypeFor("model")
|
|
||||||
}
|
|
||||||
val models = when(node.matchMethod) {
|
|
||||||
EXTENDS -> model.mapValue { ctx.discovery.loadHierarchy(it).ancestors() }
|
|
||||||
else -> model.mapValue { listOf(it) }
|
|
||||||
}
|
|
||||||
return when(node.matchMethod) {
|
|
||||||
EXACT_MATCH, EXTENDS -> anyModel(node.matchMethod, ::locationMatches)
|
|
||||||
CONTAINS -> anyModel(CONTAINS, ::locationContains)
|
|
||||||
}.compare(models, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mParam(ctx: RuleProcessingContext, node: Node.MatchParam) = node.values.map { value ->
|
|
||||||
val paramValue = ctx.params[node.name] ?.let {
|
|
||||||
MValue.right(it, "parameter ${node.name.quoted}", node.configSource, immutable = false)
|
|
||||||
} ?: node.error("missing parameter ${node.name.quoted}")
|
|
||||||
|
|
||||||
val target = when(value) {
|
|
||||||
is Node.Value.Literal -> value.asEither.mapDescription { it.description.quoted }
|
|
||||||
else -> value.invalidTypeFor("parameter")
|
|
||||||
}
|
|
||||||
MComparison.equals.compare(paramValue, target)
|
|
||||||
}.let { MListAny(it) }
|
|
||||||
|
|
||||||
fun mParamSet(ctx: RuleProcessingContext, node: Node.SetParam): MValue<Boolean> {
|
|
||||||
val target = when(node.value) {
|
|
||||||
is Node.Value.Literal -> node.value.asEither
|
|
||||||
is Node.Value.Texture -> when(val model = ctx.discovery.getUnbaked()) {
|
|
||||||
is BlockModel -> node.value.asEither.map(
|
|
||||||
func = { model.getMaterial(it).texture().toString() },
|
|
||||||
description = { m, r -> "texture \"${m.value}\" = \"$r\" of model ${model.name}"}
|
|
||||||
)
|
|
||||||
else -> node.error("unsupported model type: ${model::class.java.name.quoted}")
|
|
||||||
}
|
|
||||||
is Node.Value.Tint -> when(val model = ctx.discovery.getUnbaked()) {
|
|
||||||
is BlockModel -> node.value.asEither.mapNotNull(
|
|
||||||
func = { model.tintOf(it)?.toString() },
|
|
||||||
dRight = { m, r -> "tint index $r for sprite ${m.value}" },
|
|
||||||
dLeft = { m -> "tint index -1 for unused sprite ${m.value}"}
|
|
||||||
).toRight("-1")
|
|
||||||
|
|
||||||
else -> node.error("unsupported model type: ${model::class.java.name.quoted}")
|
|
||||||
}
|
|
||||||
else -> node.value.invalidTypeFor("prameter")
|
|
||||||
}
|
|
||||||
target.ifRight { ctx.params[node.name] = it.value }
|
|
||||||
return target.mapDescription { m -> "parameter ${node.name} set to ${m.value}" }.mapValue { true }.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val classExtends = MComparison.of<Class<*>, Class<*>>(EXTENDS) { c1, c2 -> c2.isAssignableFrom(c1) }
|
|
||||||
|
|
||||||
private val blockNameExact = MComparison.of<ResourceLocation, PartialLocation>(EXACT_MATCH) { block, partial ->
|
|
||||||
locationMatches(block, partial)
|
|
||||||
}
|
|
||||||
private val blockNameContains = MComparison.of<ResourceLocation, Pair<String?, String>>(CONTAINS) { block, partial ->
|
|
||||||
locationContains(block, partial)
|
|
||||||
}
|
|
||||||
private fun anyModel(matchMethod: MatchMethod, func: (ResourceLocation, PartialLocation)->Boolean) =
|
|
||||||
MComparison.of<List<BlockModel>, PartialLocation>(matchMethod) { models, partial ->
|
|
||||||
models.any { func(ResourceLocation(it.name), partial) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun locationMatches(loc: ResourceLocation, partial: PartialLocation) =
|
|
||||||
(partial.first == null || loc.namespace == partial.first) && loc.path == partial.second
|
|
||||||
fun locationContains(loc: ResourceLocation, partial: PartialLocation) =
|
|
||||||
(partial.first == null || loc.namespace.contains(partial.first!!)) && loc.path.contains(partial.second)
|
|
||||||
|
|
||||||
fun splitLocation(str: String): PartialLocation =
|
|
||||||
if (str.contains(":")) ResourceLocation(str).let { it.namespace to it.path } else null to str
|
|
||||||
|
|
||||||
fun <T, R> quoteString(mValue: MValue<T>, newValue: R) = mValue.description.quoted
|
|
||||||
|
|
||||||
fun BlockModel.ancestors(): List<BlockModel> = if (parent == null) listOf(this) else parent!!.ancestors() + this
|
|
||||||
|
|
||||||
fun BlockModel.tintOf(spriteName: String) =
|
|
||||||
elements.findFirst { element ->
|
|
||||||
element.faces.entries.findFirst { (_, face) ->
|
|
||||||
if (face.texture == "#$spriteName") face.tintIndex else null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package mods.betterfoliage.integration
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.util.ThreadLocalDelegate
|
|
||||||
import mods.betterfoliage.util.allAvailable
|
|
||||||
import mods.betterfoliage.util.reflectField
|
|
||||||
import mods.betterfoliage.BlockPos
|
|
||||||
import mods.betterfoliage.BlockState
|
|
||||||
import mods.betterfoliage.CustomColors
|
|
||||||
import mods.betterfoliage.RenderEnv
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.Minecraft
|
|
||||||
import net.minecraft.client.renderer.model.BakedQuad
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.level.ColorResolver
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration for OptiFine custom block colors.
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
object OptifineCustomColors {
|
|
||||||
val logger = LogManager.getLogger(this)
|
|
||||||
|
|
||||||
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
|
|
||||||
|
|
||||||
init {
|
|
||||||
logger.log(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)
|
|
||||||
|
|
||||||
fun getBlockColor(ctx: BlockCtx, resolver: ColorResolver): Int {
|
|
||||||
val ofColor = if (isColorAvailable && Minecraft.getInstance().options.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.color(resolver) 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,98 +0,0 @@
|
|||||||
package mods.betterfoliage.integration
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BlockAliases
|
|
||||||
import mods.betterfoliage.BufferBuilder_sVertexBuilder
|
|
||||||
import mods.betterfoliage.SVertexBuilder
|
|
||||||
import mods.betterfoliage.Shaders
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
|
||||||
import mods.betterfoliage.util.allAvailable
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.mapArray
|
|
||||||
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.Direction
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.Direction.EAST
|
|
||||||
import net.minecraft.util.Direction.NORTH
|
|
||||||
import net.minecraft.util.Direction.SOUTH
|
|
||||||
import net.minecraft.util.Direction.WEST
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockDisplayReader
|
|
||||||
import net.minecraftforge.client.model.pipeline.LightUtil
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration for ShadersMod.
|
|
||||||
*/
|
|
||||||
object ShadersModIntegration : HasLogger() {
|
|
||||||
@JvmStatic val isEffectsAvailable = allAvailable(SVertexBuilder.pushState, SVertexBuilder.popState, BlockAliases.getAliasBlockId)
|
|
||||||
@JvmStatic val isDiffuseAvailable = allAvailable(Shaders.shaderPackLoaded, Shaders.blockLightLevel05, Shaders.blockLightLevel06, Shaders.blockLightLevel08)
|
|
||||||
|
|
||||||
@JvmStatic val defaultLeaves = Blocks.OAK_LEAVES.defaultBlockState()
|
|
||||||
@JvmStatic val defaultGrass = Blocks.GRASS.defaultBlockState()
|
|
||||||
|
|
||||||
@JvmStatic var diffuseShades = Direction.values().mapArray { LightUtil.diffuseLight(it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called from transformed ShadersMod code.
|
|
||||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
|
||||||
*/
|
|
||||||
@JvmStatic fun getBlockStateOverride(state: BlockState, world: IBlockDisplayReader, pos: BlockPos): BlockState {
|
|
||||||
if (state in BetterFoliage.blockTypes.leaf) return defaultLeaves
|
|
||||||
// if (BlockConfig.crops.matchesClass(state.block)) return defaultGrass
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
logger.log(INFO, "ShadersMod diffuse shading integration is ${if (isDiffuseAvailable) "enabled" else "disabled" }")
|
|
||||||
logger.log(INFO, "ShadersMod vertex shader integration is ${if (isEffectsAvailable) "enabled" else "disabled" }")
|
|
||||||
|
|
||||||
// Recalculate the diffuse shading values used when resources are reloaded
|
|
||||||
if (isDiffuseAvailable) BetterFoliage.modelManager.onInvalidate {
|
|
||||||
if (Shaders.shaderPackLoaded.getStatic()) {
|
|
||||||
diffuseShades = Direction.values().mapArray { face ->
|
|
||||||
when(face) {
|
|
||||||
DOWN -> Shaders.blockLightLevel05.getStatic()
|
|
||||||
WEST, EAST -> Shaders.blockLightLevel06.getStatic()
|
|
||||||
NORTH, SOUTH -> Shaders.blockLightLevel08.getStatic()
|
|
||||||
else -> LightUtil.diffuseLight(face)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
diffuseShades = Direction.values().mapArray { LightUtil.diffuseLight(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
|
||||||
inline fun renderAs(buffer: BufferBuilder, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) {
|
|
||||||
if (isEffectsAvailable && enabled) {
|
|
||||||
val aliasBlockId = BlockAliases.getAliasBlockId.invokeStatic(state)
|
|
||||||
val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder]
|
|
||||||
SVertexBuilder.pushState.invoke(sVertexBuilder, aliasBlockId)
|
|
||||||
func()
|
|
||||||
SVertexBuilder.popState.invoke(sVertexBuilder)
|
|
||||||
} else {
|
|
||||||
func()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
|
|
||||||
inline fun grass(ctx: RenderCtxBase, enabled: Boolean = true, func: ()->Unit) =
|
|
||||||
((ctx as? RenderCtxVanilla)?.buffer as? BufferBuilder)?.let { bufferBuilder ->
|
|
||||||
renderAs(bufferBuilder, defaultGrass, MODEL, enabled, func)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
|
|
||||||
inline fun leaves(ctx: RenderCtxBase, enabled: Boolean = true, func: ()->Unit) =
|
|
||||||
((ctx as? RenderCtxVanilla)?.buffer as? BufferBuilder)?.let { bufferBuilder ->
|
|
||||||
renderAs(bufferBuilder, defaultLeaves, MODEL, enabled, func)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.WrappedLayerPredicate
|
|
||||||
import mods.betterfoliage.render.pipeline.layerPredicate
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
|
||||||
import mods.betterfoliage.util.Double3
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
|
||||||
import mods.betterfoliage.util.directionsAndNull
|
|
||||||
import mods.betterfoliage.util.mapArray
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.client.renderer.RenderTypeLookup
|
|
||||||
import net.minecraft.client.renderer.model.BakedQuad
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel
|
|
||||||
import net.minecraft.client.renderer.model.SimpleBakedModel
|
|
||||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
|
||||||
import net.minecraft.client.renderer.vertex.VertexFormatElement
|
|
||||||
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hybrid baked quad implementation, carrying both baked and unbaked information.
|
|
||||||
* Used to do advanced vertex lighting without unbaking vertex data at lighting time.
|
|
||||||
*/
|
|
||||||
data class HalfBakedQuad(
|
|
||||||
val raw: Quad,
|
|
||||||
val baked: BakedQuad
|
|
||||||
)
|
|
||||||
|
|
||||||
open class HalfBakedSimpleModelWrapper(baseModel: SimpleBakedModel): IBakedModel by baseModel, SpecialRenderModel {
|
|
||||||
val baseQuads = baseModel.unbakeQuads()
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random) = Unit
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
// if the passed data is a BlockState, render on the same layer(s) as that block
|
|
||||||
val testState = (data as? BlockState) ?: ctx.state
|
|
||||||
|
|
||||||
// this could get called for more layers than the underlying model is on
|
|
||||||
// ignore extra decoration layers
|
|
||||||
val shouldRender = when(val predicate = testState.block.layerPredicate) {
|
|
||||||
is WrappedLayerPredicate -> predicate.original.test(layer)
|
|
||||||
else -> RenderTypeLookup.canRenderInLayer(testState, layer)
|
|
||||||
}
|
|
||||||
if (shouldRender) ctx.renderQuads(baseQuads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): SpecialRenderModel by baseModel {
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() {
|
|
||||||
override fun bake(ctx: ModelBakingContext): IBakedModel? {
|
|
||||||
val baseModel = super.bake(ctx)
|
|
||||||
val halfBaked = when(baseModel) {
|
|
||||||
is SimpleBakedModel -> HalfBakedSimpleModelWrapper(baseModel)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return if (halfBaked == null) baseModel else bake(ctx, halfBaked)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel
|
|
||||||
}
|
|
||||||
|
|
||||||
fun List<Quad>.bake(applyDiffuseLighting: Boolean) = map { quad ->
|
|
||||||
if (quad.sprite == null) throw IllegalStateException("Quad must have a texture assigned before baking")
|
|
||||||
val builder = BakedQuadBuilder(quad.sprite)
|
|
||||||
builder.setApplyDiffuseLighting(applyDiffuseLighting)
|
|
||||||
builder.setQuadOrientation(quad.face())
|
|
||||||
builder.setQuadTint(quad.colorIndex)
|
|
||||||
quad.verts.forEach { vertex ->
|
|
||||||
DefaultVertexFormats.BLOCK.elements.forEachIndexed { idx, element ->
|
|
||||||
when {
|
|
||||||
element.usage == VertexFormatElement.Usage.POSITION -> builder.put(idx,
|
|
||||||
(vertex.xyz.x + 0.5).toFloat(),
|
|
||||||
(vertex.xyz.y + 0.5).toFloat(),
|
|
||||||
(vertex.xyz.z + 0.5).toFloat(),
|
|
||||||
1.0f
|
|
||||||
)
|
|
||||||
// don't fill lightmap UV coords
|
|
||||||
element.usage == VertexFormatElement.Usage.UV && element.type == VertexFormatElement.Type.FLOAT -> builder.put(idx,
|
|
||||||
quad.sprite.u0 + (quad.sprite.u1 - quad.sprite.u0) * (vertex.uv.u + 0.5).toFloat(),
|
|
||||||
quad.sprite.v0 + (quad.sprite.v1 - quad.sprite.v0) * (vertex.uv.v + 0.5).toFloat(),
|
|
||||||
0.0f, 1.0f
|
|
||||||
)
|
|
||||||
element.usage == VertexFormatElement.Usage.COLOR -> builder.put(idx,
|
|
||||||
(vertex.color.red and 255).toFloat() / 255.0f,
|
|
||||||
(vertex.color.green and 255).toFloat() / 255.0f,
|
|
||||||
(vertex.color.blue and 255).toFloat() / 255.0f,
|
|
||||||
(vertex.color.alpha and 255).toFloat() / 255.0f
|
|
||||||
)
|
|
||||||
element.usage == VertexFormatElement.Usage.NORMAL -> builder.put(idx,
|
|
||||||
(vertex.normal ?: quad.normal).x.toFloat(),
|
|
||||||
(vertex.normal ?: quad.normal).y.toFloat(),
|
|
||||||
(vertex.normal ?: quad.normal).z.toFloat(),
|
|
||||||
0.0f
|
|
||||||
)
|
|
||||||
else -> builder.put(idx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HalfBakedQuad(quad, builder.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Array<List<Quad>>.bake(applyDiffuseLighting: Boolean) = mapArray { it.bake(applyDiffuseLighting) }
|
|
||||||
|
|
||||||
fun BakedQuad.unbake(): HalfBakedQuad {
|
|
||||||
val size = DefaultVertexFormats.BLOCK.integerSize
|
|
||||||
val verts = Array(4) { vIdx ->
|
|
||||||
val x = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 0]) - 0.5f
|
|
||||||
val y = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 1]) - 0.5f
|
|
||||||
val z = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 2]) - 0.5f
|
|
||||||
val color = vertices[vIdx * size + 3]
|
|
||||||
val u = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 4])
|
|
||||||
val v = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 5])
|
|
||||||
Vertex(Double3(x, y, z), UV(u.toDouble(), v.toDouble()), Color(color))
|
|
||||||
}
|
|
||||||
val unbaked = Quad(
|
|
||||||
verts[0], verts[1], verts[2], verts[3],
|
|
||||||
colorIndex = if (isTinted) tintIndex else -1,
|
|
||||||
face = direction
|
|
||||||
)
|
|
||||||
return HalfBakedQuad(unbaked, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SimpleBakedModel.unbakeQuads() = directionsAndNull.flatMap { face ->
|
|
||||||
getQuads(null, face, Random()).map { it.unbake() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.Double3
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.boxFaces
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.minmax
|
|
||||||
import mods.betterfoliage.util.nearestAngle
|
|
||||||
import mods.betterfoliage.util.rotate
|
|
||||||
import mods.betterfoliage.util.times
|
|
||||||
import mods.betterfoliage.util.vec
|
|
||||||
import net.minecraft.client.renderer.texture.NativeImage
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.Direction
|
|
||||||
import java.lang.Math.max
|
|
||||||
import java.lang.Math.min
|
|
||||||
import java.util.Random
|
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vertex UV coordinates
|
|
||||||
*
|
|
||||||
* Zero-centered: coordinates fall between (-0.5, 0.5) (inclusive)
|
|
||||||
*/
|
|
||||||
data class UV(val u: Double, val v: Double) {
|
|
||||||
companion object {
|
|
||||||
val topLeft = UV(-0.5, -0.5)
|
|
||||||
val topRight = UV(0.5, -0.5)
|
|
||||||
val bottomLeft = UV(-0.5, 0.5)
|
|
||||||
val bottomRight = UV(0.5, 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
val rotate: UV get() = UV(v, -u)
|
|
||||||
|
|
||||||
fun rotate(n: Int) = when (n % 4) {
|
|
||||||
0 -> copy()
|
|
||||||
1 -> UV(v, -u)
|
|
||||||
2 -> UV(-u, -v)
|
|
||||||
else -> UV(-v, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clamp(minU: Double = -0.5, maxU: Double = 0.5, minV: Double = -0.5, maxV: Double = 0.5) =
|
|
||||||
UV(u.minmax(minU, maxU), v.minmax(minV, maxV))
|
|
||||||
|
|
||||||
fun mirror(mirrorU: Boolean, mirrorV: Boolean) = UV(if (mirrorU) -u else u, if (mirrorV) -v else v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model vertex
|
|
||||||
*
|
|
||||||
* @param[xyz] x, y, z coordinates
|
|
||||||
* @param[uv] u, v coordinates
|
|
||||||
* @param[aoShader] [ModelLighter] instance to use with AO rendering
|
|
||||||
* @param[flatShader] [ModelLighter] instance to use with non-AO rendering
|
|
||||||
*/
|
|
||||||
data class Vertex(
|
|
||||||
val xyz: Double3 = Double3(0.0, 0.0, 0.0),
|
|
||||||
val uv: UV = UV(0.0, 0.0),
|
|
||||||
val color: Color = Color.white,
|
|
||||||
val normal: Double3? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Color(val alpha: Int, val red: Int, val green: Int, val blue: Int) {
|
|
||||||
constructor(combined: Int) : this(
|
|
||||||
combined shr 24 and 255,
|
|
||||||
combined shr 16 and 255,
|
|
||||||
combined shr 8 and 255,
|
|
||||||
combined and 255
|
|
||||||
)
|
|
||||||
|
|
||||||
val asInt get() = (alpha shl 24) or (red shl 16) or (green shl 8) or blue
|
|
||||||
val asHSB get() = HSB.fromColor(this)
|
|
||||||
|
|
||||||
operator fun times(f: Float) = Color(
|
|
||||||
alpha,
|
|
||||||
(f * red.toFloat()).toInt().coerceIn(0 until 256),
|
|
||||||
(f * green.toFloat()).toInt().coerceIn(0 until 256),
|
|
||||||
(f * blue.toFloat()).toInt().coerceIn(0 until 256)
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val white get() = Color(255, 255, 255, 255)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
|
||||||
companion object {
|
|
||||||
/** Red is assumed to be LSB, see [NativeImage.PixelFormat.RGBA] */
|
|
||||||
fun fromColorRGBA(color: Int): HSB {
|
|
||||||
val hsbVals = java.awt.Color.RGBtoHSB(color and 255, (color shr 8) and 255, (color shr 16) and 255, null)
|
|
||||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
|
||||||
}
|
|
||||||
fun fromColorBGRA(color: Int): HSB {
|
|
||||||
val hsbVals = java.awt.Color.RGBtoHSB((color shr 16) and 255, (color shr 8) and 255, color and 255, null)
|
|
||||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
|
||||||
}
|
|
||||||
fun fromColor(color: Color): HSB {
|
|
||||||
val hsbVals = java.awt.Color.RGBtoHSB(color.red, color.green, color.blue, null)
|
|
||||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val asInt: Int get() = java.awt.Color.HSBtoRGB(hue, saturation, brightness)
|
|
||||||
val asColor: Color get() = Color(asInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intermediate representation of model quad
|
|
||||||
* Immutable, double-precision
|
|
||||||
* Zero-centered (both XYZ and UV) coordinates for simpler rotation/mirroring
|
|
||||||
*/
|
|
||||||
data class Quad(
|
|
||||||
val v1: Vertex, val v2: Vertex, val v3: Vertex, val v4: Vertex,
|
|
||||||
val sprite: TextureAtlasSprite? = null,
|
|
||||||
val colorIndex: Int = -1,
|
|
||||||
val face: Direction? = null
|
|
||||||
) {
|
|
||||||
val verts = arrayOf(v1, v2, v3, v4)
|
|
||||||
|
|
||||||
inline fun transformV(trans: (Vertex) -> Vertex): Quad = transformVI { vertex, idx -> trans(vertex) }
|
|
||||||
inline fun transformVI(trans: (Vertex, Int) -> Vertex): Quad = copy(
|
|
||||||
v1 = trans(v1, 0), v2 = trans(v2, 1), v3 = trans(v3, 2), v4 = trans(v4, 3)
|
|
||||||
)
|
|
||||||
|
|
||||||
val normal: Double3 get() = (v2.xyz - v1.xyz).cross(v4.xyz - v1.xyz).normalize
|
|
||||||
|
|
||||||
fun move(trans: Double3) = transformV { it.copy(xyz = it.xyz + trans) }
|
|
||||||
fun move(trans: Pair<Double, Direction>) = move(Double3(trans.second) * trans.first)
|
|
||||||
fun scale(scale: Double) = transformV { it.copy(xyz = it.xyz * scale) }
|
|
||||||
fun scale(scale: Double3) =
|
|
||||||
transformV { it.copy(xyz = Double3(it.xyz.x * scale.x, it.xyz.y * scale.y, it.xyz.z * scale.z)) }
|
|
||||||
|
|
||||||
fun rotate(rot: Rotation) =
|
|
||||||
transformV { it.copy(xyz = it.xyz.rotate(rot), normal = it.normal?.rotate(rot)) }.copy(face = face?.rotate(rot))
|
|
||||||
|
|
||||||
fun rotateZ(angle: Double) = transformV {
|
|
||||||
it.copy(
|
|
||||||
xyz = Double3(
|
|
||||||
it.xyz.x * cos(angle) + it.xyz.z * sin(angle),
|
|
||||||
it.xyz.y,
|
|
||||||
it.xyz.z * cos(angle) - it.xyz.x * sin(angle)
|
|
||||||
),
|
|
||||||
normal = it.normal?.let { normal ->
|
|
||||||
Double3(
|
|
||||||
normal.x * cos(angle) + normal.z * sin(angle),
|
|
||||||
normal.y,
|
|
||||||
normal.z * cos(angle) - normal.x * sin(angle)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scaleUV(scale: Double) = transformV { it.copy(uv = UV(it.uv.u * scale, it.uv.v * scale)) }
|
|
||||||
fun rotateUV(n: Int) = transformV { it.copy(uv = it.uv.rotate(n)) }
|
|
||||||
fun clampUV(minU: Double = -0.5, maxU: Double = 0.5, minV: Double = -0.5, maxV: Double = 0.5) =
|
|
||||||
transformV { it.copy(uv = it.uv.clamp(minU, maxU, minV, maxV)) }
|
|
||||||
fun mirrorUV(mirrorU: Boolean, mirrorV: Boolean) = transformV { it.copy(uv = it.uv.mirror(mirrorU, mirrorV)) }
|
|
||||||
fun scrambleUV(random: Random, canFlipU: Boolean, canFlipV: Boolean, canRotate: Boolean) = this
|
|
||||||
.mirrorUV(canFlipU && random.nextBoolean(), canFlipV && random.nextBoolean())
|
|
||||||
.let { if (canRotate) it.rotateUV(random.nextInt(4)) else it }
|
|
||||||
|
|
||||||
fun sprite(sprite: TextureAtlasSprite) = copy(sprite = sprite)
|
|
||||||
fun color(color: Color) = transformV { it.copy(color = color) }
|
|
||||||
fun color(color: Int) = transformV { it.copy(color = Color(color)) }
|
|
||||||
fun colorIndex(colorIndex: Int) = copy(colorIndex = colorIndex)
|
|
||||||
fun colorAndIndex(color: Color?) = color(color ?: Color.white).colorIndex(if (color == null) 0 else -1)
|
|
||||||
|
|
||||||
fun face() = face ?: nearestAngle(normal, Direction.values().toList()) { it.vec }.first
|
|
||||||
|
|
||||||
val flipped: Quad get() = Quad(v4, v3, v2, v1, sprite, colorIndex)
|
|
||||||
fun cycleVertices(n: Int) = when (n % 4) {
|
|
||||||
1 -> Quad(v2, v3, v4, v1)
|
|
||||||
2 -> Quad(v3, v4, v1, v2)
|
|
||||||
3 -> Quad(v4, v1, v2, v3)
|
|
||||||
else -> this.copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun mix(first: Quad, second: Quad, vertexFactory: (Vertex, Vertex) -> Vertex) = Quad(
|
|
||||||
v1 = vertexFactory(first.v1, second.v1),
|
|
||||||
v2 = vertexFactory(first.v2, second.v2),
|
|
||||||
v3 = vertexFactory(first.v3, second.v3),
|
|
||||||
v4 = vertexFactory(first.v4, second.v4)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun verticalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, yBottom: Double, yTop: Double) = Quad(
|
|
||||||
Vertex(Double3(x1, yBottom, z1), UV.bottomLeft),
|
|
||||||
Vertex(Double3(x2, yBottom, z2), UV.bottomRight),
|
|
||||||
Vertex(Double3(x2, yTop, z2), UV.topRight),
|
|
||||||
Vertex(Double3(x1, yTop, z1), UV.topLeft)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun horizontalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, y: Double): Quad {
|
|
||||||
val xMin = min(x1, x2);
|
|
||||||
val xMax = max(x1, x2)
|
|
||||||
val zMin = min(z1, z2);
|
|
||||||
val zMax = max(z1, z2)
|
|
||||||
return Quad(
|
|
||||||
Vertex(Double3(xMin, y, zMin), UV.topLeft),
|
|
||||||
Vertex(Double3(xMin, y, zMax), UV.bottomLeft),
|
|
||||||
Vertex(Double3(xMax, y, zMax), UV.bottomRight),
|
|
||||||
Vertex(Double3(xMax, y, zMin), UV.topRight)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun faceQuad(face: Direction): Quad {
|
|
||||||
val base = face.vec * 0.5
|
|
||||||
val top = boxFaces[face].top * 0.5
|
|
||||||
val left = boxFaces[face].left * 0.5
|
|
||||||
return Quad(
|
|
||||||
Vertex(base + top + left, UV.topLeft),
|
|
||||||
Vertex(base - top + left, UV.bottomLeft),
|
|
||||||
Vertex(base - top - left, UV.bottomRight),
|
|
||||||
Vertex(base + top - left, UV.topRight)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel
|
|
||||||
import net.minecraft.client.renderer.model.WeightedBakedModel
|
|
||||||
import net.minecraft.util.WeightedRandom
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model that makes use of advanced rendering features.
|
|
||||||
*/
|
|
||||||
interface SpecialRenderModel : IBakedModel {
|
|
||||||
/**
|
|
||||||
* Create custom renderdata object. Called once per block. Result is passed to renderLayer().
|
|
||||||
*/
|
|
||||||
fun prepare(ctx: BlockCtx, random: Random): Any
|
|
||||||
fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the actual model that will be rendered. Useful for container models (like [WeightedBakedModel]).
|
|
||||||
*/
|
|
||||||
fun resolve(random: Random) = this
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SpecialRenderData {
|
|
||||||
fun canRenderInLayer(layer: RenderType) = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class WeightedModelWrapper(
|
|
||||||
val models: List<WeightedModel>, baseModel: SpecialRenderModel
|
|
||||||
) : IBakedModel by baseModel, SpecialRenderModel {
|
|
||||||
class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight)
|
|
||||||
|
|
||||||
val totalWeight = models.sumBy { it.weight }
|
|
||||||
|
|
||||||
fun getModel(random: Random) = WeightedRandom.getWeightedItem(models, random.nextInt(totalWeight))
|
|
||||||
|
|
||||||
override fun resolve(random: Random) = getModel(random).model.resolve(random)
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random) = getModel(random).model.let { actual ->
|
|
||||||
WeightedRenderData(actual, actual.prepare(ctx, random))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) = when (data) {
|
|
||||||
is WeightedRenderData -> data.model.renderLayer(ctx, data.modelRenderData, layer)
|
|
||||||
else -> getModel(ctx.random).model.renderLayer(ctx, data, layer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class WeightedRenderData(
|
|
||||||
val model: SpecialRenderModel,
|
|
||||||
val modelRenderData: Any
|
|
||||||
) : SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = (modelRenderData as? SpecialRenderData)?.canRenderInLayer(layer) ?: false
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.resourceManager
|
|
||||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
interface SpriteSet {
|
|
||||||
val num: Int
|
|
||||||
operator fun get(idx: Int): TextureAtlasSprite
|
|
||||||
}
|
|
||||||
|
|
||||||
class FixedSpriteSet(val sprites: List<TextureAtlasSprite>) : SpriteSet {
|
|
||||||
override val num = sprites.size
|
|
||||||
override fun get(idx: Int) = sprites[idx % num]
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpriteDelegate(val atlas: Atlas, val idFunc: () -> ResourceLocation) : ReadOnlyProperty<Any, TextureAtlasSprite> {
|
|
||||||
private lateinit var id: ResourceLocation
|
|
||||||
private var value: TextureAtlasSprite? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
BetterFoliageMod.bus.register(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
|
||||||
id = idFunc(); value = null
|
|
||||||
event.addSprite(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any, property: KProperty<*>): TextureAtlasSprite {
|
|
||||||
value?.let { return it }
|
|
||||||
synchronized(this) {
|
|
||||||
value?.let { return it }
|
|
||||||
atlas[id].let { value = it; return it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpriteSetDelegate(
|
|
||||||
val atlas: Atlas,
|
|
||||||
val idRegister: (ResourceLocation) -> ResourceLocation = { it },
|
|
||||||
val idFunc: (Int) -> ResourceLocation
|
|
||||||
) : ReadOnlyProperty<Any, SpriteSet> {
|
|
||||||
private var idList: List<ResourceLocation> = emptyList()
|
|
||||||
private var spriteSet: SpriteSet? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
BetterFoliageMod.bus.register(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
|
||||||
if (event.map.location() != atlas.resourceId) return
|
|
||||||
spriteSet = null
|
|
||||||
idList = (0 until 16)
|
|
||||||
.map(idFunc)
|
|
||||||
.filter { resourceManager.hasResource(atlas.file(it)) }
|
|
||||||
.map(idRegister)
|
|
||||||
idList.forEach { event.addSprite(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any, property: KProperty<*>): SpriteSet {
|
|
||||||
spriteSet?.let { return it }
|
|
||||||
synchronized(this) {
|
|
||||||
spriteSet?.let { return it }
|
|
||||||
spriteSet = FixedSpriteSet(
|
|
||||||
idList
|
|
||||||
.ifEmpty { listOf(MissingTextureSprite.getLocation()) }
|
|
||||||
.map { atlas[it] }
|
|
||||||
)
|
|
||||||
return spriteSet!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Double3
|
|
||||||
import mods.betterfoliage.util.PI2
|
|
||||||
import mods.betterfoliage.util.allDirections
|
|
||||||
import mods.betterfoliage.util.random
|
|
||||||
import mods.betterfoliage.util.randomB
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import mods.betterfoliage.util.rot
|
|
||||||
import mods.betterfoliage.util.vec
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx.toDouble() / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
|
|
||||||
|
|
||||||
data class TuftShapeKey(
|
|
||||||
val size: Double,
|
|
||||||
val height: Double,
|
|
||||||
val offset: Double3,
|
|
||||||
val flipU1: Boolean,
|
|
||||||
val flipU2: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
fun tuftShapeSet(size: Double, heightMin: Double, heightMax: Double, hOffset: Double): Array<TuftShapeKey> {
|
|
||||||
return Array(64) { idx ->
|
|
||||||
TuftShapeKey(
|
|
||||||
size,
|
|
||||||
randomD(heightMin, heightMax),
|
|
||||||
xzDisk(idx) * randomD(hOffset / 2.0, hOffset),
|
|
||||||
randomB(),
|
|
||||||
randomB()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun tuftQuadSingle(size: Double, height: Double, flipU: Boolean) =
|
|
||||||
Quad.verticalRectangle(
|
|
||||||
x1 = -0.5 * size,
|
|
||||||
z1 = 0.5 * size,
|
|
||||||
x2 = 0.5 * size,
|
|
||||||
z2 = -0.5 * size,
|
|
||||||
yBottom = 0.5,
|
|
||||||
yTop = 0.5 + height
|
|
||||||
)
|
|
||||||
.mirrorUV(flipU, false)
|
|
||||||
|
|
||||||
fun tuftModelSet(shapes: Array<TuftShapeKey>, color: Color, tint: Int, spriteGetter: (Int) -> TextureAtlasSprite) =
|
|
||||||
shapes.mapIndexed { idx, shape ->
|
|
||||||
listOf(
|
|
||||||
tuftQuadSingle(shape.size, shape.height, shape.flipU1),
|
|
||||||
tuftQuadSingle(shape.size, shape.height, shape.flipU2).rotate(rot(UP))
|
|
||||||
).map { it.move(shape.offset) }
|
|
||||||
.map { it.color(color).colorIndex(tint) }
|
|
||||||
.map { it.sprite(spriteGetter(idx)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fullCubeTextured(
|
|
||||||
spriteLocation: ResourceLocation,
|
|
||||||
tintIndex: Int,
|
|
||||||
scrambleUV: Boolean = true
|
|
||||||
): List<HalfBakedQuad> {
|
|
||||||
val sprite = Atlas.BLOCKS[spriteLocation]
|
|
||||||
return allDirections.map { Quad.faceQuad(it) }
|
|
||||||
.map { if (!scrambleUV) it else it.rotateUV(randomI(max = 4)) }
|
|
||||||
.map { it.sprite(sprite) }
|
|
||||||
.map { it.colorIndex(tintIndex) }
|
|
||||||
.bake(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): List<List<Quad>> {
|
|
||||||
return (0 until num).map { idx ->
|
|
||||||
listOf(
|
|
||||||
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41),
|
|
||||||
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
|
|
||||||
.rotate(rot(UP))
|
|
||||||
).map { it.scale(size) }
|
|
||||||
.map { it.move(xzDisk(idx) * hOffset) }
|
|
||||||
.map { it.move(UP.vec * randomD(-1.0, 1.0) * vOffset) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, color: Color, tint: Int, scrambleUV: Boolean) =
|
|
||||||
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
|
||||||
.map { it.color(color).colorIndex(tint) }
|
|
||||||
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
|
||||||
.withOpposites()
|
|
||||||
.bake(false)
|
|
||||||
|
|
||||||
fun crossModelsTextured(
|
|
||||||
leafBase: Iterable<List<Quad>>,
|
|
||||||
color: Color, tint: Int,
|
|
||||||
scrambleUV: Boolean,
|
|
||||||
spriteGetter: (Int) -> ResourceLocation
|
|
||||||
) = leafBase.mapIndexed { idx, leaf ->
|
|
||||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], color, tint, scrambleUV)
|
|
||||||
}.toTypedArray()
|
|
||||||
|
|
||||||
fun Iterable<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
|
||||||
fun Iterable<List<Quad>>.buildTufts(applyDiffuseLighting: Boolean = false) =
|
|
||||||
map { it.withOpposites().bake(applyDiffuseLighting) }.toTypedArray()
|
|
||||||
|
|
||||||
fun Iterable<List<Quad>>.transform(trans: Quad.(Int)-> Quad) = mapIndexed { idx, qList -> qList.map { it.trans(idx) } }
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.horizontalDirections
|
|
||||||
import mods.betterfoliage.util.idx
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardCactusDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
ctx.addReplacement(StandardCactusKey)
|
|
||||||
ctx.sprites.add(StandardCactusModel.cactusCrossSprite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardCactusKey : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class CactusRenderData(val armSide: Int, val armIdx: Int, val crossIdx: Int)
|
|
||||||
|
|
||||||
class StandardCactusModel(
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any = when {
|
|
||||||
!Config.enabled || !Config.cactus.enabled -> Unit
|
|
||||||
else -> CactusRenderData(
|
|
||||||
armSide = random.nextInt() and 3,
|
|
||||||
armIdx = random.idx(cactusArmModels),
|
|
||||||
crossIdx = random.idx(cactusCrossModels)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val armLighting = horizontalDirections.map { LightingPreferredFace(it) }.toTypedArray()
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (data is CactusRenderData) {
|
|
||||||
ctx.vertexLighter = armLighting[data.armSide]
|
|
||||||
ctx.renderQuads(cactusArmModels[data.armSide][data.armIdx])
|
|
||||||
ctx.vertexLighter = RoundLeafLighting
|
|
||||||
ctx.renderQuads(cactusCrossModels[data.crossIdx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val cactusCrossSprite = ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus")
|
|
||||||
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx")
|
|
||||||
}
|
|
||||||
val cactusArmModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = Config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
|
|
||||||
val models = tuftModelSet(shapes, Color.white, -1) { cactusArmSprites[randomI()] }
|
|
||||||
horizontalDirections.map { side ->
|
|
||||||
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
val cactusCrossModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val models = Config.cactus.let { config ->
|
|
||||||
crossModelsRaw(64, config.size, 0.0, 0.0)
|
|
||||||
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
|
|
||||||
}
|
|
||||||
crossModelsTextured(models, Color.white, -1, true) { cactusCrossSprite }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
|
||||||
import mods.betterfoliage.config.isSnow
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderData
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.pipeline.Layers
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.extendLayers
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.generated.CenteredSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Int3
|
|
||||||
import mods.betterfoliage.util.getBlockModel
|
|
||||||
import mods.betterfoliage.util.idxOrNull
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.block.material.Material
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardDirtDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
|
||||||
ctx.addReplacement(StandardDirtKey)
|
|
||||||
ctx.blockState.block.extendLayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardDirtKey : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardDirtModel(wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirtRenderData(
|
|
||||||
val connectedGrassModel: SpecialRenderModel?,
|
|
||||||
val algaeIdx: Int?,
|
|
||||||
val reedIdx: Int?
|
|
||||||
) : SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = when {
|
|
||||||
connectedGrassModel != null && layer == Layers.connectedDirt -> true
|
|
||||||
(algaeIdx != null || reedIdx != null) && layer == Layers.tufts -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardDirtModel(
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
val vanillaTuftLighting = LightingPreferredFace(UP)
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
val stateUp = ctx.state(UP)
|
|
||||||
val state2Up = ctx.state(Int3(0, 2, 0))
|
|
||||||
val isConnectedGrass = Config.connectedGrass.enabled &&
|
|
||||||
stateUp in BetterFoliage.blockTypes.grass &&
|
|
||||||
(Config.connectedGrass.snowEnabled || !state2Up.isSnow)
|
|
||||||
|
|
||||||
val isWater = stateUp.material == Material.WATER
|
|
||||||
val isDeepWater = isWater && state2Up.material == Material.WATER
|
|
||||||
val isShallowWater = isWater && state2Up.isAir
|
|
||||||
val isSaltWater = isWater && ctx.biome?.biomeCategory in SALTWATER_BIOMES
|
|
||||||
|
|
||||||
// get the actual grass model to use for connected grass rendering
|
|
||||||
// return null if the grass specifically does not want to connect
|
|
||||||
val connectedGrassModel = if (!isConnectedGrass) null else getBlockModel(stateUp).let { model ->
|
|
||||||
(model as? SpecialRenderModel)?.resolve(random)?.let { grassModel ->
|
|
||||||
if ((grassModel as? StandardGrassModel)?.key?.noConnect == true) null else grassModel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DirtRenderData(
|
|
||||||
connectedGrassModel = connectedGrassModel,
|
|
||||||
algaeIdx = random.idxOrNull(algaeModels) { Config.algae.enabled(random) && isDeepWater && isSaltWater },
|
|
||||||
reedIdx = random.idxOrNull(reedModels) { Config.reed.enabled(random) && isShallowWater && !isSaltWater }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
if (data is DirtRenderData) {
|
|
||||||
if (data.connectedGrassModel != null) {
|
|
||||||
ctx.renderMasquerade(UP.offset) {
|
|
||||||
data.connectedGrassModel.renderLayer(ctx, ctx.state(UP), layer)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// render non-connected grass
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layer == Layers.tufts) {
|
|
||||||
data.algaeIdx?.let {
|
|
||||||
ctx.vertexLighter = vanillaTuftLighting
|
|
||||||
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
|
|
||||||
ctx.renderQuads(algaeModels[it])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data.reedIdx?.let {
|
|
||||||
ctx.vertexLighter = vanillaTuftLighting
|
|
||||||
ShadersModIntegration.grass(ctx, Config.reed.shaderWind) {
|
|
||||||
ctx.renderQuads(reedModels[it])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else super.renderLayer(ctx, data, layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val algaeSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_algae_$idx")
|
|
||||||
}
|
|
||||||
val reedSprites by SpriteSetDelegate(
|
|
||||||
Atlas.BLOCKS,
|
|
||||||
idFunc = { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_reed_$idx") },
|
|
||||||
idRegister = { id -> CenteredSprite(id, aspectHeight = 2).register(BetterFoliage.generatedPack) }
|
|
||||||
)
|
|
||||||
val algaeModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = Config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { algaeSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
val reedModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = Config.reed.let { tuftShapeSet(2.0, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { reedSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.config.isSnow
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderData
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.fullCubeTextured
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.pipeline.Layers
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.extendLayers
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.averageHSB
|
|
||||||
import mods.betterfoliage.util.idxOrNull
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.lazyMap
|
|
||||||
import mods.betterfoliage.util.brighten
|
|
||||||
import mods.betterfoliage.util.logTextureColor
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardGrassDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
val texture = params.location("texture") ?: return
|
|
||||||
val tint = params.int("tint") ?: -1
|
|
||||||
val color = Atlas.BLOCKS.file(texture).averageHSB.let {
|
|
||||||
detailLogger.logTextureColor(INFO, "grass texture \"$texture\"", it)
|
|
||||||
it.brighten().asColor
|
|
||||||
}
|
|
||||||
val noConnect = params["no-connect"] == "true"
|
|
||||||
ctx.addReplacement(StandardGrassKey(texture, tint, color, noConnect))
|
|
||||||
BetterFoliage.blockTypes.grass.add(ctx.blockState)
|
|
||||||
ctx.blockState.block.extendLayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardGrassKey(
|
|
||||||
val sprite: ResourceLocation,
|
|
||||||
val tintIndex: Int,
|
|
||||||
val avgColor: Color,
|
|
||||||
val noConnect: Boolean
|
|
||||||
) : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
|
||||||
return StandardGrassModel(wrapped, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GrassRenderData(
|
|
||||||
val isSnowed: Boolean,
|
|
||||||
val connectedGrassIdx: Int?,
|
|
||||||
val tuftIdx: Int?
|
|
||||||
): SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = when {
|
|
||||||
connectedGrassIdx != null && layer == Layers.connectedGrass -> true
|
|
||||||
tuftIdx != null && layer == Layers.tufts -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardGrassModel(
|
|
||||||
wrapped: SpecialRenderModel,
|
|
||||||
val key: StandardGrassKey
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
|
|
||||||
val tuftNormal by grassTuftMeshesNormal.delegate(key)
|
|
||||||
val tuftSnowed by grassTuftMeshesSnowed.delegate(key)
|
|
||||||
val fullBlock by grassFullBlockMeshes.delegate(key)
|
|
||||||
val tuftLighting = LightingPreferredFace(UP)
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
|
|
||||||
val stateBelow = ctx.state(DOWN)
|
|
||||||
val stateAbove = ctx.state(UP)
|
|
||||||
val isAir = ctx.isAir(UP)
|
|
||||||
val isSnowed = stateAbove.isSnow
|
|
||||||
val connected = !key.noConnect && Config.connectedGrass.enabled &&
|
|
||||||
(!isSnowed || Config.connectedGrass.snowEnabled) &&
|
|
||||||
BetterFoliage.blockTypes.run { stateBelow in grass || stateBelow in dirt }
|
|
||||||
|
|
||||||
return GrassRenderData(
|
|
||||||
isSnowed = isSnowed,
|
|
||||||
connectedGrassIdx = random.idxOrNull(if (isSnowed) snowFullBlockMeshes else fullBlock) { connected },
|
|
||||||
tuftIdx = random.idxOrNull(if (isSnowed) tuftSnowed else tuftNormal) {
|
|
||||||
Config.shortGrass.enabled(random) &&
|
|
||||||
Config.shortGrass.grassEnabled &&
|
|
||||||
(isAir || isSnowed)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
if (data is GrassRenderData) {
|
|
||||||
if (data.connectedGrassIdx != null) {
|
|
||||||
if (layer == Layers.connectedGrass)
|
|
||||||
ctx.renderQuads((if (data.isSnowed) snowFullBlockMeshes else fullBlock)[data.connectedGrassIdx])
|
|
||||||
} else {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
}
|
|
||||||
if (data.tuftIdx != null && layer == Layers.tufts) {
|
|
||||||
ctx.vertexLighter = tuftLighting
|
|
||||||
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
|
|
||||||
ctx.renderQuads((if (data.isSnowed) tuftSnowed else tuftNormal)[data.tuftIdx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else super.renderLayer(ctx, data, layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val grassTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx")
|
|
||||||
}
|
|
||||||
val grassTuftShapes by BetterFoliage.modelManager.lazy {
|
|
||||||
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
}
|
|
||||||
val grassTuftMeshesNormal = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
|
||||||
tuftModelSet(grassTuftShapes, key.avgColor, key.tintIndex) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
val grassTuftMeshesSnowed = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
|
||||||
tuftModelSet(grassTuftShapes, Color.white, -1) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
val grassFullBlockMeshes = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
|
||||||
Array(64) { fullCubeTextured(key.sprite, key.tintIndex) }
|
|
||||||
}
|
|
||||||
val snowFullBlockMeshes by BetterFoliage.modelManager.lazy {
|
|
||||||
Array(64) { fullCubeTextured(ResourceLocation("block/snow"), -1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.config.isSnow
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
|
||||||
import mods.betterfoliage.render.lighting.RoundLeafLightingPreferUp
|
|
||||||
import mods.betterfoliage.render.particle.LeafBlockModel
|
|
||||||
import mods.betterfoliage.render.particle.LeafParticleKey
|
|
||||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.generated.GeneratedLeafSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.averageHSB
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.lazyMap
|
|
||||||
import mods.betterfoliage.util.brighten
|
|
||||||
import mods.betterfoliage.util.logTextureColor
|
|
||||||
import mods.betterfoliage.util.saturate
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
|
|
||||||
object StandardLeafDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
val texture = params.location("texture") ?: return
|
|
||||||
val tint = params.int("tint") ?: -1
|
|
||||||
val color = Atlas.BLOCKS.file(texture).averageHSB.let {
|
|
||||||
detailLogger.logTextureColor(INFO, "leaf texture \"$texture\"", it)
|
|
||||||
it.brighten().asColor
|
|
||||||
}
|
|
||||||
val leafType = params["particle"] ?: "default"
|
|
||||||
val generated = GeneratedLeafSprite(texture, leafType)
|
|
||||||
.register(BetterFoliage.generatedPack)
|
|
||||||
.apply { ctx.sprites.add(this) }
|
|
||||||
|
|
||||||
detailLogger.log(INFO, " particle $leafType")
|
|
||||||
ctx.addReplacement(StandardLeafKey(generated, leafType, tint, color))
|
|
||||||
BetterFoliage.blockTypes.leaf.add(ctx.blockState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardLeafKey(
|
|
||||||
val roundLeafTexture: ResourceLocation,
|
|
||||||
override val leafType: String,
|
|
||||||
override val tintIndex: Int,
|
|
||||||
override val avgColor: Color
|
|
||||||
) : HalfBakedWrapperKey(), LeafParticleKey {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
|
||||||
return StandardLeafModel(wrapped, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardLeafModel(
|
|
||||||
model: SpecialRenderModel,
|
|
||||||
override val key: StandardLeafKey
|
|
||||||
) : HalfBakedSpecialWrapper(model), LeafBlockModel {
|
|
||||||
val leafNormal by leafModelsNormal.delegate(key)
|
|
||||||
val leafSnowed by leafModelsSnowed.delegate(key)
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
ShadersModIntegration.leaves(ctx, true) {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (!Config.enabled || !Config.leaves.enabled) return
|
|
||||||
|
|
||||||
ctx.vertexLighter = RoundLeafLightingPreferUp
|
|
||||||
val leafIdx = ctx.random.nextInt(64)
|
|
||||||
ctx.renderQuads(leafNormal[leafIdx])
|
|
||||||
if (Config.leaves.snowEnabled && ctx.state(UP).isSnow) ctx.renderQuads(leafSnowed[leafIdx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val leafSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_leaves_snowed_$idx")
|
|
||||||
}
|
|
||||||
val leafModelsBase by BetterFoliage.modelManager.lazy {
|
|
||||||
Config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
|
||||||
}
|
|
||||||
val leafModelsNormal = BetterFoliage.modelManager.lazyMap { key: StandardLeafKey ->
|
|
||||||
// generated leaf textures naturally carry the color of their source textures
|
|
||||||
// no need to color the quad a second time
|
|
||||||
crossModelsTextured(leafModelsBase, Color.white, key.tintIndex, true) { key.roundLeafTexture }
|
|
||||||
}
|
|
||||||
val leafModelsSnowed = BetterFoliage.modelManager.lazyMap { key: StandardLeafKey ->
|
|
||||||
crossModelsTextured(leafModelsBase, Color.white, -1, false) { leafSpritesSnowed[it].name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.idx
|
|
||||||
import mods.betterfoliage.util.idxOrNull
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardLilypadDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
ctx.addReplacement(StandardLilypadKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardLilypadKey : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LilypadRenderData(
|
|
||||||
val rootIdx: Int,
|
|
||||||
val flowerIdx: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
class StandardLilypadModel(
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
return LilypadRenderData(
|
|
||||||
rootIdx = random.idx(lilypadRootModels),
|
|
||||||
flowerIdx = random.idxOrNull(lilypadFlowerModels) { Config.lilypad.enabled(random) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
ctx.checkSides = false
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (data is LilypadRenderData) {
|
|
||||||
data.flowerIdx?.let { ctx.renderQuads(lilypadFlowerModels[it]) }
|
|
||||||
ShadersModIntegration.grass(ctx, Config.lilypad.shaderWind) {
|
|
||||||
ctx.renderQuads(lilypadRootModels[data.rootIdx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val lilypadRootSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_roots_$idx")
|
|
||||||
}
|
|
||||||
val lilypadFlowerSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_flower_$idx")
|
|
||||||
}
|
|
||||||
val lilypadRootModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = tuftShapeSet(1.0, 1.0, 1.0, Config.lilypad.hOffset)
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { lilypadRootSprites[it] }
|
|
||||||
.transform { move(2.0 to DOWN) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
val lilypadFlowerModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = tuftShapeSet(0.5, 0.5, 0.5, Config.lilypad.hOffset)
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { lilypadFlowerSprites[it] }
|
|
||||||
.transform { move(1.0 to DOWN) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderData
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.pipeline.Layers
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.extendLayers
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.averageHSB
|
|
||||||
import mods.betterfoliage.util.idxOrNull
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.lazyMap
|
|
||||||
import mods.betterfoliage.util.brighten
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardMyceliumDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
val texture = params.location("texture") ?: return
|
|
||||||
val tint = params.int("tint") ?: -1
|
|
||||||
val color = Atlas.BLOCKS.file(texture).averageHSB.brighten(multiplier = 1.5f).asColor
|
|
||||||
ctx.addReplacement(StandardMyceliumKey(texture, tint, color))
|
|
||||||
ctx.blockState.block.extendLayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardMyceliumKey(
|
|
||||||
val sprite: ResourceLocation,
|
|
||||||
val tintIndex: Int,
|
|
||||||
val avgColor: Color,
|
|
||||||
) : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
|
||||||
return StandardMyceliumModel(wrapped, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyceliumRenderData(
|
|
||||||
val tuftIndex: Int?
|
|
||||||
) : SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardMyceliumModel(
|
|
||||||
wrapped: SpecialRenderModel,
|
|
||||||
key: StandardMyceliumKey
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
|
|
||||||
val tuftModels by myceliumTuftModels.delegate(key)
|
|
||||||
val tuftLighting = LightingPreferredFace(Direction.UP)
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
return MyceliumRenderData(
|
|
||||||
random.idxOrNull(tuftModels) {
|
|
||||||
Config.shortGrass.enabled(random) &&
|
|
||||||
Config.shortGrass.myceliumEnabled &&
|
|
||||||
ctx.state(Direction.UP).isAir(ctx.world, ctx.pos)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (data is MyceliumRenderData && data.tuftIndex != null && layer == Layers.tufts) {
|
|
||||||
ctx.vertexLighter = tuftLighting
|
|
||||||
ctx.renderQuads(tuftModels[data.tuftIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val myceliumTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_mycel_$idx")
|
|
||||||
}
|
|
||||||
val myceliumTuftShapes by BetterFoliage.modelManager.lazy {
|
|
||||||
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
}
|
|
||||||
val myceliumTuftModels = BetterFoliage.modelManager.lazyMap { key: StandardMyceliumKey ->
|
|
||||||
tuftModelSet(myceliumTuftShapes, key.avgColor, key.tintIndex) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderData
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.pipeline.Layers
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.extendLayers
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.idxOrNull
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction.DOWN
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardNetherrackDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
ctx.addReplacement(StandardNetherrackKey)
|
|
||||||
ctx.blockState.block.extendLayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardNetherrackKey : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardNetherrackModel(wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetherrackRenderData(
|
|
||||||
val tuftIndex: Int?
|
|
||||||
): SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardNetherrackModel(
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
|
|
||||||
val tuftLighting = LightingPreferredFace(DOWN)
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
return NetherrackRenderData(
|
|
||||||
random.idxOrNull(netherrackTuftModels) {
|
|
||||||
Config.netherrack.enabled &&
|
|
||||||
ctx.isAir(DOWN)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (data is NetherrackRenderData && data.tuftIndex != null && layer == Layers.tufts) {
|
|
||||||
ctx.vertexLighter = tuftLighting
|
|
||||||
ctx.renderQuads(netherrackTuftModels[data.tuftIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_netherrack_$idx")
|
|
||||||
}
|
|
||||||
val netherrackTuftModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = Config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { netherrackTuftSprites[randomI()] }
|
|
||||||
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.config.ACCEPTED_ROUND_LOG_MATERIALS
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.render.column.ColumnBlockKey
|
|
||||||
import mods.betterfoliage.render.column.ColumnMeshSet
|
|
||||||
import mods.betterfoliage.render.column.ColumnModelBase
|
|
||||||
import mods.betterfoliage.render.column.ColumnRenderLayer
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.lazyMap
|
|
||||||
import mods.betterfoliage.util.tryDefault
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.RotatedPillarBlock
|
|
||||||
import net.minecraft.util.Direction.Axis
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
|
|
||||||
interface RoundLogKey : ColumnBlockKey, ModelBakingKey {
|
|
||||||
val barkSprite: ResourceLocation
|
|
||||||
val endSprite: ResourceLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
object RoundLogOverlayLayer : ColumnRenderLayer() {
|
|
||||||
override fun getColumnKey(state: BlockState) = BetterFoliage.blockTypes.getTypedOrNull<ColumnBlockKey>(state)
|
|
||||||
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 StandardRoundLogDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
val barkSprite = params.location("texture-side") ?: return
|
|
||||||
val endSprite = params.location("texture-end") ?: return
|
|
||||||
val axis = getAxis(ctx.blockState)
|
|
||||||
|
|
||||||
detailLogger.log(INFO, " axis $axis, material ${ctx.blockState.material}")
|
|
||||||
if (!Config.roundLogs.plantsOnly || ctx.blockState.material in ACCEPTED_ROUND_LOG_MATERIALS)
|
|
||||||
ctx.addReplacement(StandardRoundLogKey(axis, barkSprite, endSprite))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAxis(state: BlockState): Axis? {
|
|
||||||
val axis = tryDefault(null) { state.getValue(RotatedPillarBlock.AXIS).toString() } ?:
|
|
||||||
state.values.entries.find { it.key.name.toLowerCase() == "axis" }?.value?.toString()
|
|
||||||
return when (axis) {
|
|
||||||
"x" -> Axis.X
|
|
||||||
"y" -> Axis.Y
|
|
||||||
"z" -> Axis.Z
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardRoundLogKey(
|
|
||||||
override val axis: Axis?,
|
|
||||||
override val barkSprite: ResourceLocation,
|
|
||||||
override val endSprite: ResourceLocation
|
|
||||||
) : RoundLogKey, HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardRoundLogModel(this, wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardRoundLogModel(
|
|
||||||
val key: StandardRoundLogKey,
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : ColumnModelBase(wrapped) {
|
|
||||||
override val enabled: Boolean get() = Config.enabled && Config.roundLogs.enabled
|
|
||||||
override val overlayLayer: ColumnRenderLayer get() = RoundLogOverlayLayer
|
|
||||||
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
|
|
||||||
|
|
||||||
val modelSet by modelSets.delegate(key)
|
|
||||||
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val modelSets = BetterFoliage.modelManager.lazyMap { key: StandardRoundLogKey ->
|
|
||||||
val barkSprite = Atlas.BLOCKS[key.barkSprite]
|
|
||||||
val endSprite = Atlas.BLOCKS[key.endSprite]
|
|
||||||
Config.roundLogs.let { config ->
|
|
||||||
ColumnMeshSet(
|
|
||||||
config.radiusSmall, config.radiusLarge, config.zProtection,
|
|
||||||
key.axis ?: Axis.Y,
|
|
||||||
barkSprite, barkSprite,
|
|
||||||
endSprite, endSprite
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.chunk.BlockCtx
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
|
||||||
import mods.betterfoliage.model.Quad
|
|
||||||
import mods.betterfoliage.model.SpecialRenderData
|
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.bake
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.render.pipeline.Layers
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|
||||||
import mods.betterfoliage.render.pipeline.extendLayers
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.allDirections
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.idx
|
|
||||||
import mods.betterfoliage.util.lazy
|
|
||||||
import mods.betterfoliage.util.mapArray
|
|
||||||
import mods.betterfoliage.util.randomB
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.minecraft.block.material.Material
|
|
||||||
import net.minecraft.client.renderer.RenderType
|
|
||||||
import net.minecraft.util.Direction
|
|
||||||
import net.minecraft.util.Direction.UP
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
object StandardSandDiscovery : ParametrizedModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
|
||||||
ctx.addReplacement(StandardSandKey)
|
|
||||||
ctx.blockState.block.extendLayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardSandKey : HalfBakedWrapperKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SandRenderData(
|
|
||||||
val crustIdx: Array<Int?>,
|
|
||||||
val tuftIdx: Array<Int?>
|
|
||||||
): SpecialRenderData {
|
|
||||||
override fun canRenderInLayer(layer: RenderType) = when {
|
|
||||||
(crustIdx.any { it != null } || tuftIdx.any { it != null }) && layer == Layers.coral -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardSandModel(
|
|
||||||
wrapped: SpecialRenderModel
|
|
||||||
) : HalfBakedSpecialWrapper(wrapped) {
|
|
||||||
val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) }
|
|
||||||
|
|
||||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
|
||||||
if (!Config.enabled) return Unit
|
|
||||||
if (!Config.coral.enabled(random)) return Unit
|
|
||||||
if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return Unit
|
|
||||||
|
|
||||||
val crustIdx = Array<Int?>(6) { null }
|
|
||||||
val tuftIdx = Array<Int?>(6) { null }
|
|
||||||
allDirections.filter { random.nextInt(64) < Config.coral.chance }.forEach { face ->
|
|
||||||
val isWater = ctx.state(face).material == Material.WATER
|
|
||||||
val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER
|
|
||||||
if (isDeepWater) {
|
|
||||||
crustIdx[face.ordinal] = random.idx(coralCrustModels)
|
|
||||||
tuftIdx[face.ordinal] = random.idx(coralTuftModels)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SandRenderData(crustIdx, tuftIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
|
||||||
super.renderLayer(ctx, data, layer)
|
|
||||||
if (data is SandRenderData && layer == Layers.coral) {
|
|
||||||
for (face in 0 until 6) {
|
|
||||||
ctx.vertexLighter = coralLighting[face]
|
|
||||||
data.crustIdx[face]?.let { ctx.renderQuads(coralCrustModels[face][it]) }
|
|
||||||
data.tuftIdx[face]?.let { ctx.renderQuads(coralTuftModels[face][it]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val coralTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_coral_$idx")
|
|
||||||
}
|
|
||||||
val coralCrustSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_crust_$idx")
|
|
||||||
}
|
|
||||||
val coralTuftModels by BetterFoliage.modelManager.lazy {
|
|
||||||
val shapes = Config.coral.let { tuftShapeSet(it.size, 1.0, 1.0, it.hOffset) }
|
|
||||||
allDirections.mapArray { face ->
|
|
||||||
tuftModelSet(shapes, Color.white, -1) { coralTuftSprites[randomI()] }
|
|
||||||
.transform { rotate(Rotation.fromUp[face]) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val coralCrustModels by BetterFoliage.modelManager.lazy {
|
|
||||||
allDirections.map { face ->
|
|
||||||
Array(64) { idx ->
|
|
||||||
listOf(
|
|
||||||
Quad.horizontalRectangle(x1 = -0.5, x2 = 0.5, z1 = -0.5, z2 = 0.5, y = 0.0)
|
|
||||||
.scale(Config.coral.crustSize)
|
|
||||||
.move(0.5 + randomD(0.01, Config.coral.vOffset) to UP)
|
|
||||||
.rotate(Rotation.fromUp[face])
|
|
||||||
.mirrorUV(randomB(), randomB()).rotateUV(randomI(max = 4))
|
|
||||||
.sprite(coralCrustSprites[idx]).colorAndIndex(null)
|
|
||||||
).bake(applyDiffuseLighting = false)
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user