Compare commits
110 Commits
1.16.5-Fab
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e5b1e136e | ||
|
|
e8d3a38942 | ||
|
|
5bdb316161 | ||
|
|
da7ff77320 | ||
|
|
81fbced1c8 | ||
|
|
e32f63ee3a | ||
|
|
77b1e672c4 | ||
|
|
755348ad18 | ||
|
|
47aa5df3c4 | ||
|
|
3f49f6e09b | ||
|
|
4f7ede5d9e | ||
|
|
5ad3bbe342 | ||
|
|
5a5daf6750 | ||
|
|
4bba2b06c2 | ||
|
|
adfc14bc62 | ||
|
|
85b4880391 | ||
|
|
4bc6a4a2f9 | ||
|
|
98ca2ae284 | ||
|
|
abb1c5cf22 | ||
|
|
c366e89266 | ||
|
|
03766727fd | ||
|
|
856daacd58 | ||
|
|
f8092685a8 | ||
|
|
8b4967584e | ||
|
|
ce37687a1b | ||
|
|
187b29ac41 | ||
|
|
7a38ec7c3b | ||
|
|
772b509a10 | ||
|
|
ee50ce1dc5 | ||
|
|
d2f0d9c0f6 | ||
|
|
4706b11f98 | ||
|
|
bc3e598b1e | ||
|
|
b407e51d19 | ||
|
|
dcbb636902 | ||
|
|
e88a1ba5ae | ||
|
|
f107fe0e1a | ||
|
|
97f528d426 | ||
|
|
f2aeb870ce | ||
|
|
b365c1cf06 | ||
|
|
2b1cab8e62 | ||
|
|
bae6d9b598 | ||
|
|
6472353e4a | ||
|
|
929553692f | ||
|
|
7babab8385 | ||
|
|
51589bbad4 | ||
|
|
6b57840b8f | ||
|
|
c176713f20 | ||
|
|
23abde8000 | ||
|
|
21f6569e1b | ||
|
|
e92e89f86c | ||
|
|
728e723d37 | ||
|
|
ab5d1c0f3a | ||
|
|
9d9d32b32a | ||
|
|
6623bee39f | ||
|
|
ffa8dd724e | ||
|
|
bd4a4885fe | ||
|
|
9d59105af1 | ||
|
|
7cdddef1bf | ||
|
|
168fad883d | ||
|
|
572d517828 | ||
|
|
d4963ec173 | ||
|
|
ad83a511fb | ||
|
|
e351af2369 | ||
|
|
32a88fa824 | ||
|
|
ec723113d3 | ||
|
|
4e42f63c36 | ||
|
|
0daf61583a | ||
|
|
504f033c2e | ||
|
|
fa099b1b97 | ||
|
|
b0c0cd0d1b | ||
|
|
df69605521 | ||
|
|
d3b1d138ba | ||
|
|
07369159b8 | ||
|
|
6700e724a5 | ||
|
|
5b34da3e61 | ||
|
|
fc7c9a5381 | ||
|
|
cf7ae0efa1 | ||
|
|
04bb240d36 | ||
|
|
91fda1522c | ||
|
|
7ca25c0da7 | ||
|
|
0a05a67eda | ||
|
|
43e7fb830b | ||
|
|
c72e93aedf | ||
|
|
f236ef64ad | ||
|
|
d38f556bde | ||
|
|
fad662443d | ||
|
|
32c4dc6035 | ||
|
|
aee6e5caca | ||
|
|
5fbe2ff16f | ||
|
|
7a133c95a7 | ||
|
|
25b1d76c9e | ||
|
|
7a02179481 | ||
|
|
244907f4cd | ||
|
|
4ccd753c0c | ||
|
|
2715acf9c8 | ||
|
|
1c146fb070 | ||
|
|
44a20ceab3 | ||
|
|
1be2382fed | ||
|
|
e12b7b803c | ||
|
|
8a94867cd8 | ||
|
|
3a391f1677 | ||
|
|
d5dd1a36e3 | ||
|
|
a589c868a9 | ||
|
|
37ffa219fc | ||
|
|
220f2356d8 | ||
|
|
ec184f9916 | ||
|
|
2b1fd84cd5 | ||
|
|
30f199e9a2 | ||
|
|
0c66849175 | ||
|
|
3bd402b964 |
8
.classpath
Normal file
8
.classpath
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src/main/java"/>
|
||||||
|
<classpathentry kind="src" path="src/main/resources"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,9 +1,5 @@
|
|||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
run/
|
|
||||||
.gradle/
|
.gradle/
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
build/
|
build/
|
||||||
classes/
|
libs/
|
||||||
temp/
|
|
||||||
|
|||||||
18
.project
Normal file
18
.project
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>BetterFoliage</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
@@ -1,4 +1,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/
|
||||||
|
|
||||||
|
Latest Download
|
||||||
|
========
|
||||||
|
[BetterFoliage 0.9.11-beta] (http://goo.gl/cTCFLo) (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='1.0.0'
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
inputs.property "version", project.version
|
||||||
|
inputs.property "mcversion", project.minecraft.version
|
||||||
|
|
||||||
|
from(sourceSets.main.resources.srcDirs) {
|
||||||
|
include 'mcmod.info'
|
||||||
|
expand 'version':project.version, 'mcversion':project.minecraft.version
|
||||||
|
}
|
||||||
|
|
||||||
|
from(sourceSets.main.resources.srcDirs) {
|
||||||
|
exclude 'mcmod.info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import net.fabricmc.loom.task.RemapJarTask
|
|
||||||
import org.ajoberstar.grgit.Grgit
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("fabric-loom").version("0.6-SNAPSHOT")
|
|
||||||
kotlin("jvm").version("1.4.31")
|
|
||||||
id("org.ajoberstar.grgit").version("3.1.1")
|
|
||||||
}
|
|
||||||
apply(plugin = "org.ajoberstar.grgit")
|
|
||||||
|
|
||||||
val gitHash = (project.ext.get("grgit") as Grgit).head().abbreviatedId
|
|
||||||
val semVer = "${project.version}+$gitHash"
|
|
||||||
val jarName = "BetterFoliage-$semVer-Fabric-${properties["mcVersion"]}"
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven("https://maven.fabricmc.net/")
|
|
||||||
maven("https://minecraft.curseforge.com/api/maven")
|
|
||||||
maven("https://maven.modmuss50.me/")
|
|
||||||
maven("https://maven.shedaniel.me/")
|
|
||||||
maven("https://grondag-repo.appspot.com").credentials { username = "guest"; password = "" }
|
|
||||||
maven("https://jitpack.io")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
"minecraft"("com.mojang:minecraft:${properties["mcVersion"]}")
|
|
||||||
"mappings"("net.fabricmc:yarn:${properties["yarnMappings"]}:v2")
|
|
||||||
|
|
||||||
// basic Fabric stuff
|
|
||||||
"modImplementation"("net.fabricmc:fabric-loader:${properties["loaderVersion"]}")
|
|
||||||
"modImplementation"("net.fabricmc.fabric-api:fabric-api:${properties["fabricVersion"]}")
|
|
||||||
"modImplementation"("net.fabricmc:fabric-language-kotlin:${properties["fabricKotlinVersion"]}")
|
|
||||||
|
|
||||||
// configuration handling
|
|
||||||
"modImplementation"("io.github.prospector:modmenu:${properties["modMenuVersion"]}")
|
|
||||||
listOf("modImplementation", "include").forEach { configuration ->
|
|
||||||
configuration("me.shedaniel.cloth:cloth-config-fabric:${properties["clothConfigVersion"]}")
|
|
||||||
configuration("me.zeroeightsix:fiber:${properties["fiberVersion"]}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canvas Renderer
|
|
||||||
// "modImplementation"("grondag:canvas:0.7.+")
|
|
||||||
|
|
||||||
// Optifabric
|
|
||||||
// "modImplementation"("com.github.modmuss50:OptiFabric:1.0.0")
|
|
||||||
"implementation"("org.zeroturnaround:zt-zip:1.13")
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
get("main").ext["refMap"] = "betterfoliage.refmap.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ProcessResources>("processResources") {
|
|
||||||
filesMatching("fabric.mod.json") { expand(mutableMapOf("version" to semVer)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.getByName<RemapJarTask>("remapJar") {
|
|
||||||
archiveName = "$jarName.jar"
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx2G
|
|
||||||
org.gradle.daemon=false
|
|
||||||
|
|
||||||
group = com.github.octarine-noise
|
|
||||||
name = betterfoliage
|
|
||||||
jarName = BetterFoliage-Forge
|
|
||||||
|
|
||||||
version = 2.6.2
|
|
||||||
|
|
||||||
mcVersion = 1.16.5
|
|
||||||
yarnMappings=1.16.5+build.6
|
|
||||||
loaderVersion=0.11.3
|
|
||||||
fabricVersion=0.32.5+1.16
|
|
||||||
|
|
||||||
kotlinVersion=1.3.60
|
|
||||||
fabricKotlinVersion=1.5.0+kotlin.1.4.31
|
|
||||||
|
|
||||||
clothConfigVersion=4.11.24
|
|
||||||
modMenuVersion=1.16.9
|
|
||||||
fiberVersion=0.8.0-2
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
183
gradlew
vendored
183
gradlew
vendored
@@ -1,183 +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" "$@"
|
|
||||||
100
gradlew.bat
vendored
100
gradlew.bat
vendored
@@ -1,100 +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 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 init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
pluginManagement {
|
|
||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
maven("https://maven.fabricmc.net/")
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rootProject.name = "betterfoliage"
|
|
||||||
52
src/main/java/mods/betterfoliage/BetterFoliage.java
Normal file
52
src/main/java/mods/betterfoliage/BetterFoliage.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package mods.betterfoliage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.network.NetworkCheckHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
|
||||||
|
@Mod(name=BetterFoliage.MOD_NAME, modid=BetterFoliage.MOD_ID, acceptedMinecraftVersions=BetterFoliage.MC_VERSIONS, guiFactory=BetterFoliage.GUI_FACTORY)
|
||||||
|
public class BetterFoliage {
|
||||||
|
|
||||||
|
public static final String MOD_ID = "BetterFoliage";
|
||||||
|
public static final String MOD_NAME = "Better Foliage";
|
||||||
|
public static final String MC_VERSIONS = "[1.7.2,1.7.10]";
|
||||||
|
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
|
||||||
|
|
||||||
|
@Mod.Instance
|
||||||
|
public static BetterFoliage instance;
|
||||||
|
|
||||||
|
public static Logger log;
|
||||||
|
|
||||||
|
public static File configDir;
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void preInit(FMLPreInitializationEvent event) {
|
||||||
|
log = event.getModLog();
|
||||||
|
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
|
||||||
|
configDir.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void postInit(FMLPostInitializationEvent event) {
|
||||||
|
if (event.getSide() == Side.CLIENT) {
|
||||||
|
Config.getDefaultBiomes();
|
||||||
|
Config.readConfig(new File(configDir, "betterfoliage.cfg"));
|
||||||
|
BetterFoliageClient.postInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NetworkCheckHandler
|
||||||
|
public boolean checkVersion(Map<String, String> mods, Side side) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package mods.betterfoliage;
|
|
||||||
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import org.apache.logging.log4j.Level;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class MixinConfigPlugin implements IMixinConfigPlugin {
|
|
||||||
|
|
||||||
Logger logger = LogManager.getLogger(this);
|
|
||||||
|
|
||||||
Boolean hasOptifine = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
|
||||||
if (hasOptifine == null) {
|
|
||||||
hasOptifine = FabricLoader.getInstance().isModLoaded("optifabric");
|
|
||||||
if (hasOptifine) logger.log(Level.INFO, "[BetterFoliage] Optifabric detected, applying Optifine mixins");
|
|
||||||
else logger.log(Level.INFO, "[BetterFoliage] Optifabric not detected, applying Vanilla mixins");
|
|
||||||
}
|
|
||||||
if (mixinClassName.endsWith("Vanilla") && hasOptifine) return false;
|
|
||||||
if (mixinClassName.endsWith("Optifine") && !hasOptifine) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoad(String mixinPackage) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRefMapperConfig() { return null; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getMixins() { return null; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
|
|
||||||
}
|
|
||||||
128
src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
Normal file
128
src/main/java/mods/betterfoliage/client/BetterFoliageClient.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.impl.EntityFXFallingLeaves;
|
||||||
|
import mods.betterfoliage.client.render.impl.EntityFXRisingSoul;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterGrass;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterMycelium;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterNetherrack;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlockBetterReed;
|
||||||
|
import mods.betterfoliage.client.render.impl.RenderBlocksBetterGrassSide;
|
||||||
|
import mods.betterfoliage.client.resource.LeafGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.LeafParticleTextures;
|
||||||
|
import mods.betterfoliage.client.resource.LeafTextureEnumerator;
|
||||||
|
import mods.betterfoliage.client.resource.ReedGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.ShortGrassGenerator;
|
||||||
|
import mods.betterfoliage.client.resource.SoulParticleTextures;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
|
||||||
|
public class BetterFoliageClient {
|
||||||
|
|
||||||
|
public static ResourceLocation missingTexture = new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png");
|
||||||
|
|
||||||
|
public static Map<Integer, IRenderBlockDecorator> decorators = Maps.newHashMap();
|
||||||
|
public static LeafGenerator leafGenerator = new LeafGenerator();
|
||||||
|
public static LeafParticleTextures leafParticles = new LeafParticleTextures(0);
|
||||||
|
public static SoulParticleTextures soulParticles = new SoulParticleTextures();
|
||||||
|
public static WindTracker wind = new WindTracker();
|
||||||
|
|
||||||
|
public static void postInit() {
|
||||||
|
FMLCommonHandler.instance().bus().register(new KeyHandler());
|
||||||
|
FMLCommonHandler.instance().bus().register(new Config());
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Registering renderers");
|
||||||
|
registerRenderer(new RenderBlockBetterCactus());
|
||||||
|
registerRenderer(new RenderBlockBetterNetherrack());
|
||||||
|
registerRenderer(new RenderBlockBetterLilypad());
|
||||||
|
registerRenderer(new RenderBlockBetterMycelium());
|
||||||
|
registerRenderer(new RenderBlockBetterLeaves());
|
||||||
|
registerRenderer(new RenderBlockBetterGrass());
|
||||||
|
registerRenderer(new RenderBlockBetterReed());
|
||||||
|
registerRenderer(new RenderBlockBetterAlgae());
|
||||||
|
registerRenderer(new RenderBlockBetterCoral());
|
||||||
|
registerRenderer(new RenderBlocksBetterGrassSide());
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(wind);
|
||||||
|
FMLCommonHandler.instance().bus().register(wind);
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(Config.leaves);
|
||||||
|
MinecraftForge.EVENT_BUS.register(Config.crops);
|
||||||
|
MinecraftForge.EVENT_BUS.register(Config.dirt);
|
||||||
|
MinecraftForge.EVENT_BUS.register(Config.grass);
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Registering texture generators");
|
||||||
|
MinecraftForge.EVENT_BUS.register(soulParticles);
|
||||||
|
MinecraftForge.EVENT_BUS.register(leafGenerator);
|
||||||
|
MinecraftForge.EVENT_BUS.register(leafParticles);
|
||||||
|
MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator());
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_top", missingTexture, false));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass", missingTexture, false));
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass_snow", missingTexture, true));
|
||||||
|
|
||||||
|
ShadersModIntegration.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeafTexture(TextureAtlasSprite icon) {
|
||||||
|
String resourceLocation = icon.getIconName();
|
||||||
|
if (resourceLocation.startsWith("forestry:leaves/")) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRenderTypeOverride(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
// universal sign for DON'T RENDER ME!
|
||||||
|
if (original == -1) return original;
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, IRenderBlockDecorator> entry : decorators.entrySet())
|
||||||
|
if (entry.getValue().isBlockAccepted(blockAccess, x, y, z, block, original))
|
||||||
|
return entry.getKey();
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onRandomDisplayTick(Block block, World world, int x, int y, int z) {
|
||||||
|
if (Config.soulFXEnabled) {
|
||||||
|
if (world.getBlock(x, y, z) == Blocks.soul_sand && Math.random() < Config.soulFXChance) {
|
||||||
|
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXRisingSoul(world, x, y, z));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Config.leafFXEnabled) {
|
||||||
|
if (Config.leaves.matchesID(block) && world.isAirBlock(x, y - 1, z) && Math.random() < Config.leafFXChance) {
|
||||||
|
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerRenderer(IRenderBlockDecorator decorator) {
|
||||||
|
int renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||||
|
decorators.put(renderId, decorator);
|
||||||
|
RenderingRegistry.registerBlockHandler(renderId, decorator);
|
||||||
|
MinecraftForge.EVENT_BUS.register(decorator);
|
||||||
|
decorator.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
77
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
77
src/main/java/mods/betterfoliage/client/BlockMatcher.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.ResourceUtils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.multiplayer.WorldClient;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class BlockMatcher {
|
||||||
|
|
||||||
|
public Set<Class<?>> whiteList = Sets.newHashSet();
|
||||||
|
public Set<Class<?>> blackList = Sets.newHashSet();
|
||||||
|
public Set<Integer> blockIDs = Sets.newHashSet();
|
||||||
|
|
||||||
|
public boolean matchesClass(Block block) {
|
||||||
|
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
|
||||||
|
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesID(int blockId) {
|
||||||
|
return blockIDs.contains(blockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesID(Block block) {
|
||||||
|
return blockIDs.contains(Block.blockRegistry.getIDForObject(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateClassLists(String[] newWhitelist, String[] newBlacklist) {
|
||||||
|
whiteList.clear();
|
||||||
|
for(String className : newWhitelist) try {
|
||||||
|
whiteList.add(Class.forName(className));
|
||||||
|
} catch(ClassNotFoundException e) {}
|
||||||
|
|
||||||
|
blackList.clear();
|
||||||
|
for(String className : newBlacklist) try {
|
||||||
|
blackList.add(Class.forName(className));
|
||||||
|
} catch(ClassNotFoundException e) {}
|
||||||
|
|
||||||
|
updateBlockIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void updateBlockIDs() {
|
||||||
|
blockIDs.clear();
|
||||||
|
Iterator<Block> iter = Block.blockRegistry.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Block block = iter.next();
|
||||||
|
if (matchesClass(block)) blockIDs.add(Block.blockRegistry.getIDForObject(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadDefaultLists(ResourceLocation defaults, Collection<String> blacklist, Collection<String> whitelist) {
|
||||||
|
for (String line : ResourceUtils.getLines(defaults)) {
|
||||||
|
if (line.startsWith("-"))
|
||||||
|
blacklist.add(line.substring(1));
|
||||||
|
else
|
||||||
|
whitelist.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caches block IDs on world load for fast lookup
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
if (event.world instanceof WorldClient) updateBlockIDs();
|
||||||
|
}
|
||||||
|
}
|
||||||
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 mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.gui.ConfigGuiFactory;
|
||||||
|
import net.minecraft.client.settings.KeyBinding;
|
||||||
|
import cpw.mods.fml.client.FMLClientHandler;
|
||||||
|
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class KeyHandler {
|
||||||
|
|
||||||
|
public static KeyBinding guiBinding;
|
||||||
|
|
||||||
|
public KeyHandler() {
|
||||||
|
guiBinding = new KeyBinding("key.betterfoliage.gui", 66, BetterFoliage.MOD_NAME);
|
||||||
|
ClientRegistry.registerKeyBinding(guiBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleKeyPress(InputEvent.KeyInputEvent event) {
|
||||||
|
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiFactory.ConfigGuiBetterFoliage(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
/** Call hooks and helper methods for dealing with Shaders Mod.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ShadersModIntegration {
|
||||||
|
|
||||||
|
private static boolean hasShadersMod = false;
|
||||||
|
private static int tallGrassEntityData;
|
||||||
|
private static int leavesEntityData;
|
||||||
|
private static Field shadersEntityData;
|
||||||
|
private static Field shadersEntityDataIndex;
|
||||||
|
|
||||||
|
/** Hide constructor */
|
||||||
|
private ShadersModIntegration() {}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
tallGrassEntityData = Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | Blocks.tallgrass.getRenderType() << 16;
|
||||||
|
leavesEntityData = Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | Blocks.leaves.getRenderType() << 16;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> classShaders = Class.forName("shadersmodcore.client.Shaders");
|
||||||
|
shadersEntityData = classShaders.getDeclaredField("entityData");
|
||||||
|
shadersEntityDataIndex = classShaders.getDeclaredField("entityDataIndex");
|
||||||
|
hasShadersMod = true;
|
||||||
|
} catch(Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Signal start of grass-type quads
|
||||||
|
*/
|
||||||
|
public static void startGrassQuads() {
|
||||||
|
if (!hasShadersMod) return;
|
||||||
|
setShadersEntityData(tallGrassEntityData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Signal start of leaf-type quads
|
||||||
|
*/
|
||||||
|
public static void startLeavesQuads() {
|
||||||
|
if (!hasShadersMod) return;
|
||||||
|
setShadersEntityData(leavesEntityData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Change the entity data (containing block ID) for the currently rendered block.
|
||||||
|
* Quads drawn afterwards will have the altered data.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
private static void setShadersEntityData(int data) {
|
||||||
|
try {
|
||||||
|
int[] entityData = (int[]) shadersEntityData.get(null);
|
||||||
|
int entityDataIndex = shadersEntityDataIndex.getInt(null);
|
||||||
|
entityData[(entityDataIndex * 2)] = data;
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Call hook from transformed ShadersMod class
|
||||||
|
* @param original entity data of currently rendered block
|
||||||
|
* @param block the block
|
||||||
|
* @return entity data to use
|
||||||
|
*/
|
||||||
|
public static int getBlockIdOverride(int original, Block block) {
|
||||||
|
if (Config.leaves.matchesID(original & 0xFFFF)) return leavesEntityData;
|
||||||
|
if (Config.crops.matchesID(original & 0xFFFF)) return tallGrassEntityData;
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource texture resource
|
||||||
|
* @return true if texture is a normal or specular map
|
||||||
|
*/
|
||||||
|
public static boolean isSpecialTexture(ResourceLocation resource) {
|
||||||
|
return resource.getResourcePath().toLowerCase().endsWith("_n.png") || resource.getResourcePath().toLowerCase().endsWith("_s.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/main/java/mods/betterfoliage/client/TextureMatcher.java
Normal file
67
src/main/java/mods/betterfoliage/client/TextureMatcher.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.ResourceUtils;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
public class TextureMatcher {
|
||||||
|
|
||||||
|
public static class TextureMapping {
|
||||||
|
public String matchDomain;
|
||||||
|
public String matchName;
|
||||||
|
public String textureType;
|
||||||
|
|
||||||
|
public TextureMapping(String matchDomain, String matchName, String textureType) {
|
||||||
|
this.matchDomain = matchDomain;
|
||||||
|
this.matchName = matchName;
|
||||||
|
this.textureType = textureType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(TextureAtlasSprite icon) {
|
||||||
|
ResourceLocation iconLocation = new ResourceLocation(icon.getIconName());
|
||||||
|
if (matchDomain != null && !matchDomain.equals(iconLocation.getResourceDomain())) return false;
|
||||||
|
return iconLocation.getResourcePath().contains(matchName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TextureMapping> mappings = Lists.newLinkedList();
|
||||||
|
|
||||||
|
public Map<IIcon, String> types = Maps.newHashMap();
|
||||||
|
|
||||||
|
public String put(TextureAtlasSprite icon) {
|
||||||
|
if (types.keySet().contains(icon)) return types.get(icon);
|
||||||
|
for (TextureMapping mapping : mappings) if (mapping.matches(icon)) {
|
||||||
|
types.put(icon, mapping.textureType);
|
||||||
|
return mapping.textureType;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(IIcon icon) {
|
||||||
|
return types.get(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMappings(ResourceLocation resource) {
|
||||||
|
mappings = Lists.newLinkedList();
|
||||||
|
|
||||||
|
for (String line : ResourceUtils.getLines(resource)) {
|
||||||
|
String[] lineSplit = line.split("=");
|
||||||
|
if (lineSplit.length != 2) continue;
|
||||||
|
|
||||||
|
String[] match = lineSplit[0].split(":");
|
||||||
|
if (match.length == 2) {
|
||||||
|
mappings.add(new TextureMapping(match[0], match[1], lineSplit[1]));
|
||||||
|
} else if (match.length == 1) {
|
||||||
|
mappings.add(new TextureMapping(null, match[0], lineSplit[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
64
src/main/java/mods/betterfoliage/client/WindTracker.java
Normal file
64
src/main/java/mods/betterfoliage/client/WindTracker.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package mods.betterfoliage.client;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||||
|
|
||||||
|
public class WindTracker {
|
||||||
|
|
||||||
|
public Random random = new Random();
|
||||||
|
|
||||||
|
public double targetX;
|
||||||
|
public double targetZ;
|
||||||
|
|
||||||
|
public double currentX;
|
||||||
|
public double currentZ;
|
||||||
|
|
||||||
|
public long nextChange = 0;
|
||||||
|
|
||||||
|
public void changeWind(World world) {
|
||||||
|
long changeTime = 120;
|
||||||
|
nextChange = world.getWorldInfo().getWorldTime() + changeTime;
|
||||||
|
|
||||||
|
double direction = 2.0 * Math.PI * random.nextDouble();
|
||||||
|
double speed = Math.abs(random.nextGaussian()) * Config.leafFXWindStrength;
|
||||||
|
if (world.isRaining()) speed += Math.abs(random.nextGaussian()) * Config.leafFXStormStrength;
|
||||||
|
|
||||||
|
targetX = Math.cos(direction) * speed;
|
||||||
|
targetZ = Math.sin(direction) * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldTick(ClientTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.START) return;
|
||||||
|
World world = Minecraft.getMinecraft().theWorld;
|
||||||
|
if (world == null) return;
|
||||||
|
|
||||||
|
// change target wind speed
|
||||||
|
if (world.getWorldInfo().getWorldTime() >= nextChange) changeWind(world);
|
||||||
|
|
||||||
|
// change current wind speed
|
||||||
|
double changeRate = world.isRaining() ? 0.015 : 0.005;
|
||||||
|
|
||||||
|
double deltaX = targetX - currentX;
|
||||||
|
if (deltaX < -changeRate) deltaX = -changeRate;
|
||||||
|
if (deltaX > changeRate) deltaX = changeRate;
|
||||||
|
double deltaZ = targetZ - currentZ;
|
||||||
|
if (deltaZ < -changeRate) deltaZ = -changeRate;
|
||||||
|
if (deltaZ > changeRate) deltaZ = changeRate;
|
||||||
|
|
||||||
|
currentX += deltaX;
|
||||||
|
currentZ += deltaZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
if (event.world.isRemote) changeWind(event.world);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
|
||||||
|
|
||||||
|
public class AlternateTextBooleanEntry extends GuiConfigEntries.ButtonEntry {
|
||||||
|
|
||||||
|
protected final boolean beforeValue;
|
||||||
|
protected boolean currentValue;
|
||||||
|
|
||||||
|
public AlternateTextBooleanEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<Boolean> configElement)
|
||||||
|
{
|
||||||
|
super(owningScreen, owningEntryList, configElement);
|
||||||
|
this.beforeValue = Boolean.valueOf(configElement.get().toString());
|
||||||
|
this.currentValue = beforeValue;
|
||||||
|
this.btnValue.enabled = enabled();
|
||||||
|
updateValueButtonText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateValueButtonText()
|
||||||
|
{
|
||||||
|
this.btnValue.displayString = I18n.format(configElement.getLanguageKey() + "." + String.valueOf(currentValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void valueButtonPressed(int slotIndex)
|
||||||
|
{
|
||||||
|
if (enabled())
|
||||||
|
currentValue = !currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefault()
|
||||||
|
{
|
||||||
|
return currentValue == Boolean.valueOf(configElement.getDefault().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setToDefault()
|
||||||
|
{
|
||||||
|
if (enabled())
|
||||||
|
{
|
||||||
|
currentValue = Boolean.valueOf(configElement.getDefault().toString());
|
||||||
|
updateValueButtonText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChanged()
|
||||||
|
{
|
||||||
|
return currentValue != beforeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoChanges()
|
||||||
|
{
|
||||||
|
if (enabled())
|
||||||
|
{
|
||||||
|
currentValue = beforeValue;
|
||||||
|
updateValueButtonText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public boolean saveConfigElement()
|
||||||
|
{
|
||||||
|
if (enabled() && isChanged())
|
||||||
|
{
|
||||||
|
configElement.set(currentValue);
|
||||||
|
return configElement.requiresMcRestart();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getCurrentValue()
|
||||||
|
{
|
||||||
|
return currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean[] getCurrentValues()
|
||||||
|
{
|
||||||
|
return new Boolean[] { getCurrentValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.BiomeUtils;
|
||||||
|
import net.minecraft.world.biome.BiomeGenBase;
|
||||||
|
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
|
||||||
|
|
||||||
|
public class BiomeListConfigEntry extends SelectListConfigEntry<BiomeGenBase> {
|
||||||
|
|
||||||
|
public BiomeListConfigEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
|
||||||
|
super(owningScreen, owningEntryList, configElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BiomeGenBase> getBaseSet(String name) {
|
||||||
|
return BiomeUtils.getAllBiomes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BiomeGenBase> getDefaultSelected(String name) {
|
||||||
|
if (name.equals("reedBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeTempRainFilter(0.4f, null, 0.4f, null)));
|
||||||
|
if (name.equals("algaeBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeClassNameFilter("river", "ocean")));
|
||||||
|
if (name.equals("coralBiomeList")) return Lists.newArrayList(Collections2.filter(getBaseSet(name), BiomeUtils.biomeClassNameFilter("river", "ocean", "beach")));
|
||||||
|
return ImmutableList.<BiomeGenBase>of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getItemId(BiomeGenBase item) {
|
||||||
|
return item.biomeID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getItemName(BiomeGenBase item) {
|
||||||
|
return item.biomeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTooltipLangKey(String name) {
|
||||||
|
if (name.equals("reedBiomeList")) return "betterfoliage.reeds.biomeSelectTooltip";
|
||||||
|
if (name.equals("algaeBiomeList")) return "betterfoliage.algae.biomeSelectTooltip";
|
||||||
|
if (name.equals("coralBiomeList")) return "betterfoliage.coral.biomeSelectTooltip";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.IModGuiFactory;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ConfigGuiFactory implements IModGuiFactory {
|
||||||
|
|
||||||
|
public void initialize(Minecraft minecraftInstance) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
||||||
|
return ConfigGuiBetterFoliage.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
|
||||||
|
return ImmutableSet.<RuntimeOptionCategoryElement>of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConfigGuiBetterFoliage extends GuiConfig {
|
||||||
|
public ConfigGuiBetterFoliage(GuiScreen parentScreen) {
|
||||||
|
super(parentScreen, Config.getConfigRootElements(), BetterFoliage.MOD_ID, null, false, false, BetterFoliage.MOD_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.RenderUtils;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
|
||||||
|
|
||||||
|
public class NonVerboseArrayEntry extends GuiConfigEntries.ArrayEntry {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public NonVerboseArrayEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
|
||||||
|
super(owningScreen, owningEntryList, configElement);
|
||||||
|
|
||||||
|
RenderUtils.stripTooltipDefaultText(toolTip);
|
||||||
|
String shortDefaults = I18n.format("betterfoliage.arrayEntryDisplay", configElement.getDefaults().length);
|
||||||
|
toolTip.addAll(this.mc.fontRenderer.listFormattedStringToWidth(EnumChatFormatting.AQUA + I18n.format("fml.configgui.tooltip.default", shortDefaults),300));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateValueButtonText() {
|
||||||
|
this.btnValue.displayString = I18n.format("betterfoliage.arrayEntryDisplay", currentValues.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package mods.betterfoliage.client.gui;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.RenderUtils;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.ConfigGuiType;
|
||||||
|
import cpw.mods.fml.client.config.DummyConfigElement;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries.CategoryEntry;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class SelectListConfigEntry<T> extends CategoryEntry {
|
||||||
|
|
||||||
|
List<ItemWrapperElement> children;
|
||||||
|
List<Integer> notFoundIdList;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public SelectListConfigEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement<?> configElement) {
|
||||||
|
super(owningScreen, owningEntryList, configElement);
|
||||||
|
RenderUtils.stripTooltipDefaultText(toolTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GuiScreen buildChildScreen()
|
||||||
|
{
|
||||||
|
return new GuiConfig(this.owningScreen, createChildElements(), this.owningScreen.modID,
|
||||||
|
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
|
||||||
|
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(), this.owningScreen.title,
|
||||||
|
((this.owningScreen.titleLine2 == null ? "" : this.owningScreen.titleLine2) + " > " + this.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract List<T> getBaseSet(String qualifiedName);
|
||||||
|
protected abstract List<T> getDefaultSelected(String qualifiedName);
|
||||||
|
protected abstract int getItemId(T item);
|
||||||
|
protected abstract String getItemName(T item);
|
||||||
|
protected abstract String getTooltipLangKey(String qualifiedName);
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
protected List<IConfigElement> createChildElements() {
|
||||||
|
children = Lists.newArrayList();
|
||||||
|
|
||||||
|
List<Integer> idList = Lists.newArrayList();
|
||||||
|
for (Object id : configElement.getList()) idList.add((Integer) id);
|
||||||
|
|
||||||
|
List<T> defaults = getDefaultSelected(configElement.getName());
|
||||||
|
for(T item : getBaseSet(configElement.getQualifiedName())) {
|
||||||
|
children.add(new ItemWrapperElement(item, defaults.contains(item), idList.contains(getItemId(item))));
|
||||||
|
idList.remove(new Integer(getItemId(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
notFoundIdList = idList;
|
||||||
|
List<IConfigElement> result = Lists.newArrayList();
|
||||||
|
result.addAll(children);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public boolean saveConfigElement() {
|
||||||
|
boolean requiresRestart = ((GuiConfig) childScreen).entryList.saveConfigElements();
|
||||||
|
|
||||||
|
Set<Integer> idSet = Sets.newHashSet();
|
||||||
|
for (ItemWrapperElement child : children)
|
||||||
|
if (Boolean.TRUE.equals(child.getCurrentValue()))
|
||||||
|
idSet.add(getItemId(child.item));
|
||||||
|
|
||||||
|
idSet.addAll(notFoundIdList);
|
||||||
|
List<Integer> result = Lists.newArrayList(idSet);
|
||||||
|
Collections.sort(result);
|
||||||
|
configElement.set(result.toArray());
|
||||||
|
|
||||||
|
return requiresRestart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ItemWrapperElement extends DummyConfigElement<Boolean> implements IConfigElement<Boolean> {
|
||||||
|
|
||||||
|
public T item;
|
||||||
|
|
||||||
|
public ItemWrapperElement(T item, boolean defaultValue, boolean currentValue) {
|
||||||
|
super(getItemName(item), defaultValue, ConfigGuiType.BOOLEAN, getItemName(item));
|
||||||
|
set(currentValue);
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getComment() {
|
||||||
|
return I18n.format(getTooltipLangKey(configElement.getQualifiedName()), EnumChatFormatting.GOLD + getItemName(item) + EnumChatFormatting.YELLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getCurrentValue() {
|
||||||
|
return (Boolean) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Boolean value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefault(Boolean value) {
|
||||||
|
this.defaultValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
|
||||||
|
/** Same as {@link RenderBlockAOBase}, but does not actually render anything.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class FakeRenderBlockAOBase extends RenderBlockAOBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoZNXYPP);
|
||||||
|
saveShadingTopRight(aoZNXYNP);
|
||||||
|
saveShadingBottomLeft(aoZNXYPN);
|
||||||
|
saveShadingBottomRight(aoZNXYNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoZPXYNP);
|
||||||
|
saveShadingTopRight(aoZPXYPP);
|
||||||
|
saveShadingBottomLeft(aoZPXYNN);
|
||||||
|
saveShadingBottomRight(aoZPXYPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoXNYZPN);
|
||||||
|
saveShadingTopRight(aoXNYZPP);
|
||||||
|
saveShadingBottomLeft(aoXNYZNN);
|
||||||
|
saveShadingBottomRight(aoXNYZNP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoXPYZPP);
|
||||||
|
saveShadingTopRight(aoXPYZPN);
|
||||||
|
saveShadingBottomLeft(aoXPYZNP);
|
||||||
|
saveShadingBottomRight(aoXPYZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoYNXZNP);
|
||||||
|
saveShadingTopRight(aoYNXZPP);
|
||||||
|
saveShadingBottomLeft(aoYNXZNN);
|
||||||
|
saveShadingBottomRight(aoYNXZPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
saveShadingTopLeft(aoYPXZPP);
|
||||||
|
saveShadingTopRight(aoYPXZNP);
|
||||||
|
saveShadingBottomLeft(aoYPXZPN);
|
||||||
|
saveShadingBottomRight(aoYPXZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
|
||||||
|
/** Block rendering handler that is only used under certain conditions
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public interface IRenderBlockDecorator extends ISimpleBlockRenderingHandler {
|
||||||
|
|
||||||
|
/** Initialize necessary helper values
|
||||||
|
*/
|
||||||
|
public void init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param blockAccess the world
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param z
|
||||||
|
* @param block
|
||||||
|
* @param original renderType of the block
|
||||||
|
* @return true if this renderer should handle this block
|
||||||
|
*/
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original);
|
||||||
|
|
||||||
|
}
|
||||||
52
src/main/java/mods/betterfoliage/client/render/IconSet.java
Normal file
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.ResourceUtils;
|
||||||
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Holds an indexed set of textures
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class IconSet {
|
||||||
|
|
||||||
|
/** Icon array */
|
||||||
|
public IIcon[] icons = new IIcon[16];
|
||||||
|
|
||||||
|
/** Number of successfully loaded icons */
|
||||||
|
public int numLoaded = 0;
|
||||||
|
|
||||||
|
/** Resource domain of icons */
|
||||||
|
String domain;
|
||||||
|
|
||||||
|
/** Format string of icon paths */
|
||||||
|
String path;
|
||||||
|
|
||||||
|
public IconSet(String domain, String path) {
|
||||||
|
this.domain = domain;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerIcons(IIconRegister register) {
|
||||||
|
numLoaded = 0;
|
||||||
|
for (int idx = 0; idx < 16; idx++) {
|
||||||
|
icons[idx] = null;
|
||||||
|
// if the path contains a domain, use that to check if the resource exists
|
||||||
|
String resolvedDomain = path.contains(":") ? new ResourceLocation(path).getResourceDomain() : domain;
|
||||||
|
String resolvedPath = String.format("textures/blocks/" + (path.contains(":") ? new ResourceLocation(path).getResourcePath() : path) + ".png", idx);
|
||||||
|
if (ResourceUtils.resourceExists(new ResourceLocation(resolvedDomain, resolvedPath)))
|
||||||
|
icons[numLoaded++] = register.registerIcon(domain + ":" + String.format(path, idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IIcon get(int variation) {
|
||||||
|
return numLoaded == 0 ? null : icons[variation % numLoaded];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasIcons() {
|
||||||
|
return numLoaded > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
package mods.betterfoliage.client.render;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import mods.betterfoliage.common.util.RenderUtils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Block renderer base class. Stores calculated ambient occlusion light and color values when rendering
|
||||||
|
* block sides for later use.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockAOBase extends RenderBlocks {
|
||||||
|
|
||||||
|
/** AO light and color values
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public static class ShadingValues {
|
||||||
|
public int passCounter = 0;
|
||||||
|
public int brightness;
|
||||||
|
public float red;
|
||||||
|
public float green;
|
||||||
|
public float blue;
|
||||||
|
public void setGray(float value) {
|
||||||
|
red = value; green = value; blue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
|
||||||
|
protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
|
||||||
|
|
||||||
|
protected ForgeDirection[] faceDir1 = new ForgeDirection[] {ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.EAST, ForgeDirection.SOUTH, ForgeDirection.NORTH};
|
||||||
|
protected ForgeDirection[] faceDir2 = new ForgeDirection[] {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP};
|
||||||
|
|
||||||
|
/** Random vector pool. Unit rotation vectors in the XZ plane, Y coord goes between [-1.0, 1.0].
|
||||||
|
* Filled at init time */
|
||||||
|
public Double3[] pRot = new Double3[64];
|
||||||
|
|
||||||
|
/** Pool of random double values. Filled at init time. */
|
||||||
|
public double[] pRand = new double[64];
|
||||||
|
|
||||||
|
public ShadingValues aoXPYZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoXPYZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoXNYZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoYPXZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZPP = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZPN = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZNP = new ShadingValues();
|
||||||
|
public ShadingValues aoYNXZNN = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYPP = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYPN = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYNP = new ShadingValues();
|
||||||
|
public ShadingValues aoZPXYNN = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYPP = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYPN = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYNP = new ShadingValues();
|
||||||
|
public ShadingValues aoZNXYNN = new ShadingValues();
|
||||||
|
|
||||||
|
// temporary shading values for a single face
|
||||||
|
public ShadingValues faceAOPP, faceAOPN, faceAONN, faceAONP;
|
||||||
|
|
||||||
|
/** Initialize random values */
|
||||||
|
public void init() {
|
||||||
|
List<Double3> perturbs = new ArrayList<Double3>(64);
|
||||||
|
for (int idx = 0; idx < 64; idx++) {
|
||||||
|
double angle = (double) idx * Math.PI * 2.0 / 64.0;
|
||||||
|
perturbs.add(new Double3(Math.cos(angle), Math.random() * 2.0 - 1.0, Math.sin(angle)));
|
||||||
|
pRand[idx] = Math.random();
|
||||||
|
}
|
||||||
|
Collections.shuffle(perturbs);
|
||||||
|
Iterator<Double3> iter = perturbs.iterator();
|
||||||
|
for (int idx = 0; idx < 64; idx++) pRot[idx] = iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a semi-random value depending on block position.
|
||||||
|
* @param x block X coord
|
||||||
|
* @param y block Y coord
|
||||||
|
* @param z block Z coord
|
||||||
|
* @param seed additional seed
|
||||||
|
* @return semirandom value
|
||||||
|
*/
|
||||||
|
protected int getSemiRandomFromPos(double x, double y, double z, int seed) {
|
||||||
|
long lx = MathHelper.floor_double(x);
|
||||||
|
long ly = MathHelper.floor_double(y);
|
||||||
|
long lz = MathHelper.floor_double(z);
|
||||||
|
long value = (lx * lx + ly * ly + lz * lz + lx * ly + ly * lz + lz * lx + seed * seed) & 63;
|
||||||
|
value = (3 * lx * value + 5 * ly * value + 7 * lz * value + 11 * seed) & 63;
|
||||||
|
return (int) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
|
||||||
|
renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldRender3DInInventory(int modelId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRenderId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderWorldBlockBase(int pass, IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
ISimpleBlockRenderingHandler handler = RenderUtils.getRenderingHandler(block.getRenderType());
|
||||||
|
if (handler != null) {
|
||||||
|
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
|
||||||
|
} else {
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderStandardBlockAsItem(RenderBlocks renderer, Block p_147800_1_, int p_147800_2_, float p_147800_3_) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
boolean flag = p_147800_1_ == Blocks.grass;
|
||||||
|
|
||||||
|
float f2;
|
||||||
|
float f3;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
p_147800_1_.setBlockBoundsForItemRender();
|
||||||
|
renderer.setRenderBoundsFromBlock(p_147800_1_);
|
||||||
|
GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
|
||||||
|
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, -1.0F, 0.0F);
|
||||||
|
renderer.renderFaceYNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 0, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
if (flag && renderer.useInventoryTint)
|
||||||
|
{
|
||||||
|
k = p_147800_1_.getRenderColor(p_147800_2_);
|
||||||
|
f2 = (float)(k >> 16 & 255) / 255.0F;
|
||||||
|
f3 = (float)(k >> 8 & 255) / 255.0F;
|
||||||
|
float f4 = (float)(k & 255) / 255.0F;
|
||||||
|
GL11.glColor4f(f2 * p_147800_3_, f3 * p_147800_3_, f4 * p_147800_3_, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 1.0F, 0.0F);
|
||||||
|
renderer.renderFaceYPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 1, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
if (flag && renderer.useInventoryTint)
|
||||||
|
{
|
||||||
|
GL11.glColor4f(p_147800_3_, p_147800_3_, p_147800_3_, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 0.0F, -1.0F);
|
||||||
|
renderer.renderFaceZNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 2, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(0.0F, 0.0F, 1.0F);
|
||||||
|
renderer.renderFaceZPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 3, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
|
||||||
|
renderer.renderFaceXNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 4, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setNormal(1.0F, 0.0F, 0.0F);
|
||||||
|
renderer.renderFaceXPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 5, p_147800_2_));
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glTranslatef(0.5F, 0.5F, 0.5F);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setShadingForFace(ForgeDirection dir) {
|
||||||
|
if (dir == ForgeDirection.DOWN) {
|
||||||
|
// dir1 WEST, dir2 NORTH
|
||||||
|
faceAOPP = aoYNXZPP; faceAOPN = aoYNXZPN; faceAONN = aoYNXZNN; faceAONP = aoYNXZNP;
|
||||||
|
} else if (dir == ForgeDirection.UP) {
|
||||||
|
// dir1 WEST, dir2 SOUTH
|
||||||
|
faceAOPP = aoYPXZPP; faceAOPN = aoYPXZPN; faceAONN = aoYPXZNN; faceAONP = aoYPXZNP;
|
||||||
|
} else if (dir == ForgeDirection.NORTH) {
|
||||||
|
// dir1 WEST, dir2 UP
|
||||||
|
faceAOPP = aoZNXYNP; faceAOPN = aoZNXYNN; faceAONN = aoZNXYPN; faceAONP = aoZNXYPP;
|
||||||
|
} else if (dir == ForgeDirection.SOUTH) {
|
||||||
|
// dir1 EAST, dir2 UP
|
||||||
|
faceAOPP = aoZPXYPP; faceAOPN = aoZPXYPN; faceAONN = aoZPXYNN; faceAONP = aoZPXYNP;
|
||||||
|
} else if (dir == ForgeDirection.WEST) {
|
||||||
|
// dir1 SOUTH, dir2 UP
|
||||||
|
faceAOPP = aoXNYZPP; faceAOPN = aoXNYZNP; faceAONN = aoXNYZNN; faceAONP = aoXNYZPN;
|
||||||
|
} else if (dir == ForgeDirection.EAST) {
|
||||||
|
// dir1 NORTH, dir2 UP
|
||||||
|
faceAOPP = aoXPYZPN; faceAOPN = aoXPYZNN; faceAONN = aoXPYZNP; faceAONP = aoXPYZPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderCrossedSideQuads(Double3 drawBase, ForgeDirection dir, double scale, double halfHeight, Double3 rendomVec, double offset, IIcon renderIcon, int uvRot, boolean noShading) {
|
||||||
|
Double3 facePP, faceNP, faceNormal, drawCenter;
|
||||||
|
|
||||||
|
if (dir == ForgeDirection.UP) {
|
||||||
|
// special case for block top, we'll be rendering a LOT of those
|
||||||
|
facePP = new Double3(-scale, 0.0, scale);
|
||||||
|
faceNP = new Double3(scale, 0.0, scale);
|
||||||
|
faceNormal = new Double3(0.0, halfHeight, 0.0);
|
||||||
|
drawCenter = drawBase.add(faceNormal);
|
||||||
|
if (rendomVec != null) {
|
||||||
|
drawCenter = drawBase.add(faceNormal).add(rendomVec.scaleAxes(-offset, 0.0, offset));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
facePP = new Double3(faceDir1[dir.ordinal()]).add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||||
|
faceNP = new Double3(faceDir1[dir.ordinal()]).inverse().add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
|
||||||
|
faceNormal = new Double3(dir).scale(halfHeight);
|
||||||
|
drawCenter = drawBase.add(faceNormal);
|
||||||
|
if (rendomVec != null) {
|
||||||
|
drawCenter = drawCenter.add(new Double3(faceDir1[dir.ordinal()]).scale(rendomVec.x).scale(offset))
|
||||||
|
.add(new Double3(faceDir2[dir.ordinal()]).scale(rendomVec.z).scale(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled() && !noShading) {
|
||||||
|
setShadingForFace(dir);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, facePP, faceNormal, uvRot, faceAOPP, faceAONN, faceAONN, faceAOPP);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot, faceAONN, faceAOPP, faceAOPP, faceAONN);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, faceNP, faceNormal, uvRot, faceAONP, faceAOPN, faceAOPN, faceAONP);
|
||||||
|
renderQuadWithShading(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot, faceAOPN, faceAONP, faceAONP, faceAOPN);
|
||||||
|
} else {
|
||||||
|
renderQuad(renderIcon, drawCenter, facePP, faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, faceNP, faceNormal, uvRot);
|
||||||
|
renderQuad(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCrossedBlockQuadsTranslate(Double3 blockCenter, double halfSize, Double3 offsetVec, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
Double3 drawCenter = blockCenter;
|
||||||
|
if (offsetVec != null) drawCenter = drawCenter.add(offsetVec);
|
||||||
|
Double3 horz1 = new Double3(halfSize, 0.0, halfSize);
|
||||||
|
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize);
|
||||||
|
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||||
|
|
||||||
|
renderCrossedBlockQuadsInternal(drawCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCrossedBlockQuadsSkew(Double3 blockCenter, double halfSize, Double3 offsetVec1, Double3 offsetVec2, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
Double3 horz1 = new Double3(halfSize, 0.0, halfSize).add(offsetVec1);
|
||||||
|
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize).add(offsetVec2);
|
||||||
|
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
|
||||||
|
|
||||||
|
renderCrossedBlockQuadsInternal(blockCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderCrossedBlockQuadsInternal(Double3 drawCenter, Double3 horz1, Double3 horz2, Double3 vert1, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz1, vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZPP : aoZPXYPP, isAirTop ? aoYPXZNN : aoXNYZPN, isAirBottom ? aoYNXZNN : aoXNYZNN, isAirBottom ? aoYNXZPP : aoZPXYPN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZNN : aoZNXYNP, isAirTop ? aoYPXZPP : aoXPYZPP, isAirBottom ? aoYNXZPP : aoXPYZNP, isAirBottom ? aoYNXZNN : aoZNXYNN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz2, vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZPN : aoXPYZPN, isAirTop ? aoYPXZNP : aoZPXYNP, isAirBottom ? aoYNXZNP : aoZPXYNN, isAirBottom ? aoYNXZPN : aoXPYZNN);
|
||||||
|
renderQuadWithShading(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot,
|
||||||
|
isAirTop ? aoYPXZNP : aoXNYZPP, isAirTop ? aoYPXZPN : aoZNXYPP, isAirBottom ? aoYNXZPN : aoZNXYPN, isAirBottom ? aoYNXZNP : aoXNYZNP);
|
||||||
|
} else {
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz1, vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz2, vert1, uvRot);
|
||||||
|
renderQuad(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoZNXYPP);
|
||||||
|
saveShadingTopRight(aoZNXYNP);
|
||||||
|
saveShadingBottomLeft(aoZNXYPN);
|
||||||
|
saveShadingBottomRight(aoZNXYNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoZPXYNP);
|
||||||
|
saveShadingTopRight(aoZPXYPP);
|
||||||
|
saveShadingBottomLeft(aoZPXYNN);
|
||||||
|
saveShadingBottomRight(aoZPXYPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoXNYZPN);
|
||||||
|
saveShadingTopRight(aoXNYZPP);
|
||||||
|
saveShadingBottomLeft(aoXNYZNN);
|
||||||
|
saveShadingBottomRight(aoXNYZNP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoXPYZPP);
|
||||||
|
saveShadingTopRight(aoXPYZPN);
|
||||||
|
saveShadingBottomLeft(aoXPYZNP);
|
||||||
|
saveShadingBottomRight(aoXPYZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceYNeg(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoYNXZNP);
|
||||||
|
saveShadingTopRight(aoYNXZPP);
|
||||||
|
saveShadingBottomLeft(aoYNXZNN);
|
||||||
|
saveShadingBottomRight(aoYNXZPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceYPos(block, x, y, z, icon);
|
||||||
|
saveShadingTopLeft(aoYPXZPP);
|
||||||
|
saveShadingTopRight(aoYPXZNP);
|
||||||
|
saveShadingBottomLeft(aoYPXZPN);
|
||||||
|
saveShadingBottomRight(aoYPXZNN);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingTopLeft(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessTopLeft;
|
||||||
|
values.red = colorRedTopLeft;
|
||||||
|
values.green = colorGreenTopLeft;
|
||||||
|
values.blue = colorBlueTopLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingTopRight(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessTopRight;
|
||||||
|
values.red = colorRedTopRight;
|
||||||
|
values.green = colorGreenTopRight;
|
||||||
|
values.blue = colorBlueTopRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingBottomLeft(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessBottomLeft;
|
||||||
|
values.red = colorRedBottomLeft;
|
||||||
|
values.green = colorGreenBottomLeft;
|
||||||
|
values.blue = colorBlueBottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveShadingBottomRight(ShadingValues values) {
|
||||||
|
if (--values.passCounter != 0) return;
|
||||||
|
values.brightness = brightnessBottomRight;
|
||||||
|
values.red = colorRedBottomRight;
|
||||||
|
values.green = colorGreenBottomRight;
|
||||||
|
values.blue = colorBlueBottomRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set pass counter on all shading value objects.
|
||||||
|
* Used to collect AO values from a specific draw pass
|
||||||
|
* if the underlying renderer draws overlays
|
||||||
|
* @param value pass counter
|
||||||
|
*/
|
||||||
|
protected void setPassCounters(int value) {
|
||||||
|
aoXPYZPP.passCounter = value;
|
||||||
|
aoXPYZPN.passCounter = value;
|
||||||
|
aoXPYZNP.passCounter = value;
|
||||||
|
aoXPYZNN.passCounter = value;
|
||||||
|
aoXNYZPP.passCounter = value;
|
||||||
|
aoXNYZPN.passCounter = value;
|
||||||
|
aoXNYZNP.passCounter = value;
|
||||||
|
aoXNYZNN.passCounter = value;
|
||||||
|
aoYPXZPP.passCounter = value;
|
||||||
|
aoYPXZPN.passCounter = value;
|
||||||
|
aoYPXZNP.passCounter = value;
|
||||||
|
aoYPXZNN.passCounter = value;
|
||||||
|
aoYNXZPP.passCounter = value;
|
||||||
|
aoYNXZPN.passCounter = value;
|
||||||
|
aoYNXZNP.passCounter = value;
|
||||||
|
aoYNXZNN.passCounter = value;
|
||||||
|
aoZPXYPP.passCounter = value;
|
||||||
|
aoZPXYPN.passCounter = value;
|
||||||
|
aoZPXYNP.passCounter = value;
|
||||||
|
aoZPXYNN.passCounter = value;
|
||||||
|
aoZNXYPP.passCounter = value;
|
||||||
|
aoZNXYPN.passCounter = value;
|
||||||
|
aoZNXYNP.passCounter = value;
|
||||||
|
aoZNXYNN.passCounter = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render textured quad
|
||||||
|
* @param icon texture to use
|
||||||
|
* @param center center of quad
|
||||||
|
* @param vec1 vector to the half-point of one of the sides
|
||||||
|
* @param vec2 vector to half-point of side next to vec1
|
||||||
|
* @param uvRot number of increments to rotate UV coordinates by
|
||||||
|
*/
|
||||||
|
protected void renderQuad(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render textured quad using AO information
|
||||||
|
* @param icon texture to use
|
||||||
|
* @param center center of quad
|
||||||
|
* @param vec1 vector to the half-point of one of the sides
|
||||||
|
* @param vec2 vector to half-point of side next to vec1
|
||||||
|
* @param uvRot number of increments to rotate UV coordinates by
|
||||||
|
* @param aoPP AO values for vertex at (+vec1, +vec2)
|
||||||
|
* @param aoNP AO values for vertex at (-vec1, +vec2)
|
||||||
|
* @param aoNN AO values for vertex at (-vec1, -vec2)
|
||||||
|
* @param aoPN AO values for vertex at (+vec1, -vec2)
|
||||||
|
*/
|
||||||
|
protected void renderQuadWithShading(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot, ShadingValues aoPP, ShadingValues aoNP, ShadingValues aoNN, ShadingValues aoPN) {
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
tessellator.setBrightness(aoPP.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoPP.red, aoPP.green, aoPP.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
|
||||||
|
tessellator.setBrightness(aoNP.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoNP.red, aoNP.green, aoNP.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
|
||||||
|
tessellator.setBrightness(aoNN.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoNN.red, aoNN.green, aoNN.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
|
||||||
|
tessellator.setBrightness(aoPN.brightness);
|
||||||
|
tessellator.setColorOpaque_F(aoPN.red, aoPN.green, aoPN.blue);
|
||||||
|
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getBrightness(Block block, int x, int y, int z) {
|
||||||
|
return block.getMixedBrightnessForBlock(blockAccess, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.particle.EntityFX;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class EntityFXFallingLeaves extends EntityFX {
|
||||||
|
|
||||||
|
protected static double[] cos = new double[64];
|
||||||
|
protected static double[] sin = new double[64];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int idx = 0; idx < 64; idx++) {
|
||||||
|
cos[idx] = Math.cos(2.0 * Math.PI / 64.0 * idx);
|
||||||
|
sin[idx] = Math.sin(2.0 * Math.PI / 64.0 * idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float biomeBrightnessMultiplier = 0.5f;
|
||||||
|
public boolean wasOnGround = false;
|
||||||
|
public boolean isMirrored;
|
||||||
|
public int particleRotation = 0;
|
||||||
|
public boolean rotationPositive = true;
|
||||||
|
|
||||||
|
public EntityFXFallingLeaves(World world, int x, int y, int z) {
|
||||||
|
super(world, x + 0.5, y, z + 0.5);
|
||||||
|
particleMaxAge = MathHelper.floor_double((0.6 + 0.4 * rand.nextDouble()) * Config.leafFXLifetime * 20.0);
|
||||||
|
isMirrored = (rand.nextInt() & 1) == 1;
|
||||||
|
motionY = -Config.leafFXSpeed;
|
||||||
|
particleRotation = rand.nextInt(64);
|
||||||
|
particleScale = (float) Config.leafFXSize;
|
||||||
|
|
||||||
|
|
||||||
|
Block block = world.getBlock(x, y, z);
|
||||||
|
IIcon blockIcon = block.getIcon(world, x, y, z, ForgeDirection.DOWN.ordinal());
|
||||||
|
particleIcon = BetterFoliageClient.leafParticles.getIconSet(blockIcon).get(rand.nextInt(1024));
|
||||||
|
calculateParticleColor(BetterFoliageClient.leafParticles.getColor(blockIcon), block.colorMultiplier(world, x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate() {
|
||||||
|
super.onUpdate();
|
||||||
|
|
||||||
|
particleScale = (float) Config.leafFXSize;
|
||||||
|
if (rand.nextFloat() > 0.95f) rotationPositive = !rotationPositive;
|
||||||
|
if (particleAge > particleMaxAge - 20) particleAlpha = 0.05f * (particleMaxAge - particleAge);
|
||||||
|
|
||||||
|
if (onGround || wasOnGround) {
|
||||||
|
motionX = 0.0;
|
||||||
|
motionZ = 0.0;
|
||||||
|
motionZ = 0.0;
|
||||||
|
if (!wasOnGround) {
|
||||||
|
particleAge = Math.max(particleAge, particleMaxAge - 20);
|
||||||
|
}
|
||||||
|
wasOnGround = true;
|
||||||
|
} else {
|
||||||
|
motionX = (BetterFoliageClient.wind.currentX + cos[particleRotation] * Config.leafFXPerturb) * Config.leafFXSpeed;
|
||||||
|
motionZ = (BetterFoliageClient.wind.currentZ + sin[particleRotation] * Config.leafFXPerturb) * Config.leafFXSpeed;
|
||||||
|
motionY = -Config.leafFXSpeed;
|
||||||
|
particleRotation = (particleRotation + (rotationPositive ? 1 : -1)) & 63;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderParticle(Tessellator tessellator, float partialTickTime, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ)
|
||||||
|
{
|
||||||
|
float minU = isMirrored ? particleIcon.getMinU() : particleIcon.getMaxU();
|
||||||
|
float maxU = isMirrored ? particleIcon.getMaxU() : particleIcon.getMinU();
|
||||||
|
float minV = particleIcon.getMinV();
|
||||||
|
float maxV = particleIcon.getMaxV();
|
||||||
|
float scale = 0.1F * this.particleScale;
|
||||||
|
|
||||||
|
Double3 center = new Double3(prevPosX + (posX - prevPosX) * partialTickTime - interpPosX,
|
||||||
|
prevPosY + (posY - prevPosY) * partialTickTime - interpPosY,
|
||||||
|
prevPosZ + (posZ - prevPosZ) * partialTickTime - interpPosZ);
|
||||||
|
Double3 vec1 = new Double3(rotX + rotXY, rotZ, rotYZ + rotXZ).scale(scale);
|
||||||
|
Double3 vec2 = new Double3(rotX - rotXY, -rotZ, rotYZ - rotXZ).scale(scale);
|
||||||
|
Double3 vec1Rot = vec1.scale(cos[particleRotation]).add(vec2.scale(sin[particleRotation]));
|
||||||
|
Double3 vec2Rot = vec1.scale(-sin[particleRotation]).add(vec2.scale(cos[particleRotation]));
|
||||||
|
|
||||||
|
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha);
|
||||||
|
addVertex(tessellator, center.sub(vec1Rot), maxU, maxV);
|
||||||
|
addVertex(tessellator, center.sub(vec2Rot), maxU, minV);
|
||||||
|
addVertex(tessellator, center.add(vec1Rot), minU, minV);
|
||||||
|
addVertex(tessellator, center.add(vec2Rot), minU, maxV);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addVertex(Tessellator tessellator, Double3 coord, double u, double v) {
|
||||||
|
tessellator.addVertexWithUV(coord.x, coord.y, coord.z, u, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculates and sets the color of the particle by blending the average color of the block texture with the current biome color
|
||||||
|
* Blending is done in HSB color space, weighted by the relative saturation of the colors
|
||||||
|
* @param textureAvgColor average color of the block texture
|
||||||
|
* @param blockColor biome color at the spawning block
|
||||||
|
*/
|
||||||
|
public void calculateParticleColor(int textureAvgColor, int blockColor) {
|
||||||
|
float[] hsbTexture = Color.RGBtoHSB((textureAvgColor >> 16) & 0xFF, (textureAvgColor >> 8) & 0xFF, textureAvgColor & 0xFF, null);
|
||||||
|
float[] hsbBlock = Color.RGBtoHSB((blockColor >> 16) & 0xFF, (blockColor >> 8) & 0xFF, blockColor & 0xFF, null);
|
||||||
|
|
||||||
|
float weightTex = hsbTexture[1] / (hsbTexture[1] + hsbBlock[1]);
|
||||||
|
float weightBlock = 1.0f - weightTex;
|
||||||
|
|
||||||
|
// avoid circular average for hue for performance reasons
|
||||||
|
// one of the color components should dominate anyway
|
||||||
|
float h = weightTex * hsbTexture[0] + weightBlock * hsbBlock[0];
|
||||||
|
float s = weightTex * hsbTexture[1] + weightBlock * hsbBlock[1];
|
||||||
|
float b = weightTex * hsbTexture[2] + weightBlock * hsbBlock[2] * biomeBrightnessMultiplier;
|
||||||
|
int particleColor = Color.HSBtoRGB(h, s, b);
|
||||||
|
|
||||||
|
particleBlue = (particleColor & 0xFF) / 256.0f;
|
||||||
|
particleGreen = ((particleColor >> 8) & 0xFF) / 256.0f;
|
||||||
|
particleRed = ((particleColor >> 16) & 0xFF) / 256.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFXLayer() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.client.particle.EntityFX;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class EntityFXRisingSoul extends EntityFX {
|
||||||
|
|
||||||
|
protected static double[] cos = new double[64];
|
||||||
|
protected static double[] sin = new double[64];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int idx = 0; idx < 64; idx++) {
|
||||||
|
cos[idx] = Math.cos(2.0 * Math.PI / 64.0 * idx);
|
||||||
|
sin[idx] = Math.sin(2.0 * Math.PI / 64.0 * idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int initialPhase;
|
||||||
|
|
||||||
|
public Deque<Double3> particleTrail = Lists.newLinkedList();
|
||||||
|
|
||||||
|
public EntityFXRisingSoul(World world, int x, int y, int z) {
|
||||||
|
super(world, x + 0.5, y + 1.0, z + 0.5);
|
||||||
|
|
||||||
|
motionY = 0.1f;
|
||||||
|
particleGravity = 0.0f;
|
||||||
|
|
||||||
|
particleIcon = BetterFoliageClient.soulParticles.soulHeadIcons.get(rand.nextInt(256));
|
||||||
|
particleMaxAge = MathHelper.floor_double((0.6 + 0.4 * rand.nextDouble()) * Config.soulFXLifetime * 20.0);
|
||||||
|
initialPhase = rand.nextInt(64);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate() {
|
||||||
|
super.onUpdate();
|
||||||
|
|
||||||
|
int phase = (initialPhase + particleAge) % 64;
|
||||||
|
|
||||||
|
motionY = 0.1f;
|
||||||
|
motionX = cos[phase] * Config.soulFXPerturb;
|
||||||
|
motionZ = sin[phase] * Config.soulFXPerturb;
|
||||||
|
|
||||||
|
particleTrail.addFirst(new Double3(posX, posY, posZ));
|
||||||
|
while (particleTrail.size() > Config.soulFXTrailLength) particleTrail.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderParticle(Tessellator tessellator, float partialTickTime, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ)
|
||||||
|
{
|
||||||
|
Double3 vec1 = new Double3(rotX + rotXY, rotZ, rotYZ + rotXZ);
|
||||||
|
Double3 vec2 = new Double3(rotX - rotXY, -rotZ, rotYZ - rotXZ);
|
||||||
|
|
||||||
|
Iterator<Double3> iter = particleTrail.iterator();
|
||||||
|
Double3 current, previous;
|
||||||
|
IIcon renderIcon = particleIcon;
|
||||||
|
double scale = Config.soulFXHeadSize * 0.25;
|
||||||
|
float alpha = (float) Config.soulFXOpacity;
|
||||||
|
if (particleAge > particleMaxAge - 40) alpha *= (particleMaxAge - particleAge) / 40.0f;
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
previous = iter.next();
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
current = previous;
|
||||||
|
previous = iter.next();
|
||||||
|
|
||||||
|
if (idx++ % Config.soulFXTrailDensity == 0) {
|
||||||
|
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, alpha);
|
||||||
|
renderParticleQuad(tessellator, partialTickTime, current, previous, vec1, vec2, renderIcon, scale);
|
||||||
|
}
|
||||||
|
if (idx == 1) {
|
||||||
|
// set initial trail particle size and icon after rendering head
|
||||||
|
scale = Config.soulFXTrailSize * 0.25;
|
||||||
|
renderIcon = BetterFoliageClient.soulParticles.soulTrackIcon;
|
||||||
|
}
|
||||||
|
scale *= Config.soulFXSizeDecay;
|
||||||
|
alpha *= Config.soulFXOpacityDecay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderParticleQuad(Tessellator tessellator, float partialTickTime, Double3 currentPos, Double3 previousPos, Double3 vec1, Double3 vec2, IIcon icon, double scale) {
|
||||||
|
float minU = icon.getMinU();
|
||||||
|
float maxU = icon.getMaxU();
|
||||||
|
float minV = icon.getMinV();
|
||||||
|
float maxV = icon.getMaxV();
|
||||||
|
|
||||||
|
Double3 center = new Double3(previousPos.x + (currentPos.x - previousPos.x) * partialTickTime - interpPosX,
|
||||||
|
previousPos.y + (currentPos.y - previousPos.y) * partialTickTime - interpPosY,
|
||||||
|
previousPos.z + (currentPos.z - previousPos.z) * partialTickTime - interpPosZ);
|
||||||
|
|
||||||
|
addVertex(tessellator, center.sub(vec1.scale(scale)), maxU, maxV);
|
||||||
|
addVertex(tessellator, center.sub(vec2.scale(scale)), maxU, minV);
|
||||||
|
addVertex(tessellator, center.add(vec1.scale(scale)), minU, minV);
|
||||||
|
addVertex(tessellator, center.add(vec2.scale(scale)), minU, maxV);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addVertex(Tessellator tessellator, Double3 coord, double u, double v) {
|
||||||
|
tessellator.addVertexWithUV(coord.x, coord.y, coord.z, u, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFXLayer() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.gen.NoiseGeneratorSimplex;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterAlgae extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet algaeIcons = new IconSet("bettergrassandleaves", "better_algae_%d");
|
||||||
|
public NoiseGeneratorSimplex noise;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.algaeEnabled) return false;
|
||||||
|
if (!Config.dirt.matchesID(block)) return false;
|
||||||
|
if (!Config.algaeBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 2, z).getMaterial() != Material.water) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < Config.algaePopulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.setRenderBoundsFromBlock(block);
|
||||||
|
renderer.renderStandardBlock(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render dirt block
|
||||||
|
setPassCounters(1);
|
||||||
|
setRenderBoundsFromBlock(block);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
int variation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
|
||||||
|
IIcon renderIcon = algaeIcons.get(variation);
|
||||||
|
if (renderIcon == null) return true;
|
||||||
|
|
||||||
|
double scale = Config.algaeSize * 0.5;
|
||||||
|
double halfHeight = 0.5 * (Config.algaeHeightMin + pRand[heightVariation] * (Config.algaeHeightMax - Config.algaeHeightMin));
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], Config.algaeHOffset, renderIcon, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
algaeIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d algae textures", algaeIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterCactus extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IIcon cactusRoundIcon;
|
||||||
|
public IconSet cactusSideIcons = new IconSet("bettergrassandleaves", "better_cactus_arm_%d");
|
||||||
|
|
||||||
|
/** Possible directions for cactus side growth*/
|
||||||
|
public static ForgeDirection[] cactusDirections = new ForgeDirection[] { ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.EAST, ForgeDirection.WEST};
|
||||||
|
|
||||||
|
/** Inner radius of cactus stem */
|
||||||
|
public static double cactusRadius = 0.4375;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
return Config.cactusEnabled && block == Blocks.cactus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.renderBlockCactus(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render cactus center
|
||||||
|
setPassCounters(1);
|
||||||
|
renderStandardBlock(block, x, y, z);
|
||||||
|
|
||||||
|
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block,x, y, z));
|
||||||
|
renderCactusCore(block.getBlockTextureFromSide(ForgeDirection.UP.ordinal()),
|
||||||
|
block.getBlockTextureFromSide(ForgeDirection.NORTH.ordinal()),
|
||||||
|
blockCenter, 0);
|
||||||
|
|
||||||
|
// render side growth
|
||||||
|
ForgeDirection drawDirection = cactusDirections[getSemiRandomFromPos(x, y, z, 0) % 4];
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
Double3 drawBase = blockCenter.add(new Double3(drawDirection).scale(cactusRadius));
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
if (cactusSideIcons.hasIcons()) renderCrossedSideQuads(drawBase, drawDirection, 0.5, 0.5, pRot[iconVariation], 0.2, cactusSideIcons.get(iconVariation), 0, false);
|
||||||
|
renderCrossedBlockQuadsSkew(blockCenter, 0.65,
|
||||||
|
pRot[iconVariation].scaleAxes(0.1, 0.0, 0.1),
|
||||||
|
pRot[(iconVariation + 1) & 63].scaleAxes(0.1, 0.0, 0.1),
|
||||||
|
cactusRoundIcon, iconVariation, false, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCactusCore(IIcon topIcon, IIcon sideIcon, Double3 blockCenter, int sideUvRot) {
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXPYZPN, aoXPYZPP, aoXPYZNP, aoXPYZNN);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXNYZPP, aoXNYZPN, aoXNYZNN, aoXNYZNP);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZPXYPP, aoZPXYNP, aoZPXYNN, aoZPXYPN);
|
||||||
|
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZNXYNP, aoZNXYPP, aoZNXYPN, aoZNXYNN);
|
||||||
|
renderQuadWithShading(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0, aoYPXZNP, aoYPXZPP, aoYPXZPN, aoYPXZNN);
|
||||||
|
} else {
|
||||||
|
renderQuad(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
|
||||||
|
renderQuad(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
cactusRoundIcon = event.map.registerIcon("bettergrassandleaves:better_cactus");
|
||||||
|
cactusSideIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d cactus arm textures", cactusSideIcons.numLoaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.gen.NoiseGeneratorSimplex;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class RenderBlockBetterCoral extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet coralCrustIcons = new IconSet("bettergrassandleaves", "better_crust_%d");
|
||||||
|
public IconSet coralCrossIcons = new IconSet("bettergrassandleaves", "better_coral_%d");
|
||||||
|
public NoiseGeneratorSimplex noise;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.coralEnabled) return false;
|
||||||
|
if (block != Blocks.sand) return false;
|
||||||
|
if (!Config.coralBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x * 0.1, z * 0.1) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < Config.coralPopulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
|
||||||
|
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
double offset = pRand[getSemiRandomFromPos(x, y, z, 6)] * Config.coralVOffset;
|
||||||
|
double halfSize = Config.coralSize * 0.5;
|
||||||
|
double halfCrustSize = Config.coralCrustSize * 0.5;
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
|
||||||
|
if (blockAccess.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ).getMaterial() != Material.water) continue;
|
||||||
|
if (blockAccess.isAirBlock(x + dir.offsetX, y + dir.offsetY + 1, z + dir.offsetZ)) continue;
|
||||||
|
|
||||||
|
int variation = getSemiRandomFromPos(x, y, z, dir.ordinal());
|
||||||
|
if (variation < Config.coralChance) {
|
||||||
|
IIcon crustIcon = coralCrustIcons.get(variation);
|
||||||
|
IIcon coralIcon = coralCrossIcons.get(variation);
|
||||||
|
if (crustIcon != null) renderCoralCrust(blockCenter, dir, offset, halfCrustSize, crustIcon, variation);
|
||||||
|
if (coralIcon != null) renderCrossedSideQuads(blockCenter.add(new Double3(dir).scale(0.5)), dir,
|
||||||
|
halfSize, halfSize,
|
||||||
|
pRot[variation], Config.coralHOffset,
|
||||||
|
coralIcon, 0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCoralCrust(Double3 blockCenter, ForgeDirection dir, double offset, double scale, IIcon icon, int uvRot) {
|
||||||
|
Double3 face1 = new Double3(faceDir1[dir.ordinal()]).scale(scale);
|
||||||
|
Double3 face2 = new Double3(faceDir2[dir.ordinal()]).scale(scale);
|
||||||
|
Double3 drawCenter = blockCenter.add(new Double3(dir).scale(0.5 + offset));
|
||||||
|
if (Minecraft.isAmbientOcclusionEnabled()) {
|
||||||
|
setShadingForFace(dir);
|
||||||
|
renderQuadWithShading(icon, drawCenter, face1, face2, uvRot, faceAOPP, faceAONP, faceAONN, faceAOPN);
|
||||||
|
} else {
|
||||||
|
renderQuad(icon, drawCenter, face1, face2, uvRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
coralCrustIcons.registerIcons(event.map);
|
||||||
|
coralCrossIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d coral crust textures", coralCrustIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d coral textures", coralCrossIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterGrass extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet grassIcons = new IconSet("bettergrassandleaves", "better_grass_long_%d");
|
||||||
|
public IconSet snowGrassIcons = new IconSet("bettergrassandleaves", "better_grass_snowed_%d");
|
||||||
|
public IIcon grassGenIcon;
|
||||||
|
public IIcon snowGrassGenIcon;
|
||||||
|
|
||||||
|
protected IIcon grassTopIcon;
|
||||||
|
boolean isSnowTop;
|
||||||
|
protected boolean connectXP, connectXN, connectZP, connectZN;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
return Config.grass.matchesID(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// check for connected grass
|
||||||
|
Material topMaterial = blockAccess.getBlock(x, y + 1, z).getMaterial();
|
||||||
|
isSnowTop = (topMaterial == Material.snow || topMaterial == Material.craftedSnow);
|
||||||
|
checkConnectedGrass(x, y, z);
|
||||||
|
grassTopIcon = block.getIcon(blockAccess, x, y, z, ForgeDirection.UP.ordinal());
|
||||||
|
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
if (!Config.grassEnabled) return true;
|
||||||
|
boolean isAirTop = blockAccess.isAirBlock(x, y + 1, z);
|
||||||
|
|
||||||
|
if (isSnowTop || isAirTop) {
|
||||||
|
// render short grass
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
|
||||||
|
double scale = Config.grassSize * 0.5;
|
||||||
|
double halfHeight = 0.5 * (Config.grassHeightMin + pRand[heightVariation] * (Config.grassHeightMax - Config.grassHeightMin));
|
||||||
|
|
||||||
|
IIcon shortGrassIcon = null;
|
||||||
|
if (isSnowTop) {
|
||||||
|
// clear biome colors
|
||||||
|
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
|
||||||
|
Tessellator.instance.setColorOpaque(230, 230, 230);
|
||||||
|
shortGrassIcon = Config.grassUseGenerated ? snowGrassGenIcon : snowGrassIcons.get(iconVariation);
|
||||||
|
} else {
|
||||||
|
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
|
||||||
|
shortGrassIcon = Config.grassUseGenerated ? grassGenIcon : grassIcons.get(iconVariation);
|
||||||
|
}
|
||||||
|
if (shortGrassIcon == null) return true;
|
||||||
|
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowTop ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[iconVariation], Config.grassHOffset, shortGrassIcon, 0, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkConnectedGrass(int x, int y, int z) {
|
||||||
|
if (isSnowTop) {
|
||||||
|
connectXP = false;
|
||||||
|
connectXN = false;
|
||||||
|
connectZP = false;
|
||||||
|
connectZN = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Block blockBelow = blockAccess.getBlock(x, y - 1, z);
|
||||||
|
if (Config.ctxGrassAggressiveEnabled && (Config.grass.matchesID(blockBelow) || Config.dirt.matchesID(blockBelow))) {
|
||||||
|
connectXP = true;
|
||||||
|
connectXN = true;
|
||||||
|
connectZP = true;
|
||||||
|
connectZN = true;
|
||||||
|
} else if (Config.ctxGrassClassicEnabled) {
|
||||||
|
connectXP = Config.grass.matchesID(blockAccess.getBlock(x + 1, y - 1, z));
|
||||||
|
connectXN = Config.grass.matchesID(blockAccess.getBlock(x - 1, y - 1, z));
|
||||||
|
connectZP = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z + 1));
|
||||||
|
connectZN = Config.grass.matchesID(blockAccess.getBlock(x, y - 1, z - 1));
|
||||||
|
} else {
|
||||||
|
connectXP = false;
|
||||||
|
connectXN = false;
|
||||||
|
connectZP = false;
|
||||||
|
connectZN = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZNeg(block, x, y, z, connectZN ? grassTopIcon : icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceZPos(block, x, y, z, connectZP ? grassTopIcon : icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXNeg(block, x, y, z, connectXN ? grassTopIcon : icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||||
|
super.renderFaceXPos(block, x, y, z, connectXP ? grassTopIcon : icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
grassIcons.registerIcons(event.map);
|
||||||
|
snowGrassIcons.registerIcons(event.map);
|
||||||
|
grassGenIcon = event.map.registerIcon("bf_shortgrass:minecraft:tallgrass");
|
||||||
|
snowGrassGenIcon = event.map.registerIcon("bf_shortgrass_snow:minecraft:tallgrass");
|
||||||
|
BetterFoliage.log.info(String.format("Found %d short grass textures", grassIcons.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d snowy grass textures", snowGrassIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet snowedLeavesIcons = new IconSet("bettergrassandleaves", "better_leaves_snowed_%d");
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.leavesEnabled) return false;
|
||||||
|
if (original > 0 && original < 42) return false;
|
||||||
|
return Config.leaves.matchesID(block) && !isBlockSurrounded(blockAccess, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
|
||||||
|
// find generated texture to render with, assume the
|
||||||
|
// "true" texture of the block is the one on the north size
|
||||||
|
TextureAtlasSprite blockLeafIcon = null;
|
||||||
|
try {
|
||||||
|
blockLeafIcon = (TextureAtlasSprite) block.getIcon(world, x, y, z, ForgeDirection.NORTH.ordinal());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockLeafIcon == null) {
|
||||||
|
BetterFoliage.log.debug(String.format("null leaf texture, x:%d, y:%d, z:%d, meta:%d, block:%s", x, y, z, blockAccess.getBlockMetadata(x, y, z), block.getClass().getName()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
|
||||||
|
if (crossLeafIcon == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
double halfSize = 0.5 * Config.leavesSize;
|
||||||
|
boolean isAirTop = blockAccess.isAirBlock(x, y + 1, z);
|
||||||
|
boolean isAirBottom = blockAccess.isAirBlock(x, y - 1, z);
|
||||||
|
|
||||||
|
boolean isSnowTop = blockAccess.getBlock(x, y + 1, z).getMaterial() == Material.snow;
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(isAirTop ? getBrightness(block, x, y + 1, z) : (isAirBottom ? getBrightness(block, x, y - 1, z) : getBrightness(block, x, y, z)));
|
||||||
|
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
|
||||||
|
|
||||||
|
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
Double3 offset1 = pRot[offsetVariation].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
|
||||||
|
Double3 offset2 = pRot[(offsetVariation + 1) & 63].scaleAxes(Config.leavesHOffset, Config.leavesVOffset, Config.leavesHOffset);
|
||||||
|
|
||||||
|
if (Config.leavesSkew) {
|
||||||
|
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||||
|
if (isSnowTop) {
|
||||||
|
// clear biome colors
|
||||||
|
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
|
||||||
|
Tessellator.instance.setColorOpaque(230, 230, 230);
|
||||||
|
renderCrossedBlockQuadsSkew(blockCenter, halfSize, offset1, offset2, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, crossLeafIcon, uvVariation, isAirTop, isAirBottom);
|
||||||
|
if (isSnowTop) {
|
||||||
|
// clear biome colors
|
||||||
|
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
|
||||||
|
Tessellator.instance.setColorOpaque(230, 230, 230);
|
||||||
|
renderCrossedBlockQuadsTranslate(blockCenter, halfSize, offset1, snowedLeavesIcons.get(uvVariation), 0, true, isAirBottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isBlockSurrounded(IBlockAccess blockAccess, int x, int y, int z) {
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x + 1, y, z))) return false;
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x - 1, y, z))) return false;
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x, y + 1, z))) return false;
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x, y - 1, z))) return false;
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z + 1))) return false;
|
||||||
|
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z - 1))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isBlockNonSurrounding(Block block) {
|
||||||
|
return block.getMaterial() == Material.air || block == Blocks.snow_layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
snowedLeavesIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d snowed leaves textures", snowedLeavesIcons.numLoaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterLilypad extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet lilypadFlowers = new IconSet("bettergrassandleaves", "better_lilypad_flower_%d");
|
||||||
|
public IconSet lilypadRoots = new IconSet("bettergrassandleaves", "better_lilypad_roots_%d");
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
return Config.lilypadEnabled && block == Blocks.waterlily;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// store world for later use
|
||||||
|
blockAccess = world;
|
||||||
|
|
||||||
|
// use original renderer for block breaking overlay
|
||||||
|
if (renderer.hasOverrideBlockTexture()) {
|
||||||
|
renderer.renderBlockLilyPad(block, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// render lilypad block
|
||||||
|
renderBlockLilyPad(block, x, y, z);
|
||||||
|
|
||||||
|
int chanceVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
int offsetVariation = getSemiRandomFromPos(x, y, z, 2);
|
||||||
|
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
|
||||||
|
Tessellator.instance.setColorOpaque(255, 255, 255);
|
||||||
|
if (lilypadRoots.hasIcons()) renderCrossedSideQuads(new Double3(x + 0.5, y + 0.015, z + 0.5), ForgeDirection.DOWN,
|
||||||
|
0.2, 0.3,
|
||||||
|
null, 0.0,
|
||||||
|
lilypadRoots.get(iconVariation), 2,
|
||||||
|
true);
|
||||||
|
if (chanceVariation < Config.lilypadChance && lilypadFlowers.hasIcons())
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 0.02, z + 0.5), ForgeDirection.UP,
|
||||||
|
0.2, 0.3,
|
||||||
|
pRot[offsetVariation], Config.lilypadHOffset,
|
||||||
|
lilypadFlowers.get(iconVariation), 0,
|
||||||
|
true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
lilypadFlowers.registerIcons(event.map);
|
||||||
|
lilypadRoots.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d lilypad flower textures", lilypadFlowers.numLoaded));
|
||||||
|
BetterFoliage.log.info(String.format("Found %d lilypad root textures", lilypadRoots.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterMycelium extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.grassEnabled) return false;
|
||||||
|
if (block != Blocks.mycelium) return false;
|
||||||
|
if (!blockAccess.isAirBlock(x, y + 1, z) && blockAccess.getBlock(x, y + 1, z) != Blocks.snow_layer) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
|
||||||
|
boolean isSnowed = blockAccess.getBlock(x, y + 1, z) == Blocks.snow_layer;
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
IIcon renderIcon = myceliumIcons.get(iconVariation);
|
||||||
|
|
||||||
|
if (isSnowed || renderIcon == null) return true;
|
||||||
|
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
double scale = Config.grassSize * 0.5;
|
||||||
|
double halfHeight = 0.5 * (Config.grassHeightMin + pRand[heightVariation] * (Config.grassHeightMax - Config.grassHeightMin));
|
||||||
|
|
||||||
|
if (isSnowed) {
|
||||||
|
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
|
||||||
|
Tessellator.instance.setColorOpaque(230, 230, 230);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render mycelium
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
|
||||||
|
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowed ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[iconVariation], Config.grassHOffset, renderIcon, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
myceliumIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterNetherrack extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet netherrackVineIcons = new IconSet("bettergrassandleaves", "better_netherrack_%d");
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.netherrackEnabled) return false;
|
||||||
|
if (block != Blocks.netherrack) return false;
|
||||||
|
if (!blockAccess.isAirBlock(x, y - 1, z)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
IIcon renderIcon = netherrackVineIcons.get(iconVariation);
|
||||||
|
|
||||||
|
if (renderIcon == null) return true;
|
||||||
|
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
double scale = Config.netherrackSize * 0.5;
|
||||||
|
double halfHeight = 0.5 * (Config.netherrackHeightMin + pRand[heightVariation] * (Config.netherrackHeightMax - Config.netherrackHeightMin));
|
||||||
|
|
||||||
|
// render netherrack vines
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y - 1, z));
|
||||||
|
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y, z + 0.5), ForgeDirection.DOWN, scale, halfHeight, pRot[iconVariation], Config.netherrackHOffset, renderIcon, 2, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
netherrackVineIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d netherrack vine textures", netherrackVineIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.render.RenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.Double3;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.gen.NoiseGeneratorSimplex;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlockBetterReed extends RenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
public IconSet reedBottomIcons = new IconSet("bf_reed_bottom", "bettergrassandleaves:better_reed_%d");
|
||||||
|
public IconSet reedTopIcons = new IconSet("bf_reed_top", "bettergrassandleaves:better_reed_%d");
|
||||||
|
public NoiseGeneratorSimplex noise;
|
||||||
|
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
if (!Config.reedEnabled) return false;
|
||||||
|
if (!(Config.dirt.matchesID(block))) return false;
|
||||||
|
if (!Config.reedBiomeList.contains(blockAccess.getBiomeGenForCoords(x, z).biomeID)) return false;
|
||||||
|
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
|
||||||
|
if (!blockAccess.isAirBlock(x, y + 2, z)) return false;
|
||||||
|
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
|
||||||
|
return terrainVariation < Config.reedPopulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
blockAccess = world;
|
||||||
|
renderWorldBlockBase(1, world, x, y, z, block, modelId, renderer);
|
||||||
|
|
||||||
|
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
|
||||||
|
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
|
||||||
|
|
||||||
|
IIcon bottomIcon = reedBottomIcons.get(iconVariation);
|
||||||
|
IIcon topIcon = reedTopIcons.get(iconVariation);
|
||||||
|
if (bottomIcon == null || topIcon == null) return true;
|
||||||
|
|
||||||
|
double quarterHeight = 0.25 * (Config.reedHeightMin + pRand[heightVariation] * (Config.reedHeightMax - Config.reedHeightMin));
|
||||||
|
Tessellator.instance.setBrightness(getBrightness(block, x, y + 2, z));
|
||||||
|
Tessellator.instance.setColorOpaque(255, 255, 255);
|
||||||
|
|
||||||
|
// render reeds
|
||||||
|
ShadersModIntegration.startGrassQuads();
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], Config.reedHOffset, bottomIcon, 0, true);
|
||||||
|
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + 2.0 * quarterHeight, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], Config.reedHOffset, topIcon, 0, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
reedBottomIcons.registerIcons(event.map);
|
||||||
|
reedTopIcons.registerIcons(event.map);
|
||||||
|
BetterFoliage.log.info(String.format("Found %d reed textures", reedBottomIcons.numLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleWorldLoad(WorldEvent.Load event) {
|
||||||
|
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package mods.betterfoliage.client.render.impl;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
|
||||||
|
import mods.betterfoliage.client.render.IRenderBlockDecorator;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.common.util.OffsetBlockAccess;
|
||||||
|
import mods.betterfoliage.common.util.RenderUtils;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.material.Material;
|
||||||
|
import net.minecraft.client.renderer.RenderBlocks;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Accepts dirt blocks with grass on top if aggressive connected grass is enabled.<br/>
|
||||||
|
* Renders the grass block in place of dirt.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class RenderBlocksBetterGrassSide extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
|
||||||
|
Material top2Material = blockAccess.getBlock(x, y + 2, z).getMaterial();
|
||||||
|
return Config.ctxGrassAggressiveEnabled &&
|
||||||
|
top2Material != Material.snow &&
|
||||||
|
top2Material != Material.craftedSnow &&
|
||||||
|
Config.dirt.matchesID(block) &&
|
||||||
|
Config.grass.matchesID(blockAccess.getBlock(x, y + 1, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
|
||||||
|
// fake grass block @(0, +1, 0) at render location
|
||||||
|
IBlockAccess originalBA = renderer.blockAccess;
|
||||||
|
renderer.blockAccess = new OffsetBlockAccess(world, x, y, z, 0, 1, 0);
|
||||||
|
|
||||||
|
Block renderBlock = renderer.blockAccess.getBlock(x, y, z);
|
||||||
|
boolean result;
|
||||||
|
ISimpleBlockRenderingHandler handler = RenderUtils.getRenderingHandler(renderBlock.getRenderType());
|
||||||
|
if (handler != null) {
|
||||||
|
result = handler.renderWorldBlock(renderer.blockAccess, x, y, z, renderBlock, renderBlock.getRenderType(), renderer);
|
||||||
|
} else {
|
||||||
|
result = renderer.renderStandardBlock(renderBlock, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore renderer to sanity
|
||||||
|
renderer.blockAccess = originalBA;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.common.util.ResourceUtils;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Base class for texture generators. Registers itself as a domain resource manager for the duration of block texture stitching.
|
||||||
|
* @author octarine-noise
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public abstract class BlockTextureGenerator implements IResourceManager {
|
||||||
|
|
||||||
|
/** Resource domain name of generated textures */
|
||||||
|
public String domainName;
|
||||||
|
|
||||||
|
/** Resource location for fallback texture (if the generation process fails) */
|
||||||
|
public ResourceLocation missingResource;
|
||||||
|
|
||||||
|
/** Texture atlas for block textures used in the current run */
|
||||||
|
public TextureMap blockTextures;
|
||||||
|
|
||||||
|
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
this.missingResource = missingResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = event.map;
|
||||||
|
|
||||||
|
Map<String, IResourceManager> domainManagers = ResourceUtils.getDomainResourceManagers();
|
||||||
|
if (domainManagers == null) {
|
||||||
|
BetterFoliage.log.warn("Failed to inject texture generator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
domainManagers.put(domainName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
blockTextures = null;
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
// don't leave a mess
|
||||||
|
Map<String, IResourceManager> domainManagers = ResourceUtils.getDomainResourceManagers();
|
||||||
|
if (domainManagers != null) domainManagers.remove(domainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getResourceDomains() {
|
||||||
|
return ImmutableSet.<String>of(domainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IResource> getAllResources(ResourceLocation resource) throws IOException {
|
||||||
|
return ImmutableList.<IResource>of(getResource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResource getMissingResource() throws IOException {
|
||||||
|
return Minecraft.getMinecraft().getResourceManager().getResource(missingResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLocation unwrapResource(ResourceLocation wrapped) {
|
||||||
|
return new ResourceLocation(wrapped.getResourcePath().substring(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int blendRGB(int rgbOrig, int rgbBlend, int weightOrig, int weightBlend) {
|
||||||
|
int r = (((rgbOrig >> 16) & 0xFF) * weightOrig + ((rgbBlend >> 16) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int g = (((rgbOrig >> 8) & 0xFF) * weightOrig + ((rgbBlend >> 8) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int b = ((rgbOrig & 0xFF) * weightOrig + (rgbBlend & 0xFF) * weightBlend) / (weightOrig + weightBlend);
|
||||||
|
int a = (rgbOrig >> 24) & 0xFF;
|
||||||
|
int result = (int) (a << 24 | r << 16 | g << 8 | b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.data.IMetadataSection;
|
||||||
|
|
||||||
|
/** {@link IResource} for a {@link BufferedImage}
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class BufferedImageResource implements IResource {
|
||||||
|
|
||||||
|
/** Raw PNG data*/
|
||||||
|
protected byte[] data = null;
|
||||||
|
|
||||||
|
public BufferedImageResource(BufferedImage image) {
|
||||||
|
// create PNG image
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(image, "PNG", baos);
|
||||||
|
data = baos.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return data == null ? null : new ByteArrayInputStream(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMetadata() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMetadataSection getMetadata(String var1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class LeafGenerator extends LeafGeneratorBase {
|
||||||
|
|
||||||
|
/** Name of the default alpha mask to use */
|
||||||
|
public static String defaultMask = "default";
|
||||||
|
|
||||||
|
public LeafGenerator() {
|
||||||
|
super("bf_leaves", "betterfoliage", "textures/blocks/%s/%s", "betterfoliage:textures/blocks/leafmask_%d_%s.png", BetterFoliageClient.missingTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException {
|
||||||
|
// load normal leaf texture
|
||||||
|
BufferedImage origImage = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(originalWithDirs).getInputStream());
|
||||||
|
if (origImage.getWidth() != origImage.getHeight()) throw new TextureGenerationException();
|
||||||
|
int size = origImage.getWidth();
|
||||||
|
|
||||||
|
// tile leaf texture 2x2
|
||||||
|
BufferedImage overlayIcon = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = overlayIcon.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, 0, null);
|
||||||
|
graphics.drawImage(origImage, 0, size, null);
|
||||||
|
graphics.drawImage(origImage, size, 0, null);
|
||||||
|
graphics.drawImage(origImage, size, size, null);
|
||||||
|
|
||||||
|
// overlay mask alpha on texture
|
||||||
|
if (!ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
|
||||||
|
// load alpha mask of appropriate size
|
||||||
|
BufferedImage maskImage = loadLeafMaskImage(defaultMask, size * 2);
|
||||||
|
int scale = size * 2 / maskImage.getWidth();
|
||||||
|
|
||||||
|
for (int x = 0; x < overlayIcon.getWidth(); x++) for (int y = 0; y < overlayIcon.getHeight(); y++) {
|
||||||
|
long origPixel = overlayIcon.getRGB(x, y) & 0xFFFFFFFFl;
|
||||||
|
long maskPixel = maskImage.getRGB(x / scale, y / scale) & 0xFF000000l | 0x00FFFFFF;
|
||||||
|
overlayIcon.setRGB(x, y, (int) (origPixel & maskPixel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlayIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
super.endTextureReload(event);
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", drawnCounter));
|
||||||
|
BetterFoliage.log.info(String.format("Generated %d leaf textures", generatedCounter));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
|
||||||
|
import mods.betterfoliage.common.util.ResourceUtils;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Texture generator base class for textures based on leaf blocks.
|
||||||
|
* Supports loading from resource packs instead of generating if available.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public abstract class LeafGeneratorBase extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public static class TextureGenerationException extends Exception {
|
||||||
|
private static final long serialVersionUID = 7339757761980002651L;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Format string of pre-drawn texture location */
|
||||||
|
public String handDrawnLocationFormat;
|
||||||
|
|
||||||
|
/** Format string of alpha mask location */
|
||||||
|
public String maskImageLocationFormat;
|
||||||
|
|
||||||
|
/** Resource domain name of pre-drawn textures */
|
||||||
|
public String nonGeneratedDomain;
|
||||||
|
|
||||||
|
/** Number of textures generated in the current run */
|
||||||
|
public int generatedCounter = 0;
|
||||||
|
|
||||||
|
/** Number of pre-drawn textures found in the current run */
|
||||||
|
public int drawnCounter = 0;
|
||||||
|
|
||||||
|
public LeafGeneratorBase(String domain, String nonGeneratedDomain, String handDrawnLocationFormat, String maskImageLocationFormat, ResourceLocation missingResource) {
|
||||||
|
super(domain, missingResource);
|
||||||
|
this.nonGeneratedDomain = nonGeneratedDomain;
|
||||||
|
this.handDrawnLocationFormat = handDrawnLocationFormat;
|
||||||
|
this.maskImageLocationFormat = maskImageLocationFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// check for provided texture
|
||||||
|
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format(handDrawnLocationFormat, originalNoDirs.getResourceDomain(), originalNoDirs.getResourcePath()));
|
||||||
|
if (ResourceUtils.resourceExists(handDrawnLocation)) {
|
||||||
|
drawnCounter++;
|
||||||
|
return resourceManager.getResource(handDrawnLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate our own
|
||||||
|
if (!ResourceUtils.resourceExists(originalWithDirs)) return getMissingResource();
|
||||||
|
|
||||||
|
BufferedImage result;
|
||||||
|
try {
|
||||||
|
result = generateLeaf(originalWithDirs);
|
||||||
|
} catch (TextureGenerationException e) {
|
||||||
|
return getMissingResource();
|
||||||
|
}
|
||||||
|
generatedCounter++;
|
||||||
|
return new BufferedImageResource(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException;
|
||||||
|
|
||||||
|
/** Loads the alpha mask of the given type and size. If a mask of the exact size can not be found,
|
||||||
|
* will try to load progressively smaller masks down to 16x16
|
||||||
|
* @param type mask type
|
||||||
|
* @param size texture size
|
||||||
|
* @return alpha mask
|
||||||
|
*/
|
||||||
|
protected BufferedImage loadLeafMaskImage(String type, int size) {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
IResource maskResource = null;
|
||||||
|
|
||||||
|
while (maskResource == null && size >= 1) {
|
||||||
|
try {
|
||||||
|
maskResource = resourceManager.getResource(new ResourceLocation(String.format(maskImageLocationFormat, size, type)));
|
||||||
|
} catch (Exception e) {}
|
||||||
|
size /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return maskResource == null ? null : ImageIO.read(maskResource.getInputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(Pre event) {
|
||||||
|
super.handleTextureReload(event);
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
generatedCounter = 0;
|
||||||
|
drawnCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleRegisterTexture(LeafTextureFoundEvent event) {
|
||||||
|
event.blockTextures.registerIcon(new ResourceLocation(domainName, event.icon.getIconName()).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.TextureMatcher;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Holds the textures for the falling leaf particles, and stores average texture color values for leaf textures
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class LeafParticleTextures {
|
||||||
|
|
||||||
|
/** Icons for leaf particles */
|
||||||
|
public Map<String, IconSet> iconSets = Maps.newHashMap();
|
||||||
|
|
||||||
|
/** Leaf type mappings */
|
||||||
|
public TextureMatcher leafTypes = new TextureMatcher();
|
||||||
|
|
||||||
|
/** Map of average color values */
|
||||||
|
public Map<IIcon, Integer> iconColors = Maps.newHashMap();
|
||||||
|
|
||||||
|
/** Default color value */
|
||||||
|
public int defaultColor = 0x208040;
|
||||||
|
|
||||||
|
public int loadedSets;
|
||||||
|
|
||||||
|
public LeafParticleTextures(int defaultColor) {
|
||||||
|
this.defaultColor = defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IconSet getIconSet(IIcon icon) {
|
||||||
|
String leafType = leafTypes.get(icon);
|
||||||
|
if (leafType == null) leafType = "default";
|
||||||
|
IconSet result = iconSets.get(leafType);
|
||||||
|
return result.hasIcons() ? result : iconSets.get("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor(IIcon icon) {
|
||||||
|
Integer result = iconColors.get(icon);
|
||||||
|
return result == null ? defaultColor : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate average color value (in HSB color space) for a texture.
|
||||||
|
* @param icon texture
|
||||||
|
*/
|
||||||
|
protected Integer calculateTextureColor(TextureAtlasSprite icon) {
|
||||||
|
ResourceLocation locationNoDirs = new ResourceLocation(icon.getIconName());
|
||||||
|
ResourceLocation locationWithDirs = new ResourceLocation(locationNoDirs.getResourceDomain(), String.format("textures/blocks/%s.png", locationNoDirs.getResourcePath()));
|
||||||
|
try {
|
||||||
|
BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(locationWithDirs).getInputStream());
|
||||||
|
|
||||||
|
int numOpaque = 0;
|
||||||
|
float sumHueX = 0.0f;
|
||||||
|
float sumHueY = 0.0f;
|
||||||
|
float sumSaturation = 0.0f;
|
||||||
|
float sumBrightness = 0.0f;
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
int pixel = image.getRGB(x, y);
|
||||||
|
int alpha = (pixel >> 24) & 0xFF;
|
||||||
|
float[] hsbVals = Color.RGBtoHSB((pixel >> 16) & 0xFF, (pixel >> 8) & 0xFF, pixel & 0xFF, null);
|
||||||
|
if (alpha == 255) {
|
||||||
|
numOpaque++;
|
||||||
|
sumHueX += Math.cos((hsbVals[0] - 0.5) * 2.0 * Math.PI);
|
||||||
|
sumHueY += Math.sin((hsbVals[0] - 0.5) * 2.0 * Math.PI);
|
||||||
|
sumSaturation += hsbVals[1];
|
||||||
|
sumBrightness += hsbVals[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// average hue as usual for circular values - transform average unit vector back to polar angle
|
||||||
|
float avgHue = (float) (Math.atan2(sumHueY, sumHueX) / (2.0 * Math.PI) + 0.5);
|
||||||
|
return Color.HSBtoRGB(avgHue, sumSaturation / numOpaque, sumBrightness / numOpaque);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
loadedSets = 1;
|
||||||
|
iconSets.clear();
|
||||||
|
iconColors.clear();
|
||||||
|
|
||||||
|
leafTypes.loadMappings(new ResourceLocation("betterfoliage", "leafTextureMappings.cfg"));
|
||||||
|
IconSet defaultIcons = new IconSet("betterfoliage", "falling_leaf_default_%d");
|
||||||
|
iconSets.put("default", defaultIcons);
|
||||||
|
defaultIcons.registerIcons(event.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
if (event.map.getTextureType() == 0) BetterFoliage.log.info(String.format("Loaded %d leaf particle sets", loadedSets));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleRegisterTexture(LeafTextureFoundEvent event) {
|
||||||
|
Integer textureColor = calculateTextureColor(event.icon);
|
||||||
|
if (textureColor != null) iconColors.put(event.icon, textureColor);
|
||||||
|
|
||||||
|
String leafType = leafTypes.put(event.icon);
|
||||||
|
if (leafType != null && !iconSets.keySet().contains(leafType)) {
|
||||||
|
IconSet newSet = new IconSet("betterfoliage", String.format("falling_leaf_%s_%%d", leafType));
|
||||||
|
newSet.registerIcons(event.blockTextures);
|
||||||
|
iconSets.put(leafType, newSet);
|
||||||
|
loadedSets++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BetterFoliageClient;
|
||||||
|
import mods.betterfoliage.common.config.Config;
|
||||||
|
import mods.betterfoliage.loader.DeobfHelper;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.Event;
|
||||||
|
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||||
|
import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Enumerates all leaf textures at stitch time and emits an event for each.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class LeafTextureEnumerator implements IIconRegister {
|
||||||
|
|
||||||
|
/**{@link Event} that is emitted for each texture belonging to a leaf block.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public static class LeafTextureFoundEvent extends Event {
|
||||||
|
|
||||||
|
public TextureMap blockTextures;
|
||||||
|
public TextureAtlasSprite icon;
|
||||||
|
|
||||||
|
private LeafTextureFoundEvent(TextureMap blockTextures, TextureAtlasSprite icon) {
|
||||||
|
super();
|
||||||
|
this.blockTextures = blockTextures;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Texture atlas for block textures used in the current run */
|
||||||
|
public TextureMap blockTextures;
|
||||||
|
|
||||||
|
/** Leaf blocks register their textures here.
|
||||||
|
* @return the originally registered {@link IIcon} already in the atlas
|
||||||
|
*/
|
||||||
|
public IIcon registerIcon(String resourceLocation) {
|
||||||
|
TextureAtlasSprite original = blockTextures.getTextureExtry(resourceLocation);
|
||||||
|
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, original));
|
||||||
|
BetterFoliage.log.debug(String.format("Found leaf texture: %s", resourceLocation));
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Iterates through all leaf blocks in the registry and makes them register
|
||||||
|
* their textures to "sniff out" all leaf textures.
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@SubscribeEvent(priority=EventPriority.LOWEST)
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = event.map;
|
||||||
|
|
||||||
|
BetterFoliage.log.info("Reloading leaf textures");
|
||||||
|
|
||||||
|
// register simple block textures
|
||||||
|
Iterator<Block> iter = Block.blockRegistry.iterator();
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
Block block = iter.next();
|
||||||
|
if (Config.leaves.matchesClass(block)) {
|
||||||
|
BetterFoliage.log.debug(String.format("Inspecting leaf block: %s", block.getClass().getName()));
|
||||||
|
block.registerBlockIcons(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enumerate all registered textures, find leaf textures among them
|
||||||
|
try {
|
||||||
|
Map<String, TextureAtlasSprite> mapAtlas = ReflectionHelper.<Map<String, TextureAtlasSprite>, TextureMap> getPrivateValue(
|
||||||
|
TextureMap.class, blockTextures, DeobfHelper.transformElementSearge("mapRegisteredSprites"), "mapRegisteredSprites"
|
||||||
|
);
|
||||||
|
|
||||||
|
Set<TextureAtlasSprite> foundLeafTextures = Sets.newHashSet();
|
||||||
|
for (TextureAtlasSprite icon : mapAtlas.values())
|
||||||
|
if (BetterFoliageClient.isLeafTexture(icon)) foundLeafTextures.add(icon);
|
||||||
|
for (TextureAtlasSprite icon : foundLeafTextures) {
|
||||||
|
BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", icon.getIconName()));
|
||||||
|
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, icon));
|
||||||
|
}
|
||||||
|
} catch (UnableToAccessFieldException e) {
|
||||||
|
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
blockTextures = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ReedGenerator extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
public boolean isBottom;
|
||||||
|
|
||||||
|
public ReedGenerator(String domainName, ResourceLocation missingResource, boolean isBottom) {
|
||||||
|
super(domainName, missingResource);
|
||||||
|
this.isBottom = isBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// load full texture
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
|
||||||
|
|
||||||
|
// draw half texture
|
||||||
|
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = result.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, isBottom ? -origImage.getHeight() / 2 : 0, null);
|
||||||
|
|
||||||
|
return new BufferedImageResource(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.ShadersModIntegration;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ShortGrassGenerator extends BlockTextureGenerator {
|
||||||
|
|
||||||
|
protected boolean isSnowed = false;
|
||||||
|
|
||||||
|
protected int snowOriginalWeight = 2;
|
||||||
|
|
||||||
|
protected int snowWhiteWeight = 3;
|
||||||
|
|
||||||
|
public ShortGrassGenerator(String domainName, ResourceLocation missingResource, boolean isSnowed) {
|
||||||
|
super(domainName, missingResource);
|
||||||
|
this.isSnowed = isSnowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
|
||||||
|
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
|
||||||
|
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
|
||||||
|
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
|
||||||
|
|
||||||
|
// load full texture
|
||||||
|
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
|
||||||
|
|
||||||
|
// draw bottom half of texture
|
||||||
|
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
|
Graphics2D graphics = result.createGraphics();
|
||||||
|
graphics.drawImage(origImage, 0, 3 * origImage.getHeight() / 8, null);
|
||||||
|
|
||||||
|
// blend with white if snowed
|
||||||
|
if (isSnowed && !ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
|
||||||
|
for (int x = 0; x < result.getWidth(); x++) for (int y = 0; y < result.getHeight(); y++) {
|
||||||
|
result.setRGB(x, y, blendRGB(result.getRGB(x, y), 0xFFFFFF, 2, 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferedImageResource(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package mods.betterfoliage.client.resource;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.render.IconSet;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
|
/** Holds the textures for the rising soul particles
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class SoulParticleTextures {
|
||||||
|
|
||||||
|
public IIcon soulTrackIcon;
|
||||||
|
|
||||||
|
public IconSet soulHeadIcons = new IconSet("bettergrassandleaves", "rising_soul_%d");
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleTextureReload(TextureStitchEvent.Pre event) {
|
||||||
|
if (event.map.getTextureType() != 0) return;
|
||||||
|
|
||||||
|
soulTrackIcon = event.map.registerIcon("bettergrassandleaves:soul_track");
|
||||||
|
soulHeadIcons.registerIcons(event.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void endTextureReload(TextureStitchEvent.Post event) {
|
||||||
|
if (event.map.getTextureType() == 0) BetterFoliage.log.info(String.format("Found %d soul particle textures", soulHeadIcons.numLoaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
322
src/main/java/mods/betterfoliage/common/config/Config.java
Normal file
322
src/main/java/mods/betterfoliage/common/config/Config.java
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
package mods.betterfoliage.common.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.client.BlockMatcher;
|
||||||
|
import mods.betterfoliage.client.gui.AlternateTextBooleanEntry;
|
||||||
|
import mods.betterfoliage.client.gui.BiomeListConfigEntry;
|
||||||
|
import mods.betterfoliage.client.gui.NonVerboseArrayEntry;
|
||||||
|
import mods.betterfoliage.common.util.BiomeUtils;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.world.biome.BiomeGenBase;
|
||||||
|
import net.minecraftforge.common.config.ConfigCategory;
|
||||||
|
import net.minecraftforge.common.config.ConfigElement;
|
||||||
|
import net.minecraftforge.common.config.Configuration;
|
||||||
|
import net.minecraftforge.common.config.Property;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
public enum Category {
|
||||||
|
blockTypes, extraLeaves, shortGrass, cactus, lilypad, reed, algae, coral, netherrack, fallingLeaves, risingSoul, connectedGrass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@link Configuration} object bound to the config file */
|
||||||
|
public static Configuration rawConfig;
|
||||||
|
|
||||||
|
// block matchers
|
||||||
|
public static BlockMatcher leaves = new BlockMatcher();
|
||||||
|
public static BlockMatcher crops = new BlockMatcher();
|
||||||
|
public static BlockMatcher dirt = new BlockMatcher();
|
||||||
|
public static BlockMatcher grass = new BlockMatcher();
|
||||||
|
|
||||||
|
// extracted config values
|
||||||
|
public static boolean leavesEnabled;
|
||||||
|
public static boolean leavesSkew;
|
||||||
|
public static double leavesHOffset;
|
||||||
|
public static double leavesVOffset;
|
||||||
|
public static double leavesSize;
|
||||||
|
|
||||||
|
public static boolean grassEnabled;
|
||||||
|
public static boolean grassUseGenerated;
|
||||||
|
public static double grassHOffset;
|
||||||
|
public static double grassHeightMin;
|
||||||
|
public static double grassHeightMax;
|
||||||
|
public static double grassSize;
|
||||||
|
|
||||||
|
public static boolean cactusEnabled;
|
||||||
|
|
||||||
|
public static boolean lilypadEnabled;
|
||||||
|
public static double lilypadHOffset;
|
||||||
|
public static int lilypadChance;
|
||||||
|
|
||||||
|
public static boolean reedEnabled;
|
||||||
|
public static double reedHOffset;
|
||||||
|
public static double reedHeightMin;
|
||||||
|
public static double reedHeightMax;
|
||||||
|
public static int reedPopulation;
|
||||||
|
|
||||||
|
public static boolean algaeEnabled;
|
||||||
|
public static double algaeHOffset;
|
||||||
|
public static double algaeSize;
|
||||||
|
public static double algaeHeightMin;
|
||||||
|
public static double algaeHeightMax;
|
||||||
|
public static int algaePopulation;
|
||||||
|
|
||||||
|
public static boolean coralEnabled;
|
||||||
|
public static int coralPopulation;
|
||||||
|
public static int coralChance;
|
||||||
|
public static double coralVOffset;
|
||||||
|
public static double coralHOffset;
|
||||||
|
public static double coralCrustSize;
|
||||||
|
public static double coralSize;
|
||||||
|
|
||||||
|
public static boolean leafFXEnabled;
|
||||||
|
public static double leafFXSpeed;
|
||||||
|
public static double leafFXWindStrength;
|
||||||
|
public static double leafFXStormStrength;
|
||||||
|
public static double leafFXSize;
|
||||||
|
public static double leafFXChance;
|
||||||
|
public static double leafFXPerturb;
|
||||||
|
public static double leafFXLifetime;
|
||||||
|
|
||||||
|
public static boolean soulFXEnabled;
|
||||||
|
public static double soulFXChance;
|
||||||
|
public static double soulFXSpeed;
|
||||||
|
public static double soulFXPerturb;
|
||||||
|
public static double soulFXHeadSize;
|
||||||
|
public static double soulFXTrailSize;
|
||||||
|
public static double soulFXOpacity;
|
||||||
|
public static double soulFXSizeDecay;
|
||||||
|
public static double soulFXOpacityDecay;
|
||||||
|
public static double soulFXLifetime;
|
||||||
|
public static int soulFXTrailLength;
|
||||||
|
public static int soulFXTrailDensity;
|
||||||
|
|
||||||
|
public static boolean netherrackEnabled;
|
||||||
|
public static double netherrackHOffset;
|
||||||
|
public static double netherrackHeightMin;
|
||||||
|
public static double netherrackHeightMax;
|
||||||
|
public static double netherrackSize;
|
||||||
|
|
||||||
|
public static boolean ctxGrassClassicEnabled;
|
||||||
|
public static boolean ctxGrassAggressiveEnabled;
|
||||||
|
|
||||||
|
public static List<Integer> reedBiomeList = Lists.newArrayList();
|
||||||
|
public static List<Integer> algaeBiomeList = Lists.newArrayList();
|
||||||
|
public static List<Integer> coralBiomeList = Lists.newArrayList();
|
||||||
|
|
||||||
|
/** Read the config file
|
||||||
|
* @param configFile
|
||||||
|
*/
|
||||||
|
public static void readConfig(File configFile) {
|
||||||
|
rawConfig = new Configuration(configFile, true);
|
||||||
|
updateValues();
|
||||||
|
if (rawConfig.hasChanged()) rawConfig.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extract the config properties to static value fields for quick access */
|
||||||
|
public static void updateValues() {
|
||||||
|
leavesEnabled = getBoolean(Category.extraLeaves, "enabled", true, "betterfoliage.enabled");
|
||||||
|
leavesSkew = getBoolean(Category.extraLeaves, "skewMode", false, "betterfoliage.leavesMode");
|
||||||
|
leavesHOffset = getDouble(Category.extraLeaves, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
|
||||||
|
leavesVOffset = getDouble(Category.extraLeaves, "vOffset", 0.1, 0.0, 0.4, "betterfoliage.vOffset");
|
||||||
|
leavesSize = getDouble(Category.extraLeaves, "size", 1.4, 0.75, 1.8, "betterfoliage.size");
|
||||||
|
|
||||||
|
grassEnabled = getBoolean(Category.shortGrass, "enabled", true, "betterfoliage.enabled");
|
||||||
|
grassHOffset = getDouble(Category.shortGrass, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
|
||||||
|
grassHeightMin = getDouble(Category.shortGrass, "heightMin", 0.6, 0.1, 1.5, "betterfoliage.minHeight");
|
||||||
|
grassHeightMax = getDouble(Category.shortGrass, "heightMax", 0.8, 0.1, 1.5, "betterfoliage.maxHeight");
|
||||||
|
grassSize = getDouble(Category.shortGrass, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
|
||||||
|
grassUseGenerated = getBoolean(Category.shortGrass, "useGenerated", false, "betterfoliage.shortGrass.useGenerated");
|
||||||
|
grassHeightMin = clampDoubleToMax(Category.shortGrass, "heightMin", "heightMax");
|
||||||
|
|
||||||
|
cactusEnabled = getBoolean(Category.cactus, "enabled", true, "betterfoliage.enabled");
|
||||||
|
|
||||||
|
lilypadEnabled = getBoolean(Category.lilypad, "enabled", true, "betterfoliage.enabled");
|
||||||
|
lilypadHOffset = getDouble(Category.lilypad, "hOffset", 0.1, 0.0, 0.25, "betterfoliage.hOffset");
|
||||||
|
lilypadChance = getInt(Category.lilypad, "flowerChance", 16, 0, 64, "betterfoliage.lilypad.flowerChance");
|
||||||
|
|
||||||
|
reedEnabled = getBoolean(Category.reed, "enabled", true, "betterfoliage.enabled");
|
||||||
|
reedHOffset = getDouble(Category.reed, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
|
||||||
|
reedHeightMin = getDouble(Category.reed, "heightMin", 2.0, 1.5, 3.5, "betterfoliage.minHeight");
|
||||||
|
reedHeightMax = getDouble(Category.reed, "heightMax", 2.5, 1.5, 3.5, "betterfoliage.maxHeight");
|
||||||
|
reedPopulation = getInt(Category.reed, "population", 32, 0, 64, "betterfoliage.population");
|
||||||
|
reedHeightMin = clampDoubleToMax(Category.reed, "heightMin", "heightMax");
|
||||||
|
reedBiomeList = getIntList(Category.reed, "reedBiomeList", reedBiomeList, "betterfoliage.reed.biomeList");
|
||||||
|
|
||||||
|
algaeEnabled = getBoolean(Category.algae, "enabled", true, "betterfoliage.enabled");
|
||||||
|
algaeHOffset = getDouble(Category.algae, "hOffset", 0.1, 0.0, 0.25, "betterfoliage.hOffset");
|
||||||
|
algaeSize = getDouble(Category.algae, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
|
||||||
|
algaeHeightMin = getDouble(Category.algae, "heightMin", 0.5, 0.1, 1.5, "betterfoliage.minHeight");
|
||||||
|
algaeHeightMax = getDouble(Category.algae, "heightMax", 1.0, 0.1, 1.5, "betterfoliage.maxHeight");
|
||||||
|
algaePopulation = getInt(Category.algae, "population", 48, 0, 64, "betterfoliage.population");
|
||||||
|
algaeHeightMin = clampDoubleToMax(Category.algae, "heightMin", "heightMax");
|
||||||
|
algaeBiomeList = getIntList(Category.algae, "algaeBiomeList", algaeBiomeList, "betterfoliage.algae.biomeList");
|
||||||
|
|
||||||
|
coralEnabled = getBoolean(Category.coral, "enabled", true, "betterfoliage.enabled");
|
||||||
|
coralHOffset = getDouble(Category.coral, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
|
||||||
|
coralVOffset = getDouble(Category.coral, "vOffset", 0.1, 0.0, 0.4, "betterfoliage.vOffset");
|
||||||
|
coralSize = getDouble(Category.coral, "size", 0.7, 0.5, 1.5, "betterfoliage.coral.size");
|
||||||
|
coralCrustSize = getDouble(Category.coral, "crustSize", 1.4, 0.5, 1.5, "betterfoliage.coral.crustSize");
|
||||||
|
coralChance = getInt(Category.coral, "chance", 32, 0, 64, "betterfoliage.coral.chance");
|
||||||
|
coralPopulation = getInt(Category.coral, "population", 48, 0, 64, "betterfoliage.population");
|
||||||
|
coralBiomeList = getIntList(Category.coral, "coralBiomeList", coralBiomeList, "betterfoliage.coral.biomeList");
|
||||||
|
|
||||||
|
leafFXEnabled = getBoolean(Category.fallingLeaves, "enabled", true, "betterfoliage.enabled");
|
||||||
|
leafFXSpeed = getDouble(Category.fallingLeaves, "speed", 0.05, 0.01, 0.15, "betterfoliage.fallingLeaves.speed");
|
||||||
|
leafFXWindStrength = getDouble(Category.fallingLeaves, "windStrength", 0.5, 0.1, 2.0, "betterfoliage.fallingLeaves.windStrength");
|
||||||
|
leafFXStormStrength = getDouble(Category.fallingLeaves, "stormStrength", 0.8, 0.1, 2.0, "betterfoliage.fallingLeaves.stormStrength");
|
||||||
|
leafFXSize = getDouble(Category.fallingLeaves, "size", 0.75, 0.25, 1.5, "betterfoliage.fallingLeaves.size");
|
||||||
|
leafFXChance = getDouble(Category.fallingLeaves, "chance", 0.05, 0.001, 1.0, "betterfoliage.fallingLeaves.chance");
|
||||||
|
leafFXPerturb = getDouble(Category.fallingLeaves, "perturb", 0.25, 0.01, 1.0, "betterfoliage.fallingLeaves.perturb");
|
||||||
|
leafFXLifetime = getDouble(Category.fallingLeaves, "lifetime", 5.0, 1.0, 15.0, "betterfoliage.fallingLeaves.lifetime");
|
||||||
|
|
||||||
|
soulFXEnabled = getBoolean(Category.risingSoul, "enabled", true, "betterfoliage.enabled");
|
||||||
|
soulFXChance = getDouble(Category.risingSoul, "chance", 0.02, 0.001, 1.0, "betterfoliage.risingSoul.chance");
|
||||||
|
soulFXSpeed = getDouble(Category.risingSoul, "speed", 0.1, 0.025, 0.25, "betterfoliage.risingSoul.speed");
|
||||||
|
soulFXPerturb = getDouble(Category.risingSoul, "perturb", 0.05, 0.01, 0.25, "betterfoliage.risingSoul.perturb");
|
||||||
|
soulFXHeadSize = getDouble(Category.risingSoul, "headSize", 1.0, 0.25, 1.5, "betterfoliage.risingSoul.headSize");
|
||||||
|
soulFXTrailSize = getDouble(Category.risingSoul, "trailSize", 0.75, 0.25, 1.5, "betterfoliage.risingSoul.trailSize");
|
||||||
|
soulFXOpacity = getDouble(Category.risingSoul, "opacity", 0.5, 0.05, 1.0, "betterfoliage.risingSoul.opacity");
|
||||||
|
soulFXSizeDecay = getDouble(Category.risingSoul, "sizeDecay", 0.97, 0.5, 1.0, "betterfoliage.risingSoul.sizeDecay");
|
||||||
|
soulFXOpacityDecay = getDouble(Category.risingSoul, "opacityDecay", 0.97, 0.5, 1.0, "betterfoliage.risingSoul.opacityDecay");
|
||||||
|
soulFXLifetime = getDouble(Category.risingSoul, "lifetime", 4.0, 1.0, 15.0, "betterfoliage.risingSoul.lifetime");
|
||||||
|
soulFXTrailLength = getInt(Category.risingSoul, "trailLength", 48, 2, 128, "betterfoliage.risingSoul.trailLength");
|
||||||
|
soulFXTrailDensity = getInt(Category.risingSoul, "trailDensity", 3, 1, 16, "betterfoliage.risingSoul.trailDensity");
|
||||||
|
|
||||||
|
netherrackEnabled = getBoolean(Category.netherrack, "enabled", true, "betterfoliage.enabled");
|
||||||
|
netherrackHOffset = getDouble(Category.netherrack, "hOffset", 0.2, 0.0, 0.4, "betterfoliage.hOffset");
|
||||||
|
netherrackHeightMin = getDouble(Category.netherrack, "heightMin", 0.6, 0.1, 1.5, "betterfoliage.minHeight");
|
||||||
|
netherrackHeightMax = getDouble(Category.netherrack, "heightMax", 0.8, 0.1, 1.5, "betterfoliage.maxHeight");
|
||||||
|
netherrackSize = getDouble(Category.netherrack, "size", 1.0, 0.5, 1.5, "betterfoliage.size");
|
||||||
|
netherrackHeightMin = clampDoubleToMax(Category.netherrack, "heightMin", "heightMax");
|
||||||
|
|
||||||
|
ctxGrassClassicEnabled = getBoolean(Category.connectedGrass, "classic", true, "betterfoliage.connectedGrass.classic");
|
||||||
|
ctxGrassAggressiveEnabled= getBoolean(Category.connectedGrass, "aggressive", true, "betterfoliage.connectedGrass.aggressive");
|
||||||
|
|
||||||
|
updateBlockMatcher(dirt, Category.blockTypes, "dirtWhitelist", "betterfoliage.blockTypes.dirtWhitelist", "dirtBlacklist", "betterfoliage.blockTypes.dirtBlacklist", new ResourceLocation("betterfoliage:classesDirtDefault.cfg"));
|
||||||
|
updateBlockMatcher(grass, Category.blockTypes, "grassWhitelist", "betterfoliage.blockTypes.grassWhitelist", "grassBlacklist", "betterfoliage.blockTypes.grassBlacklist", new ResourceLocation("betterfoliage:classesGrassDefault.cfg"));
|
||||||
|
updateBlockMatcher(leaves, Category.blockTypes, "leavesWhitelist", "betterfoliage.blockTypes.leavesWhitelist", "leavesBlacklist", "betterfoliage.blockTypes.leavesBlacklist", new ResourceLocation("betterfoliage:classesLeavesDefault.cfg"));
|
||||||
|
updateBlockMatcher(crops, Category.blockTypes, "cropWhitelist", "betterfoliage.blockTypes.cropWhitelist", "cropBlacklist", "betterfoliage.blockTypes.cropBlacklist", new ResourceLocation("betterfoliage:classesCropDefault.cfg"));
|
||||||
|
|
||||||
|
rawConfig.getCategory(Category.extraLeaves.toString()).get("skewMode").setConfigEntryClass(AlternateTextBooleanEntry.class);
|
||||||
|
rawConfig.getCategory(Category.reed.toString()).get("reedBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
|
||||||
|
rawConfig.getCategory(Category.algae.toString()).get("algaeBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
|
||||||
|
rawConfig.getCategory(Category.coral.toString()).get("coralBiomeList").setConfigEntryClass(BiomeListConfigEntry.class);
|
||||||
|
|
||||||
|
for (Category category : Category.values()) rawConfig.setCategoryLanguageKey(category.toString(), String.format("betterfoliage.%s", category.toString()));
|
||||||
|
|
||||||
|
setOrder(Category.extraLeaves, "enabled", "skewMode", "hOffset", "vOffset", "size");
|
||||||
|
setOrder(Category.shortGrass, "enabled", "useGenerated", "hOffset", "heightMin", "heightMax", "size");
|
||||||
|
setOrder(Category.lilypad, "enabled", "hOffset", "flowerChance");
|
||||||
|
setOrder(Category.reed, "enabled", "hOffset", "heightMin", "heightMax", "population", "biomeList");
|
||||||
|
setOrder(Category.algae, "enabled", "hOffset", "heightMin", "heightMax", "size", "population", "biomeList");
|
||||||
|
setOrder(Category.coral, "enabled", "hOffset", "vOffset", "size", "crustSize", "population", "chance", "biomeList");
|
||||||
|
setOrder(Category.netherrack, "enabled", "hOffset", "heightMin", "heightMax", "size");
|
||||||
|
setOrder(Category.fallingLeaves, "enabled", "chance", "size", "lifetime", "speed", "windStrength", "stormStrength", "perturb");
|
||||||
|
setOrder(Category.risingSoul, "enabled", "chance", "speed", "perturb", "headSize", "trailSize", "sizeDecay", "opacity", "opacityDecay", "lifetime", "trailLength", "trailDensity");
|
||||||
|
setOrder(Category.connectedGrass, "classic", "aggressive");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void getDefaultBiomes() {
|
||||||
|
List<BiomeGenBase> biomes = BiomeUtils.getAllBiomes();
|
||||||
|
reedBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeTempRainFilter(0.4f, null, 0.4f, null));
|
||||||
|
algaeBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeClassNameFilter("river", "ocean"));
|
||||||
|
coralBiomeList = BiomeUtils.getFilteredBiomeIds(biomes, BiomeUtils.biomeClassNameFilter("river", "ocean", "beach"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public static List<IConfigElement> getConfigRootElements() {
|
||||||
|
List<IConfigElement> result = Lists.newLinkedList();
|
||||||
|
for (Category category : Category.values()) {
|
||||||
|
ConfigElement<?> element = new ConfigElement(rawConfig.getCategory(category.toString()));
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static double getDouble(Category category, String key, double defaultValue, double min, double max, String langKey) {
|
||||||
|
Property prop = rawConfig.get(category.toString(), key, defaultValue);
|
||||||
|
prop.setMinValue(min);
|
||||||
|
prop.setMaxValue(max);
|
||||||
|
prop.setLanguageKey(langKey);
|
||||||
|
return prop.getDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static double clampDoubleToMax(Category category, String keySmaller, String keyLarger) {
|
||||||
|
ConfigCategory cfgCat = rawConfig.getCategory(category.toString());
|
||||||
|
Property smaller = cfgCat.get(keySmaller);
|
||||||
|
Property larger = cfgCat.get(keyLarger);
|
||||||
|
if (smaller.getDouble() > larger.getDouble()) smaller.set(larger.getDouble());
|
||||||
|
return smaller.getDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getInt(Category category, String key, int defaultValue, int min, int max, String langKey) {
|
||||||
|
Property prop = rawConfig.get(category.toString(), key, defaultValue);
|
||||||
|
prop.setMinValue(min);
|
||||||
|
prop.setMaxValue(max);
|
||||||
|
prop.setLanguageKey(langKey);
|
||||||
|
return prop.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static List<Integer> getIntList(Category category, String key, List<Integer> defaultList, String langKey) {
|
||||||
|
int[] defaults = new int[]{};
|
||||||
|
if (defaultList != null) {
|
||||||
|
defaults = new int[defaultList.size()];
|
||||||
|
int idx = 0;
|
||||||
|
for (Integer value : defaultList) defaults[idx++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Property prop = rawConfig.get(category.toString(), key, defaults);
|
||||||
|
prop.setLanguageKey(langKey);
|
||||||
|
|
||||||
|
int[] values = prop.getIntList();
|
||||||
|
List<Integer> result = Lists.newArrayListWithCapacity(values.length);
|
||||||
|
for (int value : values) result.add(value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean getBoolean(Category category, String key, boolean defaultValue, String langKey) {
|
||||||
|
Property prop = rawConfig.get(category.toString(), key, defaultValue);
|
||||||
|
prop.setLanguageKey(langKey);
|
||||||
|
return prop.getBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void updateBlockMatcher(BlockMatcher bm, Category category, String whitelistKey, String whitelistLangKey, String blacklistKey, String blacklistLangKey, ResourceLocation defaults) {
|
||||||
|
List<String> defaultWhitelist = Lists.newLinkedList();
|
||||||
|
List<String> defaultBlacklist = Lists.newLinkedList();
|
||||||
|
BlockMatcher.loadDefaultLists(defaults, defaultBlacklist, defaultWhitelist);
|
||||||
|
|
||||||
|
Property whitelist = rawConfig.get(category.toString(), whitelistKey, defaultWhitelist.toArray(new String[]{}));
|
||||||
|
Property blacklist = rawConfig.get(category.toString(), blacklistKey, defaultBlacklist.toArray(new String[]{}));
|
||||||
|
|
||||||
|
whitelist.setLanguageKey(whitelistLangKey);
|
||||||
|
blacklist.setLanguageKey(blacklistLangKey);
|
||||||
|
whitelist.setConfigEntryClass(NonVerboseArrayEntry.class);
|
||||||
|
blacklist.setConfigEntryClass(NonVerboseArrayEntry.class);
|
||||||
|
|
||||||
|
bm.updateClassLists(whitelist.getStringList(), blacklist.getStringList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void setOrder(Category category, String... properties) {
|
||||||
|
rawConfig.setCategoryPropertyOrder(category.toString(), Lists.newArrayList(properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) {
|
||||||
|
if (event.modID.equals(BetterFoliage.MOD_ID)) {
|
||||||
|
updateValues();
|
||||||
|
if (rawConfig.hasChanged()) rawConfig.save();
|
||||||
|
Minecraft.getMinecraft().renderGlobal.loadRenderers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/main/java/mods/betterfoliage/common/util/BiomeUtils.java
Normal file
87
src/main/java/mods/betterfoliage/common/util/BiomeUtils.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.world.biome.BiomeGenBase;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
|
||||||
|
public class BiomeUtils {
|
||||||
|
|
||||||
|
/** Hide constructor */
|
||||||
|
private BiomeUtils() {}
|
||||||
|
|
||||||
|
public static List<BiomeGenBase> getAllBiomes() {
|
||||||
|
List<BiomeGenBase> biomes = Lists.newArrayList(Collections2.filter(Arrays.asList(BiomeGenBase.getBiomeGenArray()), Predicates.notNull()));
|
||||||
|
Collections.sort(biomes, new Comparator<BiomeGenBase>() {
|
||||||
|
@Override
|
||||||
|
public int compare(BiomeGenBase o1, BiomeGenBase o2) {
|
||||||
|
return o1.biomeName.compareTo(o2.biomeName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return biomes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<BiomeGenBase> biomeIdFilter(final List<Integer> biomeIdList) {
|
||||||
|
return new Predicate<BiomeGenBase>() {
|
||||||
|
public boolean apply(BiomeGenBase biome) {
|
||||||
|
return biomeIdList.contains(biome.biomeID);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Function<BiomeGenBase, Integer> biomeIdTransform() {
|
||||||
|
return new Function<BiomeGenBase, Integer>() {
|
||||||
|
public Integer apply(BiomeGenBase input) {
|
||||||
|
return input.biomeID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<BiomeGenBase> biomeTempRainFilter(final Float minTemp, final Float maxTemp, final Float minRain, final Float maxRain) {
|
||||||
|
return new Predicate<BiomeGenBase>() {
|
||||||
|
public boolean apply(BiomeGenBase biome) {
|
||||||
|
if (minTemp != null && biome.temperature < minTemp) return false;
|
||||||
|
if (maxTemp != null && biome.temperature > maxTemp) return false;
|
||||||
|
if (minRain != null && biome.rainfall < minRain) return false;
|
||||||
|
if (maxRain != null && biome.rainfall > maxRain) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<BiomeGenBase> biomeClassFilter(final Class<?>... classList) {
|
||||||
|
return new Predicate<BiomeGenBase>() {
|
||||||
|
public boolean apply(BiomeGenBase biome) {
|
||||||
|
for (Class<?> clazz : classList)
|
||||||
|
if (clazz.isAssignableFrom(biome.getClass()) || clazz.equals(biome.getClass()))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<BiomeGenBase> biomeClassNameFilter(final String... names) {
|
||||||
|
return new Predicate<BiomeGenBase>() {
|
||||||
|
public boolean apply(BiomeGenBase biome) {
|
||||||
|
for (String name : names) if (biome.getClass().getName().toLowerCase().contains(name.toLowerCase())) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> getFilteredBiomeIds(Collection<BiomeGenBase> biomes, Predicate<BiomeGenBase> filter) {
|
||||||
|
return Lists.newArrayList(Collections2.transform(Collections2.filter(biomes, filter), biomeIdTransform()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
49
src/main/java/mods/betterfoliage/common/util/Double3.java
Normal file
49
src/main/java/mods/betterfoliage/common/util/Double3.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
/** Immutable 3D vector of double precision.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
public class Double3 {
|
||||||
|
|
||||||
|
public final double x;
|
||||||
|
public final double y;
|
||||||
|
public final double z;
|
||||||
|
|
||||||
|
public Double3(double x, double y, double z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3(ForgeDirection dir) {
|
||||||
|
this.x = dir.offsetX;
|
||||||
|
this.y = dir.offsetY;
|
||||||
|
this.z = dir.offsetZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 add(Double3 other) {
|
||||||
|
return new Double3(x + other.x, y + other.y, z + other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 sub(Double3 other) {
|
||||||
|
return new Double3(x - other.x, y - other.y, z - other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 add(double x, double y, double z) {
|
||||||
|
return new Double3(this.x + x, this.y + y, this.z + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 scaleAxes(double sx, double sy, double sz) {
|
||||||
|
return new Double3(x * sx, y * sy, z * sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 scale(double s) {
|
||||||
|
return new Double3(x * s, y * s, z * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double3 inverse() {
|
||||||
|
return new Double3(-x, -y, -z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
import net.minecraft.util.Vec3Pool;
|
||||||
|
import net.minecraft.world.IBlockAccess;
|
||||||
|
import net.minecraft.world.biome.BiomeGenBase;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
/** {@link IBlockAccess} wrapper that applies an offset for a single target coordinate for all rendering-related methods.
|
||||||
|
* Returns normal values for all other coordinates.
|
||||||
|
* @author octarine-noise
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OffsetBlockAccess implements IBlockAccess {
|
||||||
|
|
||||||
|
public IBlockAccess source;
|
||||||
|
public int xTarget, yTarget, zTarget;
|
||||||
|
public int xOffset, yOffset, zOffset;
|
||||||
|
|
||||||
|
public OffsetBlockAccess(IBlockAccess source, int x, int y, int z, int xOffset, int yOffset, int zOffset) {
|
||||||
|
this.source = source;
|
||||||
|
this.xTarget = x;
|
||||||
|
this.yTarget = y;
|
||||||
|
this.zTarget = z;
|
||||||
|
this.xOffset = xOffset;
|
||||||
|
this.yOffset = yOffset;
|
||||||
|
this.zOffset = zOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.getBlock(x + xOffset, y + yOffset, z + zOffset);
|
||||||
|
else
|
||||||
|
return source.getBlock(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TileEntity getTileEntity(int x, int y, int z) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.getTileEntity(x + xOffset, y + yOffset, z + zOffset);
|
||||||
|
else
|
||||||
|
return source.getTileEntity(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int min) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.getLightBrightnessForSkyBlocks(x + xOffset, y + yOffset, z + zOffset, min);
|
||||||
|
else
|
||||||
|
return source.getLightBrightnessForSkyBlocks(x, y, z, min);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockMetadata(int x, int y, int z) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.getBlockMetadata(x + xOffset, y + yOffset, z + zOffset);
|
||||||
|
else
|
||||||
|
return source.getBlockMetadata(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAirBlock(int x, int y, int z) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.isAirBlock(x + xOffset, y + yOffset, z + zOffset);
|
||||||
|
else
|
||||||
|
return source.isAirBlock(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
|
||||||
|
return source.getBiomeGenForCoords(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public int getHeight() {
|
||||||
|
return source.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public boolean extendedLevelsInChunkCache() {
|
||||||
|
return source.extendedLevelsInChunkCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public Vec3Pool getWorldVec3Pool() {
|
||||||
|
return source.getWorldVec3Pool();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int isBlockProvidingPowerTo(int x, int y, int z, int dir) {
|
||||||
|
return source.isBlockProvidingPowerTo(x, y, z, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
|
||||||
|
if (x == xTarget && y == yTarget && z == zTarget)
|
||||||
|
return source.isSideSolid(x + xOffset, y + yOffset, z + zOffset, side, _default);
|
||||||
|
else
|
||||||
|
return source.isSideSolid(x, y, z, side, _default);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||||
|
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||||
|
|
||||||
|
|
||||||
|
public class RenderUtils {
|
||||||
|
|
||||||
|
/** Hide constructor */
|
||||||
|
private RenderUtils() {}
|
||||||
|
|
||||||
|
/** Retrieve a specific rendering handler from the registry
|
||||||
|
* @param renderType render type of block
|
||||||
|
* @return {@link ISimpleBlockRenderingHandler} if defined, null otherwise
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static ISimpleBlockRenderingHandler getRenderingHandler(int renderType) {
|
||||||
|
try {
|
||||||
|
Field field = RenderingRegistry.class.getDeclaredField("INSTANCE");
|
||||||
|
field.setAccessible(true);
|
||||||
|
RenderingRegistry inst = (RenderingRegistry) field.get(null);
|
||||||
|
field = RenderingRegistry.class.getDeclaredField("blockRenderers");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return ((Map<Integer, ISimpleBlockRenderingHandler>) field.get(inst)).get(renderType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stripTooltipDefaultText(List<String> tooltip) {
|
||||||
|
boolean defaultRows = false;
|
||||||
|
Iterator<String> iter = tooltip.iterator();
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
if (iter.next().startsWith(EnumChatFormatting.AQUA.toString())) defaultRows = true;
|
||||||
|
if (defaultRows) iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package mods.betterfoliage.common.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage;
|
||||||
|
import mods.betterfoliage.loader.DeobfHelper;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
import net.minecraft.client.resources.SimpleReloadableResourceManager;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||||
|
import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException;
|
||||||
|
|
||||||
|
public class ResourceUtils {
|
||||||
|
|
||||||
|
/** Hide constructor */
|
||||||
|
private ResourceUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return (({@link SimpleReloadableResourceManager}) Minecraft.getMinecraft().getResourceManager()).domainResourceManagers
|
||||||
|
*/
|
||||||
|
public static Map<String, IResourceManager> getDomainResourceManagers() {
|
||||||
|
try {
|
||||||
|
return ReflectionHelper.<Map<String, IResourceManager>, SimpleReloadableResourceManager> getPrivateValue(
|
||||||
|
SimpleReloadableResourceManager.class, (SimpleReloadableResourceManager) Minecraft.getMinecraft().getResourceManager(), DeobfHelper.transformElementSearge("domainResourceManagers"), "domainResourceManagers"
|
||||||
|
);
|
||||||
|
} catch (UnableToAccessFieldException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check for the existence of a {@link IResource}
|
||||||
|
* @param resourceLocation
|
||||||
|
* @return true if the resource exists
|
||||||
|
*/
|
||||||
|
public static boolean resourceExists(ResourceLocation resourceLocation) {
|
||||||
|
try {
|
||||||
|
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
|
||||||
|
if (resource != null) return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Copy a text file from a resource to the filesystem
|
||||||
|
* @param resourceLocation resource location of text file
|
||||||
|
* @param target target file
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void copyFromTextResource(ResourceLocation resourceLocation, File target) throws IOException {
|
||||||
|
IResource defaults = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(defaults.getInputStream(), Charsets.UTF_8));
|
||||||
|
FileWriter writer = new FileWriter(target);
|
||||||
|
|
||||||
|
String line = reader.readLine();
|
||||||
|
while(line != null) {
|
||||||
|
writer.write(line + System.lineSeparator());
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.close();
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Iterable<String> getLines(ResourceLocation resource) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
List<String> result = Lists.newArrayList();
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new InputStreamReader(Minecraft.getMinecraft().getResourceManager().getResource(resource).getInputStream(), Charsets.UTF_8));
|
||||||
|
String line = reader.readLine();
|
||||||
|
while(line != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (!line.isEmpty() && !line.startsWith("//")) result.add(line);
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
BetterFoliage.log.warn(String.format("Error reading resource %s", resource.toString()));
|
||||||
|
return Lists.newArrayList();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
|
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
|
||||||
|
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
||||||
|
|
||||||
|
public String[] getASMTransformerClass() {
|
||||||
|
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModContainerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetupClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectData(Map<String, Object> data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessTransformerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||||
|
|
||||||
|
public class BetterFoliageTransformer implements IClassTransformer {
|
||||||
|
|
||||||
|
protected Iterable<MethodTransformerBase> transformers = ImmutableList.<MethodTransformerBase>of(
|
||||||
|
new TransformRenderBlockOverride(),
|
||||||
|
new TransformShaderModBlockOverride(),
|
||||||
|
new TransformRandomDisplayTick()
|
||||||
|
);
|
||||||
|
|
||||||
|
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
|
||||||
|
|
||||||
|
public BetterFoliageTransformer() {
|
||||||
|
String mcVersion = FMLInjectionData.data()[4].toString();
|
||||||
|
if (!ImmutableList.<String>of("1.7.2", "1.7.10").contains(mcVersion))
|
||||||
|
logger.warn(String.format("Unsupported Minecraft version %s", mcVersion));
|
||||||
|
|
||||||
|
DeobfHelper.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||||
|
// ???
|
||||||
|
if (basicClass == null) return null;
|
||||||
|
|
||||||
|
// read class
|
||||||
|
ClassNode classNode = new ClassNode();
|
||||||
|
ClassReader classReader = new ClassReader(basicClass);
|
||||||
|
classReader.accept(classNode, 0);
|
||||||
|
boolean hasTransformed = false;
|
||||||
|
|
||||||
|
for (MethodTransformerBase transformer : transformers) {
|
||||||
|
// try to find specified method in class
|
||||||
|
if (!transformedName.equals(transformer.getClassName())) continue;
|
||||||
|
|
||||||
|
logger.debug(String.format("Found class: %s -> %s", name, transformedName));
|
||||||
|
for (MethodNode methodNode : classNode.methods) {
|
||||||
|
logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc));
|
||||||
|
Boolean isObfuscated = null;
|
||||||
|
if (methodNode.name.equals(DeobfHelper.transformElementName(transformer.getMethodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(transformer.getSignature()))) {
|
||||||
|
isObfuscated = true;
|
||||||
|
} else if (methodNode.name.equals(transformer.getMethodName()) && methodNode.desc.equals(transformer.getSignature())) {
|
||||||
|
isObfuscated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObfuscated != null) {
|
||||||
|
// transform
|
||||||
|
hasTransformed = true;
|
||||||
|
try {
|
||||||
|
transformer.transform(methodNode, isObfuscated);
|
||||||
|
logger.info(String.format("%s: SUCCESS", transformer.getLogMessage()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(String.format("%s: FAILURE", transformer.getLogMessage()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return result
|
||||||
|
ClassWriter writer = new ClassWriter(0);
|
||||||
|
if (hasTransformed) classNode.accept(writer);
|
||||||
|
return !hasTransformed ? basicClass : writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
84
src/main/java/mods/betterfoliage/loader/DeobfHelper.java
Normal file
84
src/main/java/mods/betterfoliage/loader/DeobfHelper.java
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||||
|
|
||||||
|
public class DeobfHelper {
|
||||||
|
|
||||||
|
private static Map<String, String> obfClasses = Maps.newHashMap();
|
||||||
|
private static Map<String, String> obfElements = Maps.newHashMap();
|
||||||
|
private static Map<String, String> srgElements = Maps.newHashMap();
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
String mcVersion = FMLInjectionData.data()[4].toString();
|
||||||
|
srgElements.put("domainResourceManagers", "field_110548_a");
|
||||||
|
srgElements.put("mapRegisteredSprites", "field_110574_e");
|
||||||
|
if ("1.7.2".equals(mcVersion)) {
|
||||||
|
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "ble");
|
||||||
|
obfClasses.put("net/minecraft/world/IBlockAccess", "afx");
|
||||||
|
obfClasses.put("net/minecraft/block/Block", "ahu");
|
||||||
|
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "biz");
|
||||||
|
obfClasses.put("net/minecraft/world/World", "afn");
|
||||||
|
|
||||||
|
obfElements.put("blockAccess", "a");
|
||||||
|
obfElements.put("renderBlockByRenderType", "b");
|
||||||
|
obfElements.put("mapRegisteredSprites", "bpr");
|
||||||
|
obfElements.put("doVoidFogParticles", "C");
|
||||||
|
} else if ("1.7.10".equals(mcVersion)) {
|
||||||
|
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "blm");
|
||||||
|
obfClasses.put("net/minecraft/world/IBlockAccess", "ahl");
|
||||||
|
obfClasses.put("net/minecraft/block/Block", "aji");
|
||||||
|
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "bjf");
|
||||||
|
obfClasses.put("net/minecraft/world/World", "ahb");
|
||||||
|
|
||||||
|
obfElements.put("blockAccess", "a");
|
||||||
|
obfElements.put("renderBlockByRenderType", "b");
|
||||||
|
obfElements.put("mapRegisteredSprites", "bpr");
|
||||||
|
obfElements.put("doVoidFogParticles", "C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform a class name from MCP to obfuscated names.
|
||||||
|
* @param className MCP name
|
||||||
|
* @return obfuscated name
|
||||||
|
*/
|
||||||
|
public static String transformClassName(String className) {
|
||||||
|
return obfClasses.containsKey(className) ? obfClasses.get(className) : className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform a method or field name from MCP to obfuscated names.
|
||||||
|
* @param elementName MCP name
|
||||||
|
* @return obfuscated name
|
||||||
|
*/
|
||||||
|
public static String transformElementName(String elementName) {
|
||||||
|
return obfElements.containsKey(elementName) ? obfElements.get(elementName) : elementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform a method or field name from MCP to SRG names.
|
||||||
|
* @param elementName MCP name
|
||||||
|
* @return SRG name
|
||||||
|
*/
|
||||||
|
public static String transformElementSearge(String elementName) {
|
||||||
|
return srgElements.containsKey(elementName) ? srgElements.get(elementName) : elementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform an ASM signature from MCP to obfuscated names.
|
||||||
|
* @param signature MCP signature
|
||||||
|
* @return obfuscated signature
|
||||||
|
*/
|
||||||
|
public static String transformSignature(String signature) {
|
||||||
|
String result = signature;
|
||||||
|
boolean hasChanged = false;
|
||||||
|
do {
|
||||||
|
hasChanged = false;
|
||||||
|
for (Map.Entry<String, String> entry : obfClasses.entrySet()) if (result.contains("L" + entry.getKey() + ";")) {
|
||||||
|
result = result.replace("L" + entry.getKey() + ";", "L" + entry.getValue() + ";");
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
} while(hasChanged);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
/** Base class for class transformers operating on a single method.
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
public abstract class MethodTransformerBase {
|
||||||
|
|
||||||
|
/** Instruction node filter
|
||||||
|
* @author octarine-noise
|
||||||
|
*/
|
||||||
|
public static interface IInstructionMatch {
|
||||||
|
public boolean matches(AbstractInsnNode node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return MCP name of the class to transform
|
||||||
|
*/
|
||||||
|
public abstract String getClassName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return MCP name of the method to transform
|
||||||
|
*/
|
||||||
|
public abstract String getMethodName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ASM signature of the method to transform using MCP names
|
||||||
|
*/
|
||||||
|
public abstract String getSignature();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Log message to write when method is found
|
||||||
|
*/
|
||||||
|
public abstract String getLogMessage();
|
||||||
|
|
||||||
|
/** Transform method node
|
||||||
|
* @param method method node
|
||||||
|
* @param isObfuscated true for obfuscated environment
|
||||||
|
*/
|
||||||
|
public abstract void transform(MethodNode method, boolean isObfuscated);
|
||||||
|
|
||||||
|
/** Transform a class name from MCP to obfuscated names if necessary.
|
||||||
|
* @param className MCP name
|
||||||
|
* @param isObfuscated true for obfuscated environment
|
||||||
|
* @return transformed name
|
||||||
|
*/
|
||||||
|
protected static String className(String className, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformClassName(className) : className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform a method or field name from MCP to obfuscated names if necessary.
|
||||||
|
* @param fieldName MCP name
|
||||||
|
* @param isObfuscated true for obfuscated environment
|
||||||
|
* @return transformed name
|
||||||
|
*/
|
||||||
|
protected static String element(String fieldName, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform an ASM signature from MCP to obfuscated names if necessary.
|
||||||
|
* @param signature MCP signature
|
||||||
|
* @param isObfuscated true for obfuscated environment
|
||||||
|
* @return transformed signature
|
||||||
|
*/
|
||||||
|
protected static String signature(String signature, boolean isObfuscated) {
|
||||||
|
return isObfuscated ? DeobfHelper.transformSignature(signature) : signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find the next instruction node in an instruction list starting from a given node, matching a given filter
|
||||||
|
* @param start start node
|
||||||
|
* @param match filter
|
||||||
|
* @return instruction node if found, null otherwise
|
||||||
|
*/
|
||||||
|
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
|
||||||
|
AbstractInsnNode current = start;
|
||||||
|
while(current != null) {
|
||||||
|
if (match.matches(current)) break;
|
||||||
|
current = current.getNext();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find the previous instruction node in a list starting from a given node, matching a given filter
|
||||||
|
* @param start start node
|
||||||
|
* @param match filter
|
||||||
|
* @return instruction node if found, null otherwise
|
||||||
|
*/
|
||||||
|
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
|
||||||
|
AbstractInsnNode current = start;
|
||||||
|
while(current != null) {
|
||||||
|
if (match.matches(current)) break;
|
||||||
|
current = current.getPrevious();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an instruction node filter matching any invoke instruction
|
||||||
|
*/
|
||||||
|
protected IInstructionMatch matchInvokeAny() {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node instanceof MethodInsnNode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an instruction node filter matching the given opcode
|
||||||
|
*/
|
||||||
|
protected IInstructionMatch matchOpcode(final int opcode) {
|
||||||
|
return new IInstructionMatch() {
|
||||||
|
public boolean matches(AbstractInsnNode node) {
|
||||||
|
return node.getOpcode() == opcode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Insert a list of instruction nodes in a list after a given node
|
||||||
|
* @param insnList instruction list
|
||||||
|
* @param node start node
|
||||||
|
* @param added instructions to add
|
||||||
|
*/
|
||||||
|
protected void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
|
||||||
|
InsnList listAdd = new InsnList();
|
||||||
|
for (AbstractInsnNode inst : added) listAdd.add(inst);
|
||||||
|
insnList.insert(node, listAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Insert a list of instruction nodes in a list before a given node
|
||||||
|
* @param insnList instruction list
|
||||||
|
* @param node start node
|
||||||
|
* @param added instructions to add
|
||||||
|
*/
|
||||||
|
protected void insertBefore(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
|
||||||
|
InsnList listAdd = new InsnList();
|
||||||
|
for (AbstractInsnNode inst : added) listAdd.add(inst);
|
||||||
|
insnList.insertBefore(node, listAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
public class TransformRandomDisplayTick extends MethodTransformerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return "net.minecraft.client.multiplayer.WorldClient";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return "doVoidFogParticles";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignature() {
|
||||||
|
return "(III)V";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogMessage() {
|
||||||
|
return "Applying random display tick call hook";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode endLoop = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IINC));
|
||||||
|
insertBefore(method.instructions, endLoop,
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 10),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 7),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 8),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 9),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "onRandomDisplayTick", signature("(Lnet/minecraft/block/Block;Lnet/minecraft/world/World;III)V", obf))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.FieldInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
public class TransformRenderBlockOverride extends MethodTransformerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return "net.minecraft.client.renderer.RenderBlocks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return "renderBlockByRenderType";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignature() {
|
||||||
|
return "(Lnet/minecraft/block/Block;III)Z";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogMessage() {
|
||||||
|
return "Applying RenderBlocks.renderBlockByRenderType() render type override";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
|
||||||
|
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
|
||||||
|
insertAfter(method.instructions, storeRenderType,
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks", obf), element("blockAccess", obf), signature("Lnet/minecraft/world/IBlockAccess;", obf)),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 2),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 3),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 4),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new VarInsnNode(Opcodes.ILOAD, 5),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I", obf)),
|
||||||
|
new VarInsnNode(Opcodes.ISTORE, 5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
public class TransformShaderModBlockOverride extends MethodTransformerBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return "shadersmodcore.client.Shaders";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return "pushEntity";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignature() {
|
||||||
|
return "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogMessage() {
|
||||||
|
return "Applying Shaders.pushEntity() block id override";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(MethodNode method, boolean obf) {
|
||||||
|
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
|
||||||
|
insertAfter(method.instructions, arrayStore.getPrevious(),
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I", obf))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.Hooks;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.Direction;
|
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
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(Block.class)
|
|
||||||
public class MixinBlock {
|
|
||||||
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z";
|
|
||||||
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;getCullingFace(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Lnet/minecraft/util/shape/VoxelShape;";
|
|
||||||
private static final String randomDisplayTick = "randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override 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.
|
|
||||||
*/
|
|
||||||
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getVoxelShape, ordinal = 1))
|
|
||||||
private static VoxelShape getVoxelShapeOverride(BlockState state, BlockView reader, BlockPos pos, Direction dir) {
|
|
||||||
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
|
|
||||||
*/
|
|
||||||
@Inject(method = randomDisplayTick, at = @At("HEAD"))
|
|
||||||
void onRandomDisplayTick(BlockState state, World world, BlockPos pos, Random rnd, CallbackInfo ci) {
|
|
||||||
// Hooks.onRandomDisplayTick(state.getBlock(), state, world, pos, rnd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.BlockModelsReloadCallback;
|
|
||||||
import net.minecraft.client.render.block.BlockModels;
|
|
||||||
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.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(BlockModels.class)
|
|
||||||
public class MixinBlockModels {
|
|
||||||
|
|
||||||
@Inject(method = "reload()V", at = @At("RETURN"))
|
|
||||||
void onReload(CallbackInfo ci) {
|
|
||||||
BlockModelsReloadCallback.EVENT.invoker().reloadBlockModels((BlockModels) (Object) this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +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.BlockView;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mixin to override the result of {@link BlockState}.getAmbientOcclusionLightValue().
|
|
||||||
*
|
|
||||||
* Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
|
|
||||||
*/
|
|
||||||
@Mixin(AbstractBlock.AbstractBlockState.class)
|
|
||||||
@SuppressWarnings({"deprecation"})
|
|
||||||
public class MixinBlockState {
|
|
||||||
private static final String callFrom = "Lnet/minecraft/block/AbstractBlock$AbstractBlockState;getAmbientOcclusionLightLevel(Lnet/minecraft/world/BlockView;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;getAmbientOcclusionLightLevel(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
|
|
||||||
|
|
||||||
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
|
|
||||||
float getAmbientOcclusionValue(Block block, BlockState state, BlockView reader, BlockPos pos) {
|
|
||||||
return Hooks.getAmbientOcclusionLightValueOverride(block.getAmbientOcclusionLightLevel(state, reader, pos), state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.ClientChunkLoadCallback;
|
|
||||||
import net.minecraft.client.world.ClientChunkManager;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import net.minecraft.world.biome.source.BiomeArray;
|
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Mixin(ClientChunkManager.class)
|
|
||||||
public class MixinClientChunkManager {
|
|
||||||
|
|
||||||
private static final String onLoadChunkFromPacket = "Lnet/minecraft/client/world/ClientChunkManager;loadChunkFromPacket(IILnet/minecraft/world/biome/source/BiomeArray;Lnet/minecraft/network/PacketByteBuf;Lnet/minecraft/nbt/CompoundTag;IZ)Lnet/minecraft/world/chunk/WorldChunk;";
|
|
||||||
|
|
||||||
@Inject(method = onLoadChunkFromPacket, at = @At(value = "RETURN", ordinal = 2))
|
|
||||||
void onLoadChunkFromPacket(int x, int z, @Nullable BiomeArray biomes, PacketByteBuf buf, CompoundTag tag, int verticalStripBitmask, boolean complete, CallbackInfoReturnable<WorldChunk> ci) {
|
|
||||||
ClientChunkLoadCallback.EVENT.invoker().loadChunk(ci.getReturnValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.ClientChunkLoadCallback;
|
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Mixin(targets = {"net.minecraft.client.world.ClientChunkManager$ClientChunkMap"})
|
|
||||||
public class MixinClientChunkManagerChunkMap {
|
|
||||||
|
|
||||||
private static final String onCompareAndSet = "Lnet/minecraft/client/world/ClientChunkManager$ClientChunkMap;compareAndSet(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;";
|
|
||||||
|
|
||||||
@Inject(method = onCompareAndSet, at = @At("HEAD"))
|
|
||||||
void onSetAndCompare(int i, WorldChunk oldChunk, WorldChunk newChunk, CallbackInfoReturnable<WorldChunk> ci) {
|
|
||||||
ClientChunkLoadCallback.EVENT.invoker().unloadChunk(oldChunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.ClientWorldLoadCallback;
|
|
||||||
import mods.betterfoliage.Hooks;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
|
||||||
import net.minecraft.client.render.WorldRenderer;
|
|
||||||
import net.minecraft.client.world.ClientWorld;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.profiler.Profiler;
|
|
||||||
import net.minecraft.util.registry.RegistryKey;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.LevelInfo;
|
|
||||||
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.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@Mixin(ClientWorld.class)
|
|
||||||
public class MixinClientWorld {
|
|
||||||
|
|
||||||
private static final String ctor = "Lnet/minecraft/client/world/ClientWorld;<init>(Lnet/minecraft/client/network/ClientPlayNetworkHandler;Lnet/minecraft/client/world/ClientWorld$Properties;Lnet/minecraft/util/registry/RegistryKey;Lnet/minecraft/world/dimension/DimensionType;ILjava/util/function/Supplier;Lnet/minecraft/client/render/WorldRenderer;ZJ)V";
|
|
||||||
private static final String scheduleBlockRerenderIfNeeded = "Lnet/minecraft/client/world/ClientWorld;scheduleBlockRerenderIfNeeded(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
|
|
||||||
private static final String rendererNotify = "Lnet/minecraft/client/render/WorldRenderer;method_21596(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
|
|
||||||
private static final String worldDisplayTick = "randomBlockDisplayTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)V";
|
|
||||||
private static final String blockDisplayTick = "Lnet/minecraft/block/Block;randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject callback to get notified of client-side blockstate changes.
|
|
||||||
* Used to invalidate caches in the {@link mods.betterfoliage.chunk.ChunkOverlayManager}
|
|
||||||
*/
|
|
||||||
@Inject(method = scheduleBlockRerenderIfNeeded, at = @At(value = "HEAD"))
|
|
||||||
void onClientBlockChanged(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo ci) {
|
|
||||||
Hooks.onClientBlockChanged((ClientWorld) (Object) this, pos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = ctor, at = @At("RETURN"))
|
|
||||||
void onClientWorldCreated(ClientPlayNetworkHandler networkHandler, ClientWorld.Properties properties, RegistryKey<World> registryRef, DimensionType dimensionType, int loadDistance, Supplier<Profiler> profiler, WorldRenderer worldRenderer, boolean debugWorld, long seed, CallbackInfo ci) {
|
|
||||||
ClientWorldLoadCallback.EVENT.invoker().loadWorld((ClientWorld) (Object) this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
|
|
||||||
*/
|
|
||||||
@Inject(method = worldDisplayTick, at = @At(value = "INVOKE", target = blockDisplayTick))
|
|
||||||
void onRandomDisplayTick(int xCenter, int yCenter, int zCenter, int radius, Random random, boolean spawnBarrierParticles, BlockPos.Mutable mutable, CallbackInfo ci) {
|
|
||||||
Hooks.onRandomDisplayTick((ClientWorld) (Object) this, mutable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.ModelLoadingCallback;
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
|
||||||
import net.minecraft.client.render.model.ModelLoader;
|
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.client.util.ModelIdentifier;
|
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
|
||||||
import net.minecraft.resource.ResourceManager;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(ModelLoader.class)
|
|
||||||
public class MixinModelLoader {
|
|
||||||
|
|
||||||
@Shadow @Final private ResourceManager resourceManager;
|
|
||||||
|
|
||||||
// use the same trick fabric-api does to get around the no-mixins-in-constructors policy
|
|
||||||
@Inject(at = @At("HEAD"), method = "addModel")
|
|
||||||
private void addModelHook(ModelIdentifier id, CallbackInfo info) {
|
|
||||||
if (id.getPath().equals("trident_in_hand")) {
|
|
||||||
// last step before stitching
|
|
||||||
ModelLoadingCallback.EVENT.invoker().beginLoadModels((ModelLoader) (Object) this, resourceManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
|
||||||
import net.minecraft.client.render.model.ModelLoader;
|
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(ModelLoader.class)
|
|
||||||
public class MixinModelLoaderOptifine {
|
|
||||||
|
|
||||||
private static final String loaderBake = "Lnet/minecraft/class_1088;getBakedModel(Lnet/minecraft/class_2960;Lnet/minecraft/class_3665;Ljava/util/function/Function;)Lnet/minecraft/class_1087;";
|
|
||||||
private static final String modelBake = "Lnet/minecraft/class_1100;method_4753(Lnet/minecraft/class_1088;Ljava/util/function/Function;Lnet/minecraft/class_3665;Lnet/minecraft/class_2960;)Lnet/minecraft/class_1087;";
|
|
||||||
|
|
||||||
@SuppressWarnings("UnresolvedMixinReference")
|
|
||||||
@Redirect(method = loaderBake, at = @At(value = "INVOKE", target = modelBake), remap = false)
|
|
||||||
BakedModel onBakeModel(
|
|
||||||
UnbakedModel unbaked,
|
|
||||||
ModelLoader loader,
|
|
||||||
Function<SpriteIdentifier, Sprite> textureGetter,
|
|
||||||
ModelBakeSettings rotationContainer,
|
|
||||||
Identifier modelId
|
|
||||||
) {
|
|
||||||
return BakeWrapperManager.INSTANCE.onBake(unbaked, loader, textureGetter, rotationContainer, modelId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
|
||||||
import net.minecraft.client.render.model.ModelLoader;
|
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(ModelLoader.class)
|
|
||||||
public class MixinModelLoaderVanilla {
|
|
||||||
|
|
||||||
private static final String loaderBake = "Lnet/minecraft/client/render/model/ModelLoader;bake(Lnet/minecraft/util/Identifier;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;";
|
|
||||||
private static final String modelBake = "Lnet/minecraft/client/render/model/UnbakedModel;bake(Lnet/minecraft/client/render/model/ModelLoader;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/BakedModel;";
|
|
||||||
|
|
||||||
@Redirect(method = loaderBake, at = @At(value = "INVOKE", target = modelBake))
|
|
||||||
BakedModel onBakeModel(
|
|
||||||
UnbakedModel unbaked,
|
|
||||||
ModelLoader loader,
|
|
||||||
Function<SpriteIdentifier, Sprite> textureGetter,
|
|
||||||
ModelBakeSettings rotationContainer,
|
|
||||||
Identifier modelId
|
|
||||||
) {
|
|
||||||
return BakeWrapperManager.INSTANCE.onBake(unbaked, loader, textureGetter, rotationContainer, modelId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import me.zeroeightsix.fiber.JanksonSettings
|
|
||||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
|
||||||
import mods.betterfoliage.config.BlockConfig
|
|
||||||
import mods.betterfoliage.config.MainConfig
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.block.vanilla.*
|
|
||||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
|
||||||
import mods.betterfoliage.resource.generated.GeneratedBlockTexturePack
|
|
||||||
import net.fabricmc.api.ClientModInitializer
|
|
||||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper
|
|
||||||
import net.fabricmc.fabric.mixin.resource.loader.ResourcePackManagerAccessor
|
|
||||||
import net.fabricmc.loader.api.FabricLoader
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.resource.ResourceType
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
import org.apache.logging.log4j.simple.SimpleLogger
|
|
||||||
import org.apache.logging.log4j.util.PropertiesUtil
|
|
||||||
import java.io.File
|
|
||||||
import java.io.PrintStream
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
object BetterFoliage : ClientModInitializer {
|
|
||||||
const val MOD_ID = "betterfoliage"
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
val configFile get() = File(FabricLoader.getInstance().configDirectory, "BetterFoliage.json")
|
|
||||||
|
|
||||||
val config = MainConfig().apply {
|
|
||||||
if (configFile.exists()) JanksonSettings().deserialize(fiberNode, configFile.inputStream())
|
|
||||||
else JanksonSettings().serialize(fiberNode, configFile.outputStream(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val blockConfig = BlockConfig()
|
|
||||||
val generatedPack = GeneratedBlockTexturePack(Identifier(MOD_ID, "generated"), "betterfoliage-generated", "Better Foliage", "Generated leaf textures")
|
|
||||||
|
|
||||||
/** List of recognized [BlockState]s */
|
|
||||||
var blockTypes = BlockTypeCache()
|
|
||||||
|
|
||||||
override fun onInitializeClient() {
|
|
||||||
// Register generated resource pack
|
|
||||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(generatedPack.reloader)
|
|
||||||
(MinecraftClient.getInstance().resourcePackManager as ResourcePackManagerAccessor)
|
|
||||||
.providers.add(generatedPack.finder)
|
|
||||||
|
|
||||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(blockConfig)
|
|
||||||
|
|
||||||
// Add standard block support
|
|
||||||
BakeWrapperManager.discoverers.add(StandardCactusDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardDirtDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardGrassDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardLeafDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardLilypadDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardMyceliumDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardNetherrackDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardRoundLogDiscovery)
|
|
||||||
BakeWrapperManager.discoverers.add(StandardSandDiscovery)
|
|
||||||
|
|
||||||
// Init overlay layers
|
|
||||||
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
|
|
||||||
|
|
||||||
// Init singletons
|
|
||||||
LeafParticleRegistry
|
|
||||||
StandardLeafModel.Companion
|
|
||||||
StandardGrassModel.Companion
|
|
||||||
StandardRoundLogModel.Companion
|
|
||||||
StandardCactusModel.Companion
|
|
||||||
StandardLilypadModel.Companion
|
|
||||||
DirtModel.Companion
|
|
||||||
StandardSandModel.Companion
|
|
||||||
StandardMyceliumModel.Companion
|
|
||||||
StandardNetherrackModel.Companion
|
|
||||||
RisingSoulParticle.Companion
|
|
||||||
ShadersModIntegration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntList
|
|
||||||
import mods.betterfoliage.util.YarnHelper
|
|
||||||
import net.minecraft.client.texture.Sprite
|
|
||||||
import net.minecraft.world.World
|
|
||||||
|
|
||||||
val VertexFormat_offsets = YarnHelper.requiredField<IntList>("net.minecraft.class_293", "field_1597", "Lit/unimi/dsi/fastutil/ints/IntList;")
|
|
||||||
val BakedQuad_sprite = YarnHelper.requiredField<Sprite>("net.minecraft.class_777", "field_4176", "Lnet/minecraft/class_1058;")
|
|
||||||
val WorldChunk_world = YarnHelper.requiredField<World>("net.minecraft.class_2818", "field_12858", "Lnet/minecraft/class_1937;")
|
|
||||||
val ChunkRendererRegion_world = YarnHelper.requiredField<World>("net.minecraft.class_853", "field_4490", "Lnet/minecraft/class_1937;")
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.event.Event
|
|
||||||
import net.fabricmc.fabric.api.event.EventFactory
|
|
||||||
import net.minecraft.client.render.block.BlockModels
|
|
||||||
import net.minecraft.client.render.model.ModelLoader
|
|
||||||
import net.minecraft.client.world.ClientWorld
|
|
||||||
import net.minecraft.resource.ResourceManager
|
|
||||||
import net.minecraft.world.chunk.WorldChunk
|
|
||||||
|
|
||||||
interface ClientChunkLoadCallback {
|
|
||||||
fun loadChunk(chunk: WorldChunk)
|
|
||||||
fun unloadChunk(chunk: WorldChunk)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField val EVENT: Event<ClientChunkLoadCallback> = EventFactory.createArrayBacked(ClientChunkLoadCallback::class.java) { listeners ->
|
|
||||||
object : ClientChunkLoadCallback {
|
|
||||||
override fun loadChunk(chunk: WorldChunk) { listeners.forEach { it.loadChunk(chunk) } }
|
|
||||||
override fun unloadChunk(chunk: WorldChunk) { listeners.forEach { it.unloadChunk(chunk) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ClientWorldLoadCallback {
|
|
||||||
fun loadWorld(world: ClientWorld)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField val EVENT : Event<ClientWorldLoadCallback> = EventFactory.createArrayBacked(ClientWorldLoadCallback::class.java) { listeners ->
|
|
||||||
object : ClientWorldLoadCallback {
|
|
||||||
override fun loadWorld(world: ClientWorld) { listeners.forEach { it.loadWorld(world) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event fired after [BlockModels.reload] finishes.
|
|
||||||
*/
|
|
||||||
interface BlockModelsReloadCallback {
|
|
||||||
fun reloadBlockModels(blockModels: BlockModels)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField val EVENT: Event<BlockModelsReloadCallback> = EventFactory.createArrayBacked(BlockModelsReloadCallback::class.java) { listeners ->
|
|
||||||
object : BlockModelsReloadCallback {
|
|
||||||
override fun reloadBlockModels(blockModels: BlockModels) {
|
|
||||||
listeners.forEach { it.reloadBlockModels(blockModels) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event fired when the [ModelLoader] first starts loading models.
|
|
||||||
*
|
|
||||||
* This happens during the constructor, so BEWARE!
|
|
||||||
* Try to avoid any interaction until the block texture atlas starts stitching.
|
|
||||||
*/
|
|
||||||
interface ModelLoadingCallback {
|
|
||||||
fun beginLoadModels(loader: ModelLoader, manager: ResourceManager)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField val EVENT: Event<ModelLoadingCallback> = EventFactory.createArrayBacked(ModelLoadingCallback::class.java) { listeners ->
|
|
||||||
object : ModelLoadingCallback {
|
|
||||||
override fun beginLoadModels(loader: ModelLoader, manager: ResourceManager) {
|
|
||||||
listeners.forEach { it.beginLoadModels(loader, manager) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
@file:JvmName("Hooks")
|
|
||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
|
||||||
import mods.betterfoliage.render.block.vanilla.LeafParticleKey
|
|
||||||
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
|
||||||
import mods.betterfoliage.render.particle.FallingLeafParticle
|
|
||||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.plus
|
|
||||||
import mods.betterfoliage.util.random
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.client.world.ClientWorld
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction
|
|
||||||
import net.minecraft.util.shape.VoxelShape
|
|
||||||
import net.minecraft.util.shape.VoxelShapes
|
|
||||||
import net.minecraft.world.BlockView
|
|
||||||
|
|
||||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
|
||||||
if (BetterFoliage.config.enabled &&
|
|
||||||
BetterFoliage.config.roundLogs.enabled &&
|
|
||||||
BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state)
|
|
||||||
) return BetterFoliage.config.roundLogs.dimming.toFloat()
|
|
||||||
return original
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUseNeighborBrightnessOverride(original: Boolean, state: BlockState): Boolean {
|
|
||||||
return original || (BetterFoliage.config.enabled && BetterFoliage.config.roundLogs.enabled && BetterFoliage.blockConfig.logBlocks.matchesClass(state.block));
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState) {
|
|
||||||
ChunkOverlayManager.onBlockChange(worldClient, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRandomDisplayTick(world: ClientWorld, pos: BlockPos) {
|
|
||||||
val state = world.getBlockState(pos)
|
|
||||||
|
|
||||||
if (BetterFoliage.config.enabled &&
|
|
||||||
BetterFoliage.config.risingSoul.enabled &&
|
|
||||||
state.block == Blocks.SOUL_SAND &&
|
|
||||||
world.isAir(pos + Direction.UP.offset) &&
|
|
||||||
Math.random() < BetterFoliage.config.risingSoul.chance) {
|
|
||||||
RisingSoulParticle(world, pos).addIfValid()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BetterFoliage.config.enabled &&
|
|
||||||
BetterFoliage.config.fallingLeaves.enabled &&
|
|
||||||
world.isAir(pos + Direction.DOWN.offset) &&
|
|
||||||
randomD() < BetterFoliage.config.fallingLeaves.chance) {
|
|
||||||
BetterFoliage.blockTypes.getTyped<LeafParticleKey>(state)?.let { key ->
|
|
||||||
val blockColor = MinecraftClient.getInstance().blockColors.getColor(state, world, pos, 0)
|
|
||||||
FallingLeafParticle(world, pos, key, blockColor, random).addIfValid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getVoxelShapeOverride(state: BlockState, reader: BlockView, pos: BlockPos, dir: Direction): VoxelShape {
|
|
||||||
if (BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state)) {
|
|
||||||
return VoxelShapes.empty()
|
|
||||||
}
|
|
||||||
// TODO ?
|
|
||||||
return state.getCullingFace(reader, pos, dir)
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package mods.betterfoliage.chunk
|
|
||||||
|
|
||||||
import mods.betterfoliage.ChunkRendererRegion_world
|
|
||||||
import mods.betterfoliage.util.Int3
|
|
||||||
import mods.betterfoliage.util.allDirections
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.plus
|
|
||||||
import net.minecraft.block.Block
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.client.render.chunk.ChunkRendererRegion
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import net.minecraft.world.WorldView
|
|
||||||
import net.minecraft.world.biome.Biome
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
|
|
||||||
* block-relative coordinates.
|
|
||||||
*/
|
|
||||||
interface BlockCtx {
|
|
||||||
val world: BlockRenderView
|
|
||||||
val pos: BlockPos
|
|
||||||
|
|
||||||
fun offset(dir: Direction) = offset(dir.offset)
|
|
||||||
fun offset(offset: Int3): BlockCtx
|
|
||||||
|
|
||||||
val state: BlockState get() = world.getBlockState(pos)
|
|
||||||
fun state(dir: Direction) = world.getBlockState(pos + dir.offset)
|
|
||||||
fun state(offset: Int3) = world.getBlockState(pos + offset)
|
|
||||||
|
|
||||||
val biome: Biome? get() =
|
|
||||||
(world as? WorldView)?.getBiome(pos) ?:
|
|
||||||
(world as? ChunkRendererRegion)?.let { ChunkRendererRegion_world[it]?.getBiome(pos) }
|
|
||||||
|
|
||||||
val isNormalCube: Boolean get() = state.isOpaqueFullCube(world, pos)
|
|
||||||
|
|
||||||
fun shouldSideBeRendered(side: Direction) = Block.shouldDrawSide(state, world, pos, side)
|
|
||||||
|
|
||||||
fun isNeighborSolid(dir: Direction) = offset(dir).let { it.state.isSideSolidFullSquare(it.world, it.pos, dir.opposite) }
|
|
||||||
|
|
||||||
fun model(dir: Direction) = state(dir).let { MinecraftClient.getInstance().blockRenderManager.getModel(it)!! }
|
|
||||||
fun model(offset: Int3) = state(offset).let { MinecraftClient.getInstance().blockRenderManager.getModel(it)!! }
|
|
||||||
}
|
|
||||||
|
|
||||||
open class BasicBlockCtx(
|
|
||||||
override val world: BlockRenderView,
|
|
||||||
override val pos: BlockPos
|
|
||||||
) : BlockCtx {
|
|
||||||
override val state = world.getBlockState(pos)
|
|
||||||
override fun offset(offset: Int3) = BasicBlockCtx(world, pos + offset)
|
|
||||||
fun cache() = CachedBlockCtx(world, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
open class CachedBlockCtx(world: BlockRenderView, pos: BlockPos) : BasicBlockCtx(world, pos) {
|
|
||||||
var neighbors = Array<BlockState>(6) { world.getBlockState(pos + allDirections[it].offset) }
|
|
||||||
override var biome: Biome? = super.biome
|
|
||||||
override fun state(dir: Direction) = neighbors[dir.ordinal]
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package mods.betterfoliage.chunk
|
|
||||||
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.BlockView
|
|
||||||
import net.minecraft.world.LightType
|
|
||||||
import net.minecraft.world.WorldView
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegating [IBlockAccess] that fakes a _modified_ location to return values from a _target_ location.
|
|
||||||
* All other locations are handled normally.
|
|
||||||
*
|
|
||||||
* @param[original] the [IBlockAccess] that is delegated to
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
|
||||||
open class OffsetBlockView(open val original: BlockView, val modded: BlockPos, val target: BlockPos) : BlockView {
|
|
||||||
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
|
||||||
|
|
||||||
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
|
|
||||||
override fun getBlockEntity(pos: BlockPos) = original.getBlockEntity(actualPos(pos))
|
|
||||||
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
|
||||||
class OffsetExtBlockView(val original: WorldView, val modded: BlockPos, val target: BlockPos) : WorldView by original {
|
|
||||||
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
|
||||||
|
|
||||||
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
|
|
||||||
override fun getBlockEntity(pos: BlockPos) = original.getBlockEntity(actualPos(pos))
|
|
||||||
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
|
|
||||||
|
|
||||||
override fun getLightLevel(type: LightType, pos: BlockPos) = original.getLightLevel(type, actualPos(pos))
|
|
||||||
override fun getBaseLightLevel(pos: BlockPos, light: Int) = original.getBaseLightLevel(actualPos(pos), light)
|
|
||||||
override fun getBiome(pos: BlockPos) = original.getBiome(actualPos(pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporarily replaces the [IBlockReader] used by this [BlockContext] and the corresponding [ExtendedRenderBlocks]
|
|
||||||
* to use an [OffsetEnvBlockReader] while executing this lambda.
|
|
||||||
*
|
|
||||||
* @param[modded] the _modified_ location
|
|
||||||
* @param[target] the _target_ location
|
|
||||||
* @param[func] the lambda to execute
|
|
||||||
*/
|
|
||||||
//inline fun <reified T> BlockContext.withOffset(modded: Int3, target: Int3, func: () -> T): T {
|
|
||||||
// val original = reader!!
|
|
||||||
// reader = OffsetEnvBlockReader(original, pos + modded, pos + target)
|
|
||||||
// val result = func()
|
|
||||||
// reader = original
|
|
||||||
// return result
|
|
||||||
//}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package mods.betterfoliage.chunk
|
|
||||||
|
|
||||||
import mods.betterfoliage.*
|
|
||||||
import mods.betterfoliage.util.YarnHelper
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import net.minecraft.client.render.chunk.ChunkRendererRegion
|
|
||||||
import net.minecraft.client.world.ClientWorld
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.ChunkPos
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import net.minecraft.world.World
|
|
||||||
import net.minecraft.world.WorldView
|
|
||||||
import net.minecraft.world.chunk.WorldChunk
|
|
||||||
import net.minecraft.world.dimension.DimensionType
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.List
|
|
||||||
import kotlin.collections.MutableMap
|
|
||||||
import kotlin.collections.associateWith
|
|
||||||
import kotlin.collections.forEach
|
|
||||||
import kotlin.collections.mutableListOf
|
|
||||||
import kotlin.collections.mutableMapOf
|
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
val BlockRenderView.dimType: DimensionType get() = when {
|
|
||||||
this is WorldView -> dimension
|
|
||||||
this is ChunkRendererRegion -> this[ChunkRendererRegion_world]!!.dimension
|
|
||||||
// this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache]!!.dimType
|
|
||||||
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
|
|
||||||
*/
|
|
||||||
interface ChunkOverlayLayer<T> {
|
|
||||||
fun calculate(ctx: BlockCtx): T
|
|
||||||
fun onBlockUpdate(world: WorldView, pos: BlockPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
|
|
||||||
*/
|
|
||||||
object ChunkOverlayManager : ClientChunkLoadCallback, ClientWorldLoadCallback {
|
|
||||||
|
|
||||||
init {
|
|
||||||
ClientWorldLoadCallback.EVENT.register(this)
|
|
||||||
ClientChunkLoadCallback.EVENT.register(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
val chunkData = IdentityHashMap<DimensionType, MutableMap<ChunkPos, ChunkOverlayData>>()
|
|
||||||
val layers = mutableListOf<ChunkOverlayLayer<*>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the overlay data for a given layer and position
|
|
||||||
*
|
|
||||||
* @param layer Overlay layer to query
|
|
||||||
* @param reader World to use if calculation of overlay value is necessary
|
|
||||||
* @param pos Block position
|
|
||||||
*/
|
|
||||||
fun <T> get(layer: ChunkOverlayLayer<T>, ctx: BlockCtx): T? {
|
|
||||||
val data = chunkData[ctx.world.dimType]?.get(ChunkPos(ctx.pos)) ?: return null
|
|
||||||
data.get(layer, ctx.pos).let { value ->
|
|
||||||
if (value !== ChunkOverlayData.UNCALCULATED) return value
|
|
||||||
val newValue = layer.calculate(ctx)
|
|
||||||
data.set(layer, ctx.pos, newValue)
|
|
||||||
return newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the overlay data for a given layer and position
|
|
||||||
*
|
|
||||||
* @param layer Overlay layer to clear
|
|
||||||
* @param pos Block position
|
|
||||||
*/
|
|
||||||
fun <T> clear(dimension: DimensionType, layer: ChunkOverlayLayer<T>, pos: BlockPos) {
|
|
||||||
chunkData[dimension]?.get(ChunkPos(pos))?.clear(layer, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
|
|
||||||
if (chunkData[world.dimType]?.containsKey(ChunkPos(pos)) == true) {
|
|
||||||
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadChunk(chunk: WorldChunk) {
|
|
||||||
chunk[WorldChunk_world]!!.dimType.let { dim ->
|
|
||||||
val data = chunkData[dim] ?: mutableMapOf<ChunkPos, ChunkOverlayData>().apply { chunkData[dim] = this }
|
|
||||||
data.let { chunks ->
|
|
||||||
// check for existence first because Optifine fires a TON of these
|
|
||||||
if (chunk.pos !in chunks.keys) chunks[chunk.pos] = ChunkOverlayData(layers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unloadChunk(chunk: WorldChunk) {
|
|
||||||
chunk[WorldChunk_world]!!.dimType.let { dim ->
|
|
||||||
chunkData[dim]?.remove(chunk.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadWorld(world: ClientWorld) {
|
|
||||||
val dim = world.dimType
|
|
||||||
// chunkData.keys.forEach { if (it == dim) chunkData[dim] = mutableMapOf() else chunkData.remove(dim)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
|
|
||||||
val BlockPos.isValid: Boolean get() = y in validYRange
|
|
||||||
val rawData = layers.associateWith { emptyOverlay() }
|
|
||||||
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T? else null
|
|
||||||
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data) else null
|
|
||||||
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED) else null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val UNCALCULATED = object {}
|
|
||||||
fun emptyOverlay() = Array(16) { Array(16) { Array<Any?>(256) { UNCALCULATED }}}
|
|
||||||
val validYRange = 0 until 256
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.resource.VeryEarlyReloadListener
|
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelTextureListConfiguration
|
|
||||||
import net.minecraft.resource.ResourceManager
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
|
|
||||||
class BlockConfig : VeryEarlyReloadListener {
|
|
||||||
private val list = mutableListOf<Any>()
|
|
||||||
|
|
||||||
val leafBlocks = blocks("leaves_blocks_default.cfg")
|
|
||||||
val leafModels = models("leaves_models_default.cfg")
|
|
||||||
val grassBlocks = blocks("grass_blocks_default.cfg")
|
|
||||||
val grassModels = models("grass_models_default.cfg")
|
|
||||||
// val mycelium = blocks("mycelium_blocks_default.cfg")
|
|
||||||
// val dirt = blocks("dirt_default.cfg")
|
|
||||||
// val crops = blocks("crop_default.cfg")
|
|
||||||
val logBlocks = blocks("log_blocks_default.cfg")
|
|
||||||
val logModels = models("log_models_default.cfg")
|
|
||||||
// val sand = blocks("sand_default.cfg")
|
|
||||||
// val lilypad = blocks("lilypad_default.cfg")
|
|
||||||
// val cactus = blocks("cactus_default.cfg")
|
|
||||||
// val netherrack = blocks("netherrack_blocks_default.cfg")
|
|
||||||
|
|
||||||
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
|
||||||
private fun models(cfgName: String) = ModelTextureListConfiguration(Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
|
||||||
|
|
||||||
|
|
||||||
override fun getFabricId() = Identifier(BetterFoliage.MOD_ID, "block-config")
|
|
||||||
|
|
||||||
override fun onReloadStarted(manager: ResourceManager) {
|
|
||||||
list.forEach { when(it) {
|
|
||||||
is ConfigurableBlockMatcher -> it.readDefaults(manager)
|
|
||||||
is ModelTextureListConfiguration -> it.readDefaults(manager)
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import me.shedaniel.clothconfig2.api.AbstractConfigListEntry
|
|
||||||
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder
|
|
||||||
import me.shedaniel.clothconfig2.gui.entries.SubCategoryListEntry
|
|
||||||
import me.zeroeightsix.fiber.builder.ConfigValueBuilder
|
|
||||||
import me.zeroeightsix.fiber.tree.ConfigLeaf
|
|
||||||
import me.zeroeightsix.fiber.tree.ConfigNode
|
|
||||||
import me.zeroeightsix.fiber.tree.ConfigValue
|
|
||||||
import net.minecraft.client.resource.language.I18n
|
|
||||||
import net.minecraft.text.LiteralText
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
const val MAX_LINE_LEN = 30
|
|
||||||
|
|
||||||
fun textify(string: String) = LiteralText(string)
|
|
||||||
fun textify(strings: Array<String>) = strings.map(::LiteralText).toTypedArray()
|
|
||||||
|
|
||||||
sealed class DelegatingConfigNode<N: ConfigLeaf>(val fiberNode: N) {
|
|
||||||
abstract fun createClothNode(names: List<String>): AbstractConfigListEntry<*>
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DelegatingConfigValue<T>(fiberNode: ConfigValue<T>) : DelegatingConfigNode<ConfigValue<T>>(fiberNode), ReadOnlyProperty<DelegatingConfigGroup, T>
|
|
||||||
|
|
||||||
open class DelegatingConfigGroup(fiberNode: ConfigNode) : DelegatingConfigNode<ConfigNode>(fiberNode) {
|
|
||||||
val children = mutableListOf<DelegatingConfigNode<*>>()
|
|
||||||
override fun createClothNode(names: List<String>): SubCategoryListEntry {
|
|
||||||
val builder = ConfigEntryBuilder.create()
|
|
||||||
.startSubCategory(textify(names.joinToString(".").translate()))
|
|
||||||
.setTooltip(*textify(names.joinToString(".").translateTooltip()))
|
|
||||||
.setExpanded(false)
|
|
||||||
children.forEach { builder.add(it.createClothNode(names + it.fiberNode.name!!)) }
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
operator fun get(name: String) = children.find { it.fiberNode.name == name }
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DelegatingConfigGroupFactory<T> {
|
|
||||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T: DelegatingConfigGroup> subNode(factory: (ConfigNode)->T) = object : DelegatingConfigGroupFactory<T> {
|
|
||||||
override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
val childNode = ConfigNode(property.name, null)
|
|
||||||
val configGroup = factory(childNode)
|
|
||||||
parent.fiberNode.items.add(childNode)
|
|
||||||
parent.children.add(configGroup)
|
|
||||||
return object : ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = configGroup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DelegatingConfigValueFactory<T> {
|
|
||||||
fun createFiberNode(parent: ConfigNode, name: String): ConfigValue<T>
|
|
||||||
fun createClothNode(node: ConfigValue<T>, names: List<String>): AbstractConfigListEntry<T>
|
|
||||||
|
|
||||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
|
||||||
return object : DelegatingConfigValue<T>(createFiberNode(parent.fiberNode, property.name)) {
|
|
||||||
override fun createClothNode(names: List<String>) = createClothNode(fiberNode, names)
|
|
||||||
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = fiberNode.value!!
|
|
||||||
}.apply { parent.children.add(this) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.translate() = I18n.translate(this)
|
|
||||||
fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) = ("$this.tooltip").translate().let { tooltip ->
|
|
||||||
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() }.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun boolean(
|
|
||||||
default: Boolean,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Boolean)->Boolean = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Boolean> {
|
|
||||||
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Boolean::class.java)
|
|
||||||
.withName(name)
|
|
||||||
.withParent(parent)
|
|
||||||
.withDefaultValue(default)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun createClothNode(node: ConfigValue<Boolean>, names: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startBooleanToggle(textify(langKey(names).translate()), node.value!!)
|
|
||||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
|
||||||
.setSaveConsumer { node.value = valueOverride(it) }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun integer(
|
|
||||||
default: Int, min: Int, max: Int,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Int)->Int = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Int> {
|
|
||||||
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Int::class.java)
|
|
||||||
.withName(name)
|
|
||||||
.withParent(parent)
|
|
||||||
.withDefaultValue(default)
|
|
||||||
.constraints().minNumerical(min).maxNumerical(max).finish()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun createClothNode(node: ConfigValue<Int>, names: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startIntField(textify(langKey(names).translate()), node.value!!)
|
|
||||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
|
||||||
.setMin(min).setMax(max)
|
|
||||||
.setSaveConsumer { node.value = valueOverride(it) }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun double(
|
|
||||||
default: Double, min: Double, max: Double,
|
|
||||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
|
||||||
valueOverride: (Double)->Double = { it }
|
|
||||||
) = object : DelegatingConfigValueFactory<Double> {
|
|
||||||
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Double::class.java)
|
|
||||||
.withName(name)
|
|
||||||
.withParent(parent)
|
|
||||||
.withDefaultValue(default)
|
|
||||||
.constraints().minNumerical(min).maxNumerical(max).finish()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun createClothNode(node: ConfigValue<Double>, names: List<String>) = ConfigEntryBuilder.create()
|
|
||||||
.startDoubleField(textify(langKey(names).translate()), node.value!!)
|
|
||||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
|
||||||
.setMin(min).setMax(max)
|
|
||||||
.setSaveConsumer { node.value = valueOverride(it) }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
val recurring = { names: List<String> -> "${names.first()}.${names.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 me.zeroeightsix.fiber.tree.ConfigNode
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
interface PopulationConfigData {
|
|
||||||
val enabled: Boolean
|
|
||||||
val population: Int
|
|
||||||
fun enabled(random: Random) = random.nextInt(64) < population && enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
fun population(default: Int) = integer(default, min = 0, max = 64, langKey = recurring)
|
|
||||||
|
|
||||||
class MainConfig : DelegatingConfigGroup(ConfigNode("root", null)) {
|
|
||||||
|
|
||||||
val enabled by boolean(true, langKey = fakeCategory("global"))
|
|
||||||
val nVidia by boolean(true, langKey = fakeCategory("global"))
|
|
||||||
|
|
||||||
val leaves by subNode { LeavesConfig(it) }
|
|
||||||
val shortGrass by subNode { ShortGrassConfig(it) }
|
|
||||||
val connectedGrass by subNode { ConnectedGrassConfig(it) }
|
|
||||||
val roundLogs by subNode { RoundLogConfig(it) }
|
|
||||||
val cactus by subNode { CactusConfig(it) }
|
|
||||||
val lilypad by subNode { LilypadConfig(it) }
|
|
||||||
val reed by subNode { ReedConfig(it) }
|
|
||||||
val algae by subNode { AlgaeConfig(it) }
|
|
||||||
val coral by subNode { CoralConfig(it) }
|
|
||||||
val netherrack by subNode { NetherrackConfig(it) }
|
|
||||||
val fallingLeaves by subNode { FallingLeavesConfig(it) }
|
|
||||||
val risingSoul by subNode { RisingSoulConfig(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class LeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val snowEnabled by boolean(true)
|
|
||||||
val dense by boolean(false)
|
|
||||||
val hideInternal by boolean(true)
|
|
||||||
|
|
||||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
val saturationThreshold by double(0.1, min = 0.0, max = 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
|
||||||
override val enabled by boolean(true, langKey = recurring)
|
|
||||||
val myceliumEnabled by boolean(true)
|
|
||||||
val snowEnabled by boolean(true)
|
|
||||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val heightMin by double(0.6, min = 0.1, max = 2.5, langKey = recurring)
|
|
||||||
val heightMax by double(0.6, min = 0.1, max = 2.5, langKey = recurring) { it.coerceAtLeast(heightMin) }
|
|
||||||
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
|
|
||||||
override val population by population(64)
|
|
||||||
val useGenerated by boolean(false)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
val saturationThreshold by double(0.1, min = 0.0, max = 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectedGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val snowEnabled by boolean(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoundLogConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
|
|
||||||
val defaultY by boolean(false)
|
|
||||||
val connectSolids by boolean(false)
|
|
||||||
val lenientConnect by boolean(true)
|
|
||||||
val connectPerpendicular by boolean(true)
|
|
||||||
val connectGrass by boolean(true)
|
|
||||||
|
|
||||||
val radiusSmall by double(0.25, min = 0.0, max = 0.5)
|
|
||||||
val radiusLarge by double(0.44, min = 0.0, max = 0.5) { it.coerceAtLeast(radiusSmall) }
|
|
||||||
val dimming by double(0.7, min = 0.0, max = 1.0)
|
|
||||||
val zProtection by double(0.99, min = 0.9, max = 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
class CactusConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val size by double(1.3, min = 1.0, max = 2.0, langKey = recurring)
|
|
||||||
val sizeVariation by double(0.1, min = 0.0, max = 0.5)
|
|
||||||
val hOffset by double(0.1, min = 0.0, max = 0.5, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LilypadConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
|
||||||
override val enabled by boolean(true, langKey = recurring)
|
|
||||||
val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring)
|
|
||||||
override val population by population(16)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
|
||||||
override val enabled by boolean(true, langKey = recurring)
|
|
||||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val heightMin by double(1.7, min = 1.5, max = 3.0, langKey = recurring)
|
|
||||||
val heightMax by double(2.2, min = 1.5, max = 3.0, langKey = recurring) { it.coerceAtLeast(heightMin) }
|
|
||||||
override val population by population(32)
|
|
||||||
val minBiomeTemp by double(0.4, min = 0.0, max = 2.0)
|
|
||||||
val minBiomeRainfall by double(0.4, min = 0.0, max = 1.0)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlgaeConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
|
||||||
override val enabled by boolean(true, langKey = recurring)
|
|
||||||
val hOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
|
|
||||||
val heightMin by double(0.5, min = 0.1, max = 1.0, langKey = recurring)
|
|
||||||
val heightMax by double(0.5, min = 0.1, max = 1.0, langKey = recurring) { it.coerceAtLeast(heightMin) }
|
|
||||||
override val population by population(48)
|
|
||||||
val shaderWind by boolean(true, langKey = recurring)
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoralConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
|
||||||
override val enabled by boolean(true, langKey = recurring)
|
|
||||||
val shallowWater by boolean(false)
|
|
||||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val size by double(0.7, min = 0.5, max = 1.5, langKey = recurring)
|
|
||||||
val crustSize by double(1.4, min = 0.5, max = 1.5)
|
|
||||||
val chance by integer(32, min = 0, max = 64)
|
|
||||||
override val population by population(48)
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetherrackConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
|
||||||
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
|
|
||||||
val heightMin by double(0.6, min = 0.5, max = 1.5, langKey = recurring)
|
|
||||||
val heightMax by double(0.8, min = 0.5, max = 1.5, langKey = recurring) { it.coerceAtLeast(heightMin) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class FallingLeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val opacityHack by boolean(false)
|
|
||||||
val speed by double(0.05, min = 0.01, max = 0.15)
|
|
||||||
val windStrength by double(0.5, min = 0.1, max = 2.0)
|
|
||||||
val stormStrength by double(0.8, min = 0.1, max = 2.0) { it.coerceAtLeast(windStrength) }
|
|
||||||
val size by double(0.75, min = 0.25, max = 1.5)
|
|
||||||
val chance by double(0.05, min = 0.001, max = 1.0)
|
|
||||||
val perturb by double(0.25, min = 0.01, max = 1.0)
|
|
||||||
val lifetime by double(7.5, min = 1.0, max = 15.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RisingSoulConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
|
||||||
val enabled by boolean(true, langKey = recurring)
|
|
||||||
val chance by double(0.02, min = 0.001, max = 1.0)
|
|
||||||
val perturb by double(0.05, min = 0.01, max = 0.25)
|
|
||||||
val headSize by double(1.0, min = 0.25, max = 1.5)
|
|
||||||
val trailSize by double(0.75, min = 0.25, max = 1.5)
|
|
||||||
val opacity by double(0.5, min = 0.05, max = 1.0)
|
|
||||||
val sizeDecay by double(0.97, min = 0.5, max = 1.0)
|
|
||||||
val opacityDecay by double(0.97, min = 0.5, max = 1.0)
|
|
||||||
val lifetime by double(4.0, min = 1.0, max = 15.0)
|
|
||||||
val trailLength by integer(48, min = 2, max = 128)
|
|
||||||
val trailDensity by integer(3, min = 1, max = 16)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package mods.betterfoliage.config
|
|
||||||
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.block.Material
|
|
||||||
import net.minecraft.world.biome.Biome
|
|
||||||
|
|
||||||
val CACTUS_BLOCKS = listOf(Blocks.CACTUS)
|
|
||||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL)
|
|
||||||
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
|
|
||||||
val NETHERRACK_BLOCKS = listOf(Blocks.NETHERRACK)
|
|
||||||
|
|
||||||
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
|
|
||||||
val SNOW_MATERIALS = listOf(Material.SNOW_BLOCK)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package mods.betterfoliage.integration
|
|
||||||
|
|
||||||
import io.github.prospector.modmenu.api.ModMenuApi
|
|
||||||
import me.shedaniel.clothconfig2.api.ConfigBuilder
|
|
||||||
import me.zeroeightsix.fiber.JanksonSettings
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.client.gui.screen.Screen
|
|
||||||
import net.minecraft.client.resource.language.I18n
|
|
||||||
import net.minecraft.text.LiteralText
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
object ModMenu : ModMenuApi {
|
|
||||||
override fun getModId() = BetterFoliage.MOD_ID
|
|
||||||
|
|
||||||
override fun getConfigScreenFactory() = Function { screen: Screen ->
|
|
||||||
val builder = ConfigBuilder.create()
|
|
||||||
.setParentScreen(screen)
|
|
||||||
.setTitle(LiteralText(I18n.translate("betterfoliage.title")))
|
|
||||||
BetterFoliage.config.createClothNode(listOf("betterfoliage")).value.forEach { rootOption ->
|
|
||||||
builder.getOrCreateCategory(LiteralText("main")).addEntry(rootOption)
|
|
||||||
}
|
|
||||||
builder.savingRunnable = Runnable {
|
|
||||||
JanksonSettings().serialize(BetterFoliage.config.fiberNode, BetterFoliage.configFile.outputStream(), false)
|
|
||||||
BakeWrapperManager.invalidate()
|
|
||||||
MinecraftClient.getInstance().worldRenderer.reload()
|
|
||||||
}
|
|
||||||
builder.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
package mods.betterfoliage.integration
|
|
||||||
|
|
||||||
|
|
||||||
object IC2RubberIntegration {
|
|
||||||
|
|
||||||
// val BlockRubWood = ClassRefOld<Any>("ic2.core.block.BlockRubWood")
|
|
||||||
|
|
||||||
init {
|
|
||||||
// if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
|
|
||||||
// BetterFoliage.log(Level.INFO, "IC2 rubber support initialized")
|
|
||||||
// LogRegistry.registries.add(IC2LogDiscovery)
|
|
||||||
// BetterFoliage.blockSprites.providers.add(IC2LogDiscovery)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TechRebornRubberIntegration {
|
|
||||||
|
|
||||||
// val BlockRubberLog = ClassRefOld<Any>("techreborn.blocks.BlockRubberLog")
|
|
||||||
|
|
||||||
init {
|
|
||||||
// if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
|
|
||||||
// BetterFoliage.log(Level.INFO, "TechReborn rubber support initialized")
|
|
||||||
// LogRegistry.registries.add(TechRebornLogDiscovery)
|
|
||||||
// BetterFoliage.blockSprites.providers.add(TechRebornLogDiscovery)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
class RubberLogInfo(
|
|
||||||
axis: Axis?,
|
|
||||||
val spotDir: Direction,
|
|
||||||
topTexture: Sprite,
|
|
||||||
bottomTexture: Sprite,
|
|
||||||
val spotTexture: Sprite,
|
|
||||||
sideTextures: List<Sprite>
|
|
||||||
) : SimpleColumnInfo(axis, topTexture, bottomTexture, sideTextures) {
|
|
||||||
|
|
||||||
override val side: QuadIconResolver = { ctx: CombinedContext, idx: Int, quad: Quad ->
|
|
||||||
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.modelRotation)
|
|
||||||
if (worldFace == spotDir) spotTexture else {
|
|
||||||
val sideIdx = if (this.sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % this.sideTextures.size else 0
|
|
||||||
this.sideTextures[sideIdx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object IC2LogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
|
|
||||||
override val logger = BetterFoliage.logDetail
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
|
|
||||||
// check for proper block class, existence of ModelBlock, and "state" blockstate property
|
|
||||||
if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null
|
|
||||||
val blockLoc = ctx.models.firstOrNull() as Pair<JsonUnbakedModel, Identifier> ?: return null
|
|
||||||
val type = ctx.state.entries.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
|
|
||||||
|
|
||||||
// logs with no rubber spot
|
|
||||||
if (blockLoc.derivesFrom(Identifier("block/cube_column"))) {
|
|
||||||
val axis = when(type) {
|
|
||||||
"plain_y" -> Axis.Y
|
|
||||||
"plain_x" -> Axis.X
|
|
||||||
"plain_z" -> Axis.Z
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTexture(it) }
|
|
||||||
if (textureNames.any { it == "missingno" }) return null
|
|
||||||
log("IC2LogSupport: block state ${ctx.state.toString()}")
|
|
||||||
log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
|
|
||||||
val endSprite = atlas.sprite(textureNames[0])
|
|
||||||
val sideSprite = atlas.sprite(textureNames[1])
|
|
||||||
return atlas.mapAfter {
|
|
||||||
SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logs with rubber spot
|
|
||||||
val spotDir = when(type) {
|
|
||||||
"dry_north", "wet_north" -> NORTH
|
|
||||||
"dry_south", "wet_south" -> SOUTH
|
|
||||||
"dry_west", "wet_west" -> WEST
|
|
||||||
"dry_east", "wet_east" -> EAST
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTexture(it) }
|
|
||||||
if (textureNames.any { it == "missingno" }) return null
|
|
||||||
log("IC2LogSupport: block state ${ctx.state.toString()}")
|
|
||||||
log("IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
|
||||||
val upSprite = atlas.sprite(textureNames[0])
|
|
||||||
val downSprite = atlas.sprite(textureNames[1])
|
|
||||||
val sideSprite = atlas.sprite(textureNames[2])
|
|
||||||
val spotSprite = atlas.sprite(textureNames[3])
|
|
||||||
return if (spotDir != null) atlas.mapAfter {
|
|
||||||
RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get()))
|
|
||||||
} else atlas.mapAfter {
|
|
||||||
SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TechRebornLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
|
|
||||||
override val logger = BetterFoliage.logDetail
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
|
|
||||||
// check for proper block class, existence of ModelBlock
|
|
||||||
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) return null
|
|
||||||
val blockLoc = ctx.models.map { it as? Pair<JsonUnbakedModel, Identifier> }.firstOrNull() ?: return null
|
|
||||||
|
|
||||||
val hasSap = ctx.state.entries.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
|
|
||||||
val sapSide = ctx.state.entries.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
|
|
||||||
|
|
||||||
log("$logName: block state ${ctx.state}")
|
|
||||||
if (hasSap) {
|
|
||||||
val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTexture(it) }
|
|
||||||
log("$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
|
||||||
if (textureNames.all { it != "missingno" }) {
|
|
||||||
val endSprite = atlas.sprite(textureNames[0])
|
|
||||||
val sideSprite = atlas.sprite(textureNames[1])
|
|
||||||
val sapSprite = atlas.sprite(textureNames[2])
|
|
||||||
return atlas.mapAfter {
|
|
||||||
RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTexture(it) }
|
|
||||||
log("$logName: end=${textureNames[0]}, side=${textureNames[1]}")
|
|
||||||
if (textureNames.all { it != "missingno" }) {
|
|
||||||
val endSprite = atlas.sprite(textureNames[0])
|
|
||||||
val sideSprite = atlas.sprite(textureNames[1])
|
|
||||||
return atlas.mapAfter {
|
|
||||||
SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.BakedQuad_sprite
|
|
||||||
import mods.betterfoliage.VertexFormat_offsets
|
|
||||||
import mods.betterfoliage.util.Double3
|
|
||||||
import mods.betterfoliage.util.allDirections
|
|
||||||
import mods.betterfoliage.util.findFirst
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.render.VertexFormat
|
|
||||||
import net.minecraft.client.render.VertexFormatElement
|
|
||||||
import net.minecraft.client.render.VertexFormatElement.Format.FLOAT
|
|
||||||
import net.minecraft.client.render.VertexFormatElement.Format.UBYTE
|
|
||||||
import net.minecraft.client.render.VertexFormatElement.Type.*
|
|
||||||
import net.minecraft.client.render.VertexFormatElement.Type.UV
|
|
||||||
import net.minecraft.client.render.VertexFormats
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BakedQuad
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.util.math.Direction
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
interface BakedModelConverter {
|
|
||||||
/**
|
|
||||||
* Convert baked model. Returns null if conversion unsuccessful (wrong input type).
|
|
||||||
* @param model Input model
|
|
||||||
* @param converter Converter to use for converting nested models.
|
|
||||||
*/
|
|
||||||
fun convert(model: BakedModel): BakedModel?
|
|
||||||
companion object {
|
|
||||||
fun of(func: (BakedModel)->BakedModel?) = object : BakedModelConverter {
|
|
||||||
override fun convert(model: BakedModel) = func(model)
|
|
||||||
}
|
|
||||||
val identity = of { model -> model }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert [BakedModel] using the provided list of [BakedModelConverter]s (in order).
|
|
||||||
* If all converters fail, gives the original model back.
|
|
||||||
*/
|
|
||||||
fun List<BakedModelConverter>.convert(model: BakedModel) = object : BakedModelConverter {
|
|
||||||
val converters = this@convert + BakedModelConverter.identity
|
|
||||||
override fun convert(model: BakedModel) = converters.findFirst { it.convert(model) }
|
|
||||||
}.let { converterStack ->
|
|
||||||
// we are guaranteed a result here because of the identity converter
|
|
||||||
converterStack.convert(model)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert [BasicBakedModel] into one using fabric-rendering-api [Mesh] instead of the vanilla pipeline.
|
|
||||||
* @param blendMode Use the given [BlockRenderLayer] for the [Mesh]
|
|
||||||
* instead of the one declared by the corresponding [Block]
|
|
||||||
*/
|
|
||||||
fun meshifyStandard(model: BasicBakedModel, state: BlockState? = null, blendMode: BlendMode? = null) =
|
|
||||||
WrappedMeshModel.converter(state, blendModeOverride = blendMode).convert(model)!!
|
|
||||||
|
|
||||||
fun meshifySolid(model: BasicBakedModel) = meshifyStandard(model, null, BlendMode.SOLID)
|
|
||||||
fun meshifyCutoutMipped(model: BasicBakedModel) = meshifyStandard(model, null, BlendMode.CUTOUT_MIPPED)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a vanilla [BakedModel] into intermediate [Quad]s
|
|
||||||
* Vertex normals not supported (yet)
|
|
||||||
* Vertex data elements not aligned to 32 bit boundaries not supported
|
|
||||||
*/
|
|
||||||
fun unbakeQuads(model: BakedModel, state: BlockState?, random: Random, unshade: Boolean): List<Quad> {
|
|
||||||
return (allDirections.toList() + null as Direction?).flatMap { face ->
|
|
||||||
model.getQuads(state, face, random).mapIndexed { qIdx, bakedQuad ->
|
|
||||||
var quad = Quad(Vertex(), Vertex(), Vertex(), Vertex(), face = face, colorIndex = bakedQuad.colorIndex, sprite = bakedQuad[BakedQuad_sprite])
|
|
||||||
|
|
||||||
val format = quadVertexFormat(bakedQuad)
|
|
||||||
val stride = format.vertexSizeInteger
|
|
||||||
format.getIntOffset(POSITION, FLOAT, 3)?.let { posOffset ->
|
|
||||||
quad = quad.transformVI { vertex, vIdx -> vertex.copy(xyz = Double3(
|
|
||||||
x = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 0]).toDouble(),
|
|
||||||
y = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 1]).toDouble(),
|
|
||||||
z = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 2]).toDouble()
|
|
||||||
)) }
|
|
||||||
}
|
|
||||||
format.getIntOffset(COLOR, UBYTE, 4)?.let { colorOffset ->
|
|
||||||
quad = quad.transformVI { vertex, vIdx -> vertex.copy(
|
|
||||||
color = Color(bakedQuad.vertexData[vIdx * stride + colorOffset])
|
|
||||||
) }
|
|
||||||
}
|
|
||||||
format.getIntOffset(UV, FLOAT, 2, 0)?.let { uvOffset ->
|
|
||||||
quad = quad.transformVI { vertex, vIdx -> vertex.copy(uv = UV(
|
|
||||||
u = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 0]).toDouble(),
|
|
||||||
v = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 1]).toDouble()
|
|
||||||
)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
quad = quad.transformV { it.copy(uv = it.uv.unbake(quad.sprite!!)) }.move(Double3(-0.5, -0.5, -0.5))
|
|
||||||
if (unshade) quad = quad.transformV { it.copy(color = it.color * (1.0f / Color.bakeShade(quad.face))) }
|
|
||||||
quad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the byte offset of the [VertexFormatElement] matching the given criteria */
|
|
||||||
fun VertexFormat.getByteOffset(type: VertexFormatElement.Type, format: VertexFormatElement.Format, count: Int, index: Int = 0): Int? {
|
|
||||||
elements.forEachIndexed { idx, element ->
|
|
||||||
if (element == VertexFormatElement(index, format, type, count))
|
|
||||||
return VertexFormat_offsets[this]!!.getInt(idx)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the int (32 bit) offset of the [VertexFormatElement] matching the given criteria
|
|
||||||
* Returns null if the element is not properly aligned
|
|
||||||
*/
|
|
||||||
fun VertexFormat.getIntOffset(type: VertexFormatElement.Type, format: VertexFormatElement.Format, count: Int, index: Int = 0) =
|
|
||||||
getByteOffset(type, format, count, index)?.let { if (it % 4 == 0) it / 4 else null }
|
|
||||||
|
|
||||||
/** Function to determine [VertexFormat] used by [BakedQuad] */
|
|
||||||
var quadVertexFormat: (BakedQuad)->VertexFormat = { VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL }
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.*
|
|
||||||
import mods.betterfoliage.util.minmax
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
|
||||||
import net.minecraft.client.texture.MissingSprite
|
|
||||||
import net.minecraft.client.texture.Sprite
|
|
||||||
import net.minecraft.util.math.Direction
|
|
||||||
import net.minecraft.util.math.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: sprite coordinates fall between (-0.5, 0.5)
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
|
|
||||||
fun unbake(sprite: Sprite) = UV(
|
|
||||||
(u - sprite.minU.toDouble()) / (sprite.maxU - sprite.minU).toDouble() - 0.5,
|
|
||||||
(v - sprite.minV.toDouble()) / (sprite.maxV - sprite.minV).toDouble() - 0.5
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
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)
|
|
||||||
/** Amount of vanilla diffuse lighting applied to face quads */
|
|
||||||
fun bakeShade(dir: Direction?) = when(dir) {
|
|
||||||
DOWN -> 0.5f
|
|
||||||
NORTH, SOUTH -> 0.8f
|
|
||||||
EAST, WEST -> 0.6f
|
|
||||||
else -> 1.0f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
|
||||||
companion object {
|
|
||||||
fun fromColor(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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val asColor: Int get() = java.awt.Color.HSBtoRGB(hue, saturation, brightness)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model vertex
|
|
||||||
*
|
|
||||||
* @param[xyz] x, y, z coordinates
|
|
||||||
* @param[uv] u, v coordinates
|
|
||||||
* @param[color] vertex color RGB components
|
|
||||||
* @param[alpha] vertex color alpha component
|
|
||||||
*/
|
|
||||||
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 alpha: Int = 255,
|
|
||||||
val normal: Double3? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intermediate (fabric-renderer-api independent) 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: Sprite? = 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: Sprite) = 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: Int?) = color(color ?: Color.white.asInt).colorIndex(if (color == null) 0 else -1)
|
|
||||||
|
|
||||||
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 List<Quad>.transform(trans: Quad.()-> Quad) = map { it.trans() }
|
|
||||||
fun Array<List<Quad>>.transform(trans: Quad.(Int)-> Quad) = mapIndexed { idx, qList -> qList.map { it.trans(idx) } }.toTypedArray()
|
|
||||||
|
|
||||||
fun List<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
|
||||||
fun Array<List<Quad>>.withOpposites() = map { it.withOpposites() }.toTypedArray()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pour quad data into a fabric-renderer-api Mesh
|
|
||||||
*/
|
|
||||||
fun List<Quad>.build(blendMode: BlendMode, noDiffuse: Boolean = false, flatLighting: Boolean = false): Mesh {
|
|
||||||
val renderer = RendererAccess.INSTANCE.renderer!!
|
|
||||||
val material = renderer.materialFinder().blendMode(0, blendMode).disableAo(0, flatLighting).disableDiffuse(0, noDiffuse).find()
|
|
||||||
val builder = renderer.meshBuilder()
|
|
||||||
builder.emitter.apply {
|
|
||||||
forEach { quad ->
|
|
||||||
val sprite = quad.sprite ?: Atlas.BLOCKS[MissingSprite.getMissingSpriteId()]!!
|
|
||||||
quad.verts.forEachIndexed { idx, vertex ->
|
|
||||||
pos(idx, (vertex.xyz + Double3(0.5, 0.5, 0.5)).asVec3f)
|
|
||||||
sprite(idx, 0,
|
|
||||||
(sprite.maxU - sprite.minU) * (vertex.uv.u.toFloat() + 0.5f) + sprite.minU,
|
|
||||||
(sprite.maxV - sprite.minV) * (vertex.uv.v.toFloat() + 0.5f) + sprite.minV
|
|
||||||
)
|
|
||||||
spriteColor(idx, 0, vertex.color.asInt)
|
|
||||||
}
|
|
||||||
cullFace(quad.face)
|
|
||||||
colorIndex(quad.colorIndex)
|
|
||||||
material(material)
|
|
||||||
emit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Array<List<Quad>>.build(blendMode: BlendMode, noDiffuse: Boolean = false, flatLighting: Boolean = false) = map { it.build(blendMode, noDiffuse, flatLighting) }.toTypedArray()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The model should be positioned so that (0,0,0) is the block center.
|
|
||||||
* The block extends to (-0.5, 0.5) in all directions (inclusive).
|
|
||||||
*/
|
|
||||||
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),
|
|
||||||
face = face
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.client.texture.MissingSprite
|
|
||||||
import net.minecraft.client.texture.Sprite
|
|
||||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
interface SpriteSet {
|
|
||||||
val num: Int
|
|
||||||
operator fun get(idx: Int): Sprite
|
|
||||||
}
|
|
||||||
|
|
||||||
class FixedSpriteSet(val sprites: List<Sprite>) : SpriteSet {
|
|
||||||
override val num = sprites.size
|
|
||||||
override fun get(idx: Int) = sprites[idx % num]
|
|
||||||
|
|
||||||
constructor(atlas: Atlas, ids: List<Identifier>) : this(
|
|
||||||
ids.mapNotNull { atlas[it] }.let { sprites ->
|
|
||||||
if (sprites.isNotEmpty()) sprites else listOf(atlas[MissingSprite.getMissingSpriteId()]!!)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpriteDelegate(val atlas: Atlas, val idFunc: ()->Identifier) : ReadOnlyProperty<Any, Sprite>, ClientSpriteRegistryCallback {
|
|
||||||
private var id: Identifier? = null
|
|
||||||
private var value: Sprite? = null
|
|
||||||
|
|
||||||
init { ClientSpriteRegistryCallback.event(atlas.resourceId).register(this) }
|
|
||||||
|
|
||||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
|
||||||
id = idFunc(); value = null
|
|
||||||
registry.register(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any, property: KProperty<*>): Sprite {
|
|
||||||
value?.let { return it }
|
|
||||||
synchronized(this) {
|
|
||||||
value?.let { return it }
|
|
||||||
atlas[id!!]!!.let { value = it; return it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SpriteSetDelegate(
|
|
||||||
val atlas: Atlas,
|
|
||||||
val idRegister: (Identifier)->Identifier = { it },
|
|
||||||
val idFunc: (Int)->Identifier
|
|
||||||
) : ReadOnlyProperty<Any, SpriteSet>, ClientSpriteRegistryCallback {
|
|
||||||
private var idList: List<Identifier> = emptyList()
|
|
||||||
private var spriteSet: SpriteSet? = null
|
|
||||||
init { ClientSpriteRegistryCallback.event(atlas.resourceId).register(this) }
|
|
||||||
|
|
||||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
|
||||||
spriteSet = null
|
|
||||||
val manager = MinecraftClient.getInstance().resourceManager
|
|
||||||
idList = (0 until 16).map(idFunc).filter { manager.containsResource(atlas.file(it)) }.map(idRegister)
|
|
||||||
idList.forEach { registry.register(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any, property: KProperty<*>): SpriteSet {
|
|
||||||
spriteSet?.let { return it }
|
|
||||||
synchronized(this) {
|
|
||||||
spriteSet?.let { return it }
|
|
||||||
spriteSet = FixedSpriteSet(atlas, idList)
|
|
||||||
return spriteSet!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.*
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
|
||||||
import net.minecraft.client.texture.Sprite
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.Direction.UP
|
|
||||||
|
|
||||||
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) =
|
|
||||||
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>, tintIndex: Int, spriteGetter: (Int)->Sprite) = 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.colorIndex(tintIndex) }
|
|
||||||
.map { it.sprite(spriteGetter(idx)) }
|
|
||||||
}.toTypedArray()
|
|
||||||
|
|
||||||
fun fullCubeTextured(spriteId: Identifier, tintIndex: Int, scrambleUV: Boolean = true): Mesh {
|
|
||||||
val sprite = Atlas.BLOCKS[spriteId]!!
|
|
||||||
return allDirections.map { faceQuad(it) }
|
|
||||||
.map { if (!scrambleUV) it else it.rotateUV(randomI(max = 4)) }
|
|
||||||
.map { it.sprite(sprite) }
|
|
||||||
.map { it.colorIndex(tintIndex) }
|
|
||||||
.build(BlendMode.SOLID, noDiffuse = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Array<List<Quad>> {
|
|
||||||
return Array(num) { idx ->
|
|
||||||
listOf(
|
|
||||||
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41),
|
|
||||||
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: Sprite, tintIndex: Int,scrambleUV: Boolean) =
|
|
||||||
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
|
||||||
.map { it.colorIndex(tintIndex) }
|
|
||||||
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED)
|
|
||||||
|
|
||||||
fun crossModelsTextured(
|
|
||||||
leafBase: Array<List<Quad>>,
|
|
||||||
tintIndex: Int,
|
|
||||||
scrambleUV: Boolean,
|
|
||||||
spriteGetter: (Int) -> Identifier
|
|
||||||
) = leafBase.mapIndexed { idx, leaf ->
|
|
||||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
|
||||||
}.toTypedArray()
|
|
||||||
|
|
||||||
fun Array<List<Quad>>.buildTufts() = withOpposites().build(BlendMode.CUTOUT_MIPPED)
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package mods.betterfoliage.model
|
|
||||||
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.render.RenderLayers
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.item.ItemStack
|
|
||||||
import net.minecraft.util.collection.WeightedPicker
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.*
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
abstract class ModelWrapKey : ModelBakingKey, HasLogger() {
|
|
||||||
override fun bake(ctx: ModelBakingContext): BakedModel? {
|
|
||||||
val baseModel = super.bake(ctx)
|
|
||||||
if (baseModel is BasicBakedModel)
|
|
||||||
return bake(ctx, baseModel)
|
|
||||||
else
|
|
||||||
return baseModel
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class WrappedBakedModel(val wrapped: BakedModel) : BakedModel by wrapped, FabricBakedModel {
|
|
||||||
override fun isVanillaAdapter() = false
|
|
||||||
|
|
||||||
override fun emitItemQuads(stack: ItemStack, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
(wrapped as FabricBakedModel).emitItemQuads(stack, randomSupplier, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WrappedMeshModel(wrapped: BasicBakedModel, val mesh: Mesh) : WrappedBakedModel(wrapped) {
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
context.meshConsumer().accept(mesh)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Converter for [BasicBakedModel] instances.
|
|
||||||
* @param state [BlockState] to use when querying [BakedModel]
|
|
||||||
* @param unshade undo vanilla diffuse lighting when unbaking the [BakedModel]
|
|
||||||
* @param noDiffuse disable diffuse lighting when baking the [Mesh]
|
|
||||||
* @param blendModeOverride [BlockRenderLayer] to use instead of the one declared by the corresponding [Block]
|
|
||||||
*/
|
|
||||||
fun converter(state: BlockState?, unshade: Boolean = false, noDiffuse: Boolean = true, blendModeOverride: BlendMode? = null) = BakedModelConverter.of { model ->
|
|
||||||
if (model is BasicBakedModel) {
|
|
||||||
val mesh = unbakeQuads(model, state, Random(42L), unshade).build(
|
|
||||||
blendMode = blendModeOverride ?: BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)),
|
|
||||||
noDiffuse = noDiffuse,
|
|
||||||
flatLighting = !model.useAmbientOcclusion()
|
|
||||||
)
|
|
||||||
WrappedMeshModel(model, mesh)
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WeightedModelWrapper(
|
|
||||||
val models: List<WeightedModel>, baseModel: BakedModel
|
|
||||||
): WrappedBakedModel(baseModel), FabricBakedModel {
|
|
||||||
|
|
||||||
class WeightedModel(val model: BakedModel, val weight: Int) : WeightedPicker.Entry(weight)
|
|
||||||
fun getModel(random: Random) = WeightedPicker.getRandom(random, models).model
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
(getModel(randomSupplier.get()) as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUnderlyingModel(model: BakedModel, random: Random): BakedModel = when(model) {
|
|
||||||
is WeightedModelWrapper -> getUnderlyingModel(model.getModel(random), random)
|
|
||||||
is WrappedBakedModel -> model.wrapped
|
|
||||||
else -> model
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package mods.betterfoliage.render
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.render.lighting.getBufferBuilder
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
|
||||||
import mods.betterfoliage.util.getAllMethods
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.render.BufferBuilder
|
|
||||||
import net.minecraft.client.render.RenderLayer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration for ShadersMod.
|
|
||||||
*/
|
|
||||||
object ShadersModIntegration : HasLogger() {
|
|
||||||
|
|
||||||
val BufferBuilder_SVertexBuilder = BufferBuilder::class.java.fields.find { it.name == "sVertexBuilder" }
|
|
||||||
val SVertexBuilder_pushState = getAllMethods("net.optifine.shaders.SVertexBuilder", "pushEntity").find { it.parameterCount == 1 }
|
|
||||||
val SVertexBuilder_popState = getAllMethods("net.optifine.shaders.SVertexBuilder", "popEntity").find { it.parameterCount == 0 }
|
|
||||||
val BlockAliases_getAliasBlockId = getAllMethods("net.optifine.shaders.BlockAliases", "getAliasBlockId").firstOrNull()
|
|
||||||
|
|
||||||
@JvmStatic val isAvailable =
|
|
||||||
listOf(BufferBuilder_SVertexBuilder).all { it != null } &&
|
|
||||||
listOf(SVertexBuilder_pushState, SVertexBuilder_popState, BlockAliases_getAliasBlockId).all { it != null }
|
|
||||||
|
|
||||||
val defaultLeaves = Blocks.OAK_LEAVES.defaultState
|
|
||||||
val defaultGrass = Blocks.TALL_GRASS.defaultState
|
|
||||||
|
|
||||||
init {
|
|
||||||
logger.info("[BetterFoliage] ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
|
||||||
inline fun renderAs(ctx: RenderContext, state: BlockState, layer: RenderLayer, enabled: Boolean = true, func: ()->Unit) {
|
|
||||||
if (isAvailable && enabled) {
|
|
||||||
val sVertexBuilder = BufferBuilder_SVertexBuilder!!.get(ctx.getBufferBuilder(layer))
|
|
||||||
val aliasBlockId = BlockAliases_getAliasBlockId!!.invoke(null, state)
|
|
||||||
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: RenderContext, enabled: Boolean = true, func: ()->Unit) =
|
|
||||||
renderAs(ctx, defaultGrass, RenderLayer.getCutoutMipped(), enabled, func)
|
|
||||||
|
|
||||||
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
|
|
||||||
inline fun leaves(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) =
|
|
||||||
renderAs(ctx, defaultLeaves, RenderLayer.getCutoutMipped(), enabled, func)
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.config.CACTUS_BLOCKS
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteDelegate
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
|
||||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.roundLeafLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.horizontalDirections
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.DOWN
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
|
|
||||||
object StandardCactusDiscovery : AbstractModelDiscovery() {
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
val model = ctx.getUnbaked()
|
|
||||||
if (model is JsonUnbakedModel && ctx.blockState.block in CACTUS_BLOCKS) {
|
|
||||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
|
||||||
ctx.addReplacement(StandardCactusKey)
|
|
||||||
ctx.sprites.add(StandardCactusModel.cactusCrossSprite)
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardCactusKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardCactusModel(meshifyCutoutMipped(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardCactusModel(wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
|
|
||||||
|
|
||||||
val armLighting = horizontalDirections.map { grassTuftLighting(it) }
|
|
||||||
val crossLighting = roundLeafLighting()
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.cactus.enabled) return
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
val armSide = random.nextInt() and 3
|
|
||||||
|
|
||||||
context.withLighting(armLighting[armSide]) {
|
|
||||||
it.accept(cactusArmModels[armSide][random])
|
|
||||||
}
|
|
||||||
context.withLighting(crossLighting) {
|
|
||||||
it.accept(cactusCrossModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val cactusCrossSprite = Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus")
|
|
||||||
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx")
|
|
||||||
}
|
|
||||||
val cactusArmModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = BetterFoliage.config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
|
|
||||||
val models = tuftModelSet(shapes, Color.white.asInt) { cactusArmSprites[randomI()] }
|
|
||||||
horizontalDirections.map { side ->
|
|
||||||
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
val cactusCrossModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val models = BetterFoliage.config.cactus.let { config ->
|
|
||||||
crossModelsRaw(64, config.size, 0.0, 0.0)
|
|
||||||
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
|
|
||||||
}
|
|
||||||
crossModelsTextured(models, Color.white.asInt, true) { cactusCrossSprite }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
|
||||||
import mods.betterfoliage.config.DIRT_BLOCKS
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.build
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.model.withOpposites
|
|
||||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
|
||||||
import mods.betterfoliage.model.getUnderlyingModel
|
|
||||||
import mods.betterfoliage.model.meshifySolid
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.reedLighting
|
|
||||||
import mods.betterfoliage.render.lighting.renderMasquerade
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.generated.CenteredSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Int3
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.block.Material
|
|
||||||
import net.minecraft.client.render.RenderLayer
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.UP
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardDirtDiscovery : AbstractModelDiscovery() {
|
|
||||||
fun canRenderInLayer(layer: RenderLayer) = when {
|
|
||||||
!BetterFoliage.config.enabled -> layer == RenderLayer.getSolid()
|
|
||||||
(!BetterFoliage.config.connectedGrass.enabled &&
|
|
||||||
!BetterFoliage.config.algae.enabled &&
|
|
||||||
!BetterFoliage.config.reed.enabled
|
|
||||||
) -> layer == RenderLayer.getSolid()
|
|
||||||
else -> layer == RenderLayer.getCutoutMipped()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in DIRT_BLOCKS) {
|
|
||||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
|
||||||
ctx.addReplacement(StandardDirtKey)
|
|
||||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardDirtKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = DirtModel(meshifySolid(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
|
||||||
|
|
||||||
val algaeLighting = grassTuftLighting(UP)
|
|
||||||
val reedLighting = reedLighting()
|
|
||||||
|
|
||||||
override fun emitBlockQuads(
|
|
||||||
blockView: BlockRenderView,
|
|
||||||
state: BlockState,
|
|
||||||
pos: BlockPos,
|
|
||||||
randomSupplier: Supplier<Random>,
|
|
||||||
context: RenderContext
|
|
||||||
) {
|
|
||||||
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
|
|
||||||
val ctx = BasicBlockCtx(blockView, pos)
|
|
||||||
val stateUp = ctx.offset(UP).state
|
|
||||||
val isGrassUp = stateUp in BetterFoliage.blockTypes.grass
|
|
||||||
|
|
||||||
val isWater = stateUp.material == Material.WATER
|
|
||||||
val isDeepWater = isWater && ctx.offset(Int3(2 to UP)).state.material == Material.WATER
|
|
||||||
val isShallowWater = isWater && ctx.offset(Int3(2 to UP)).state.isAir
|
|
||||||
val isSaltWater = isWater && ctx.biome?.category in SALTWATER_BIOMES
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
if (BetterFoliage.config.connectedGrass.enabled && isGrassUp) {
|
|
||||||
val grassBaseModel = getUnderlyingModel(ctx.model(UP), random)
|
|
||||||
context.renderMasquerade(grassBaseModel, blockView, stateUp, pos, randomSupplier, context)
|
|
||||||
} else {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BetterFoliage.config.algae.enabled(random) && isDeepWater) {
|
|
||||||
ShadersModIntegration.grass(context, BetterFoliage.config.algae.shaderWind) {
|
|
||||||
context.withLighting(algaeLighting) {
|
|
||||||
it.accept(algaeModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) {
|
|
||||||
ShadersModIntegration.grass(context, BetterFoliage.config.reed.shaderWind) {
|
|
||||||
context.withLighting(reedLighting) {
|
|
||||||
it.accept(reedModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val algaeSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_algae_$idx")
|
|
||||||
}
|
|
||||||
val reedSprites by SpriteSetDelegate(Atlas.BLOCKS,
|
|
||||||
idFunc = { idx -> Identifier(BetterFoliage.MOD_ID, "blocks/better_reed_$idx") },
|
|
||||||
idRegister = { id -> CenteredSprite(id, aspectHeight = 2).register(BetterFoliage.generatedPack) }
|
|
||||||
)
|
|
||||||
val algaeModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes =
|
|
||||||
BetterFoliage.config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { algaeSprites[randomI()] }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
|
||||||
|
|
||||||
}
|
|
||||||
val reedModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = BetterFoliage.config.reed.let { tuftShapeSet(2.0, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { reedSprites[randomI()] }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
|
||||||
import mods.betterfoliage.config.BlockConfig
|
|
||||||
import mods.betterfoliage.config.SNOW_MATERIALS
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.*
|
|
||||||
import mods.betterfoliage.model.*
|
|
||||||
import mods.betterfoliage.util.*
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.*
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.*
|
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.grassBlocks
|
|
||||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.grassModels.modelList
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
|
||||||
ctx.addReplacement(StandardGrassKey(textureMatch[0], null))
|
|
||||||
BetterFoliage.blockTypes.grass.add(ctx.blockState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardGrassKey(
|
|
||||||
val grassLocation: Identifier,
|
|
||||||
val overrideColor: Color?
|
|
||||||
) : ModelWrapKey() {
|
|
||||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
|
||||||
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel {
|
|
||||||
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
|
||||||
logColorOverride(detailLogger, BetterFoliage.config.shortGrass.saturationThreshold, hsb)
|
|
||||||
hsb.colorOverride(BetterFoliage.config.shortGrass.saturationThreshold)
|
|
||||||
}
|
|
||||||
return StandardGrassModel(meshifyCutoutMipped(wrapped), this.copy(overrideColor = grassSpriteColor))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardGrassModel(wrapped: BakedModel, val key: StandardGrassKey) : WrappedBakedModel(wrapped) {
|
|
||||||
|
|
||||||
val tuftNormal by grassTuftMeshesNormal.delegate(key)
|
|
||||||
val tuftSnowed by grassTuftMeshesSnowed.delegate(key)
|
|
||||||
val fullBlock by grassFullBlockMeshes.delegate(key)
|
|
||||||
|
|
||||||
val tuftLighting = grassTuftLighting(UP)
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
|
|
||||||
val ctx = BasicBlockCtx(blockView, pos)
|
|
||||||
val stateBelow = ctx.state(DOWN)
|
|
||||||
val stateAbove = ctx.state(UP)
|
|
||||||
|
|
||||||
val isSnowed = stateAbove.material in SNOW_MATERIALS
|
|
||||||
val connected = BetterFoliage.config.connectedGrass.enabled &&
|
|
||||||
(!isSnowed || BetterFoliage.config.connectedGrass.snowEnabled) &&
|
|
||||||
(stateBelow in BetterFoliage.blockTypes.dirt || stateBelow in BetterFoliage.blockTypes.grass)
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
if (connected) {
|
|
||||||
context.meshConsumer().accept(if (isSnowed) snowFullBlockMeshes[random] else fullBlock[random])
|
|
||||||
} else {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) {
|
|
||||||
ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
|
|
||||||
context.withLighting(tuftLighting) {
|
|
||||||
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val grassTuftSpritesNormal by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
|
|
||||||
}
|
|
||||||
val grassTuftSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
|
|
||||||
}
|
|
||||||
val grassTuftShapes = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
|
||||||
BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
}
|
|
||||||
val grassTuftMeshesNormal = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
|
||||||
tuftModelSet(grassTuftShapes[key], key.tintIndex) { idx -> grassTuftSpritesNormal[randomI()] }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
|
||||||
}
|
|
||||||
val grassTuftMeshesSnowed = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
|
||||||
tuftModelSet(grassTuftShapes[key], Color.white.asInt) { idx -> grassTuftSpritesSnowed[randomI()] }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
|
||||||
}
|
|
||||||
val grassFullBlockMeshes = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
|
||||||
Array(64) { fullCubeTextured(key.grassLocation, key.tintIndex) }
|
|
||||||
}
|
|
||||||
val snowFullBlockMeshes by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
Array(64) { fullCubeTextured(Identifier("block/snow"), -1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
|
||||||
import mods.betterfoliage.config.SNOW_MATERIALS
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
|
||||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.lighting.roundLeafLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
|
||||||
import mods.betterfoliage.resource.generated.GeneratedLeafSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyMap
|
|
||||||
import mods.betterfoliage.util.averageColor
|
|
||||||
import mods.betterfoliage.util.colorOverride
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.logColorOverride
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.UP
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
interface LeafBlockModel {
|
|
||||||
val key: LeafParticleKey
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LeafParticleKey {
|
|
||||||
val leafType: String
|
|
||||||
val overrideColor: Color?
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardLeafDiscovery : ConfigurableModelDiscovery() {
|
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.leafBlocks
|
|
||||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.leafModels.modelList
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
|
||||||
val leafType = LeafParticleRegistry.typeMappings.getType(textureMatch[0]) ?: "default"
|
|
||||||
val generated = GeneratedLeafSprite(textureMatch[0], leafType)
|
|
||||||
.register(BetterFoliage.generatedPack)
|
|
||||||
.apply { ctx.sprites.add(this) }
|
|
||||||
|
|
||||||
detailLogger.log(Level.INFO, " particle $leafType")
|
|
||||||
ctx.addReplacement(StandardLeafKey(generated, leafType, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StandardLeafKey(
|
|
||||||
val roundLeafTexture: Identifier,
|
|
||||||
override val leafType: String,
|
|
||||||
override val overrideColor: Color?
|
|
||||||
) : ModelWrapKey(), LeafParticleKey {
|
|
||||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
|
||||||
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel {
|
|
||||||
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
|
||||||
logColorOverride(detailLogger, BetterFoliage.config.leaves.saturationThreshold, hsb)
|
|
||||||
hsb.colorOverride(BetterFoliage.config.leaves.saturationThreshold)
|
|
||||||
}
|
|
||||||
return StandardLeafModel(meshifyCutoutMipped(wrapped), this.copy(overrideColor = leafSpriteColor))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardLeafModel(
|
|
||||||
wrapped: BakedModel,
|
|
||||||
override val key: StandardLeafKey
|
|
||||||
) : WrappedBakedModel(wrapped), LeafBlockModel {
|
|
||||||
|
|
||||||
val leafNormal by leafModelsNormal.delegate(key)
|
|
||||||
val leafSnowed by leafModelsSnowed.delegate(key)
|
|
||||||
val leafLighting = roundLeafLighting()
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
ShadersModIntegration.leaves(context, BetterFoliage.config.leaves.shaderWind) {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return
|
|
||||||
|
|
||||||
val ctx = BasicBlockCtx(blockView, pos)
|
|
||||||
val stateAbove = ctx.state(UP)
|
|
||||||
val isSnowed = stateAbove.material in SNOW_MATERIALS
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
context.withLighting(leafLighting) {
|
|
||||||
it.accept(leafNormal[random])
|
|
||||||
if (isSnowed) it.accept(leafSnowed[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val leafSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_leaves_snowed_$idx")
|
|
||||||
}
|
|
||||||
val leafModelsBase = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
|
||||||
BetterFoliage.config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
|
||||||
}
|
|
||||||
val leafModelsNormal = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
|
||||||
crossModelsTextured(leafModelsBase[key], key.tintIndex, true) { key.roundLeafTexture }
|
|
||||||
}
|
|
||||||
val leafModelsSnowed = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
|
||||||
crossModelsTextured(leafModelsBase[key], Color.white.asInt, false) { leafSpritesSnowed[it].id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.DOWN
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardLilypadDiscovery : AbstractModelDiscovery() {
|
|
||||||
val LILYPAD_BLOCKS = listOf(Blocks.LILY_PAD)
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in LILYPAD_BLOCKS) {
|
|
||||||
ctx.addReplacement(StandardLilypadKey)
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardLilypadKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardLilypadModel(meshifyCutoutMipped(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardLilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
ShadersModIntegration.grass(context, BetterFoliage.config.lilypad.shaderWind) {
|
|
||||||
context.meshConsumer().accept(lilypadRootModels[random])
|
|
||||||
}
|
|
||||||
if (random.nextInt(64) < BetterFoliage.config.lilypad.population) {
|
|
||||||
context.meshConsumer().accept(lilypadFlowerModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val lilypadRootSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_lilypad_roots_$idx")
|
|
||||||
}
|
|
||||||
val lilypadFlowerSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx")
|
|
||||||
}
|
|
||||||
val lilypadRootModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = tuftShapeSet(1.0, 1.0, 1.0, BetterFoliage.config.lilypad.hOffset)
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { lilypadRootSprites[it] }
|
|
||||||
.transform { move(2.0 to DOWN) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
val lilypadFlowerModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = tuftShapeSet(0.5, 0.5, 0.5, BetterFoliage.config.lilypad.hOffset)
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { lilypadFlowerSprites[it] }
|
|
||||||
.transform { move(1.0 to DOWN) }
|
|
||||||
.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.render.ShadersModIntegration
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.plus
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.UP
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardMyceliumDiscovery : AbstractModelDiscovery() {
|
|
||||||
val MYCELIUM_BLOCKS = listOf(Blocks.MYCELIUM)
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in MYCELIUM_BLOCKS) {
|
|
||||||
ctx.addReplacement(StandardMyceliumKey)
|
|
||||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutout())
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardMyceliumKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardMyceliumModel(meshifyCutoutMipped(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardMyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
|
||||||
|
|
||||||
val tuftLighting = grassTuftLighting(UP)
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
if (BetterFoliage.config.enabled &&
|
|
||||||
BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } &&
|
|
||||||
blockView.getBlockState(pos + UP.offset).isAir
|
|
||||||
) {
|
|
||||||
ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
|
|
||||||
context.withLighting(tuftLighting) {
|
|
||||||
it.accept(myceliumTuftModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val myceliumTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_mycel_$idx")
|
|
||||||
}
|
|
||||||
val myceliumTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.config.NETHERRACK_BLOCKS
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.build
|
|
||||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.model.withOpposites
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.offset
|
|
||||||
import mods.betterfoliage.util.plus
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.client.render.RenderLayer
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.DOWN
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardNetherrackDiscovery : AbstractModelDiscovery() {
|
|
||||||
|
|
||||||
fun canRenderInLayer(layer: RenderLayer) = when {
|
|
||||||
!BetterFoliage.config.enabled -> layer == RenderLayer.getSolid()
|
|
||||||
!BetterFoliage.config.netherrack.enabled -> layer == RenderLayer.getSolid()
|
|
||||||
else -> layer == RenderLayer.getCutoutMipped()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in NETHERRACK_BLOCKS) {
|
|
||||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
|
||||||
ctx.addReplacement(StandardNetherrackKey)
|
|
||||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardNetherrackKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardNetherrackModel(meshifyCutoutMipped(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardNetherrackModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
|
||||||
|
|
||||||
val tuftLighting = grassTuftLighting(DOWN)
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
if (BetterFoliage.config.enabled &&
|
|
||||||
BetterFoliage.config.netherrack.enabled &&
|
|
||||||
blockView.getBlockState(pos + DOWN.offset).isAir
|
|
||||||
) {
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
context.withLighting(tuftLighting) {
|
|
||||||
it.accept(netherrackTuftModels[random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_netherrack_$idx")
|
|
||||||
}
|
|
||||||
val netherrackTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = BetterFoliage.config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { netherrackTuftSprites[randomI()] }
|
|
||||||
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.meshifySolid
|
|
||||||
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.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyMap
|
|
||||||
import mods.betterfoliage.util.tryDefault
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.PillarBlock
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.Direction.Axis
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
|
|
||||||
interface RoundLogKey : ColumnBlockKey, ModelBakingKey {
|
|
||||||
val barkSprite: Identifier
|
|
||||||
val endSprite: Identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
object RoundLogOverlayLayer : ColumnRenderLayer() {
|
|
||||||
override fun getColumnKey(state: BlockState) = BetterFoliage.blockTypes.getTyped<ColumnBlockKey>(state)
|
|
||||||
override val connectSolids: Boolean get() = BetterFoliage.config.roundLogs.connectSolids
|
|
||||||
override val lenientConnect: Boolean get() = BetterFoliage.config.roundLogs.lenientConnect
|
|
||||||
override val defaultToY: Boolean get() = BetterFoliage.config.roundLogs.defaultY
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardRoundLogDiscovery : ConfigurableModelDiscovery() {
|
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.logBlocks
|
|
||||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.logModels.modelList
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
|
||||||
val axis = getAxis(ctx.blockState)
|
|
||||||
detailLogger.log(Level.INFO, " axis $axis")
|
|
||||||
ctx.addReplacement(StandardRoundLogKey(axis, textureMatch[0], textureMatch[1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAxis(state: BlockState): Axis? {
|
|
||||||
val axis = tryDefault(null) { state.get(PillarBlock.AXIS).toString() } ?:
|
|
||||||
state.entries.entries.find { it.key.getName().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: Identifier,
|
|
||||||
override val endSprite: Identifier
|
|
||||||
) : RoundLogKey, ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardRoundLogModel(meshifySolid(wrapped), this)
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardRoundLogModel(wrapped: BakedModel, val key: StandardRoundLogKey) : ColumnModelBase(wrapped) {
|
|
||||||
override val enabled: Boolean get() = BetterFoliage.config.enabled && BetterFoliage.config.roundLogs.enabled
|
|
||||||
override val overlayLayer: ColumnRenderLayer get() = RoundLogOverlayLayer
|
|
||||||
override val connectPerpendicular: Boolean get() = BetterFoliage.config.roundLogs.connectPerpendicular
|
|
||||||
|
|
||||||
val modelSet by modelSets.delegate(key)
|
|
||||||
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val modelSets = LazyMap(BakeWrapperManager) { key: StandardRoundLogKey ->
|
|
||||||
val barkSprite = Atlas.BLOCKS[key.barkSprite]!!
|
|
||||||
val endSprite = Atlas.BLOCKS[key.endSprite]!!
|
|
||||||
BetterFoliage.config.roundLogs.let { config ->
|
|
||||||
ColumnMeshSet(
|
|
||||||
config.radiusSmall, config.radiusLarge, config.zProtection,
|
|
||||||
key.axis ?: Axis.Y,
|
|
||||||
barkSprite, barkSprite,
|
|
||||||
endSprite, endSprite
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.chunk.CachedBlockCtx
|
|
||||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
|
||||||
import mods.betterfoliage.config.SAND_BLOCKS
|
|
||||||
import mods.betterfoliage.model.Color
|
|
||||||
import mods.betterfoliage.model.ModelWrapKey
|
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.WrappedBakedModel
|
|
||||||
import mods.betterfoliage.model.build
|
|
||||||
import mods.betterfoliage.model.horizontalRectangle
|
|
||||||
import mods.betterfoliage.model.meshifySolid
|
|
||||||
import mods.betterfoliage.model.meshifyStandard
|
|
||||||
import mods.betterfoliage.model.transform
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
import mods.betterfoliage.model.withOpposites
|
|
||||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
|
||||||
import mods.betterfoliage.render.lighting.withLighting
|
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
|
||||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
|
||||||
import mods.betterfoliage.util.Rotation
|
|
||||||
import mods.betterfoliage.util.allDirections
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.randomB
|
|
||||||
import mods.betterfoliage.util.randomD
|
|
||||||
import mods.betterfoliage.util.randomI
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.block.Material
|
|
||||||
import net.minecraft.client.render.model.BakedModel
|
|
||||||
import net.minecraft.client.render.model.BasicBakedModel
|
|
||||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
|
||||||
import net.minecraft.util.Identifier
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.Direction.UP
|
|
||||||
import net.minecraft.world.BlockRenderView
|
|
||||||
import java.util.Random
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
object StandardSandDiscovery : AbstractModelDiscovery() {
|
|
||||||
|
|
||||||
|
|
||||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
|
||||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in SAND_BLOCKS) {
|
|
||||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
|
||||||
ctx.addReplacement(StandardSandKey)
|
|
||||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutoutMipped())
|
|
||||||
}
|
|
||||||
super.processModel(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StandardSandKey : ModelWrapKey() {
|
|
||||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardSandModel(meshifySolid(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
class StandardSandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
|
||||||
|
|
||||||
val coralLighting = allDirections.map { grassTuftLighting(it) }.toTypedArray()
|
|
||||||
|
|
||||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
|
||||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
|
||||||
|
|
||||||
val ctx = CachedBlockCtx(blockView, pos)
|
|
||||||
|
|
||||||
val random = randomSupplier.get()
|
|
||||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.coral.enabled(random)) return
|
|
||||||
if (ctx.biome?.category !in SALTWATER_BIOMES) return
|
|
||||||
|
|
||||||
allDirections.filter { random.nextInt(64) < BetterFoliage.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) context.withLighting(coralLighting[face]) {
|
|
||||||
it.accept(coralCrustModels[face][random])
|
|
||||||
it.accept(coralTuftModels[face][random])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// val sandModel by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
|
||||||
// Array(64) { fullCubeTextured(Identifier("block/sand"), Color.white.asInt) }
|
|
||||||
// }
|
|
||||||
|
|
||||||
val coralTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_coral_$idx")
|
|
||||||
}
|
|
||||||
val coralCrustSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
|
||||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_crust_$idx")
|
|
||||||
}
|
|
||||||
val coralTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
val shapes = BetterFoliage.config.coral.let { tuftShapeSet(it.size, 1.0, 1.0, it.hOffset) }
|
|
||||||
allDirections.map { face ->
|
|
||||||
tuftModelSet(shapes, Color.white.asInt) { coralTuftSprites[randomI()] }
|
|
||||||
.transform { rotate(Rotation.fromUp[face]) }
|
|
||||||
.withOpposites()
|
|
||||||
.build(BlendMode.CUTOUT_MIPPED)
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
val coralCrustModels by LazyInvalidatable(BakeWrapperManager) {
|
|
||||||
allDirections.map { face ->
|
|
||||||
Array(64) { idx ->
|
|
||||||
listOf(horizontalRectangle(x1 = -0.5, x2 = 0.5, z1 = -0.5, z2 = 0.5, y = 0.0)
|
|
||||||
.scale(BetterFoliage.config.coral.crustSize)
|
|
||||||
.move(0.5 + randomD(0.01, BetterFoliage.config.coral.vOffset) to UP)
|
|
||||||
.rotate(Rotation.fromUp[face])
|
|
||||||
.mirrorUV(randomB(), randomB()).rotateUV(randomI(max = 4))
|
|
||||||
.sprite(coralCrustSprites[idx]).colorAndIndex(null)
|
|
||||||
).build(BlendMode.CUTOUT_MIPPED)
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user