Compare commits
43 Commits
1.15.2-For
...
1.16.5-For
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
178a014a6b | ||
|
|
0b802663dc | ||
|
|
d2b9326ced | ||
|
|
31eddf682d | ||
|
|
e689a44687 | ||
|
|
b4824b77ae | ||
|
|
4c08354d74 | ||
|
|
0518b01b50 | ||
|
|
563a67f213 | ||
|
|
4637e282ce | ||
|
|
8ef84718b5 | ||
|
|
1e69081a2f | ||
|
|
c418e001b0 | ||
|
|
4ce7eda78b | ||
|
|
145e07363c | ||
|
|
54e245bcd4 | ||
|
|
d2485cd323 | ||
|
|
257593d231 | ||
|
|
c8e79c22ff | ||
|
|
29ab544269 | ||
|
|
78c7b53595 | ||
|
|
3a04099fc2 | ||
|
|
d6038dd072 | ||
|
|
4ebb7f2d35 | ||
|
|
14a8600552 | ||
|
|
b3ffb7e4d6 | ||
|
|
fc7f2be15c | ||
|
|
b7bdd438e4 | ||
|
|
65c9596a14 | ||
|
|
25b8896a25 | ||
|
|
f1f811219e | ||
|
|
57dc83f1af | ||
|
|
180c2bf230 | ||
|
|
ff89aa7a13 | ||
|
|
25cea8633c | ||
|
|
eeabc1922e | ||
|
|
d7e16d603f | ||
|
|
b1a08ab500 | ||
|
|
6219e9353d | ||
|
|
e3eb222d93 | ||
|
|
8f82fefbb7 | ||
|
|
8a303a1a29 | ||
|
|
49d4f8aa31 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ build/
|
||||
classes/
|
||||
temp/
|
||||
logs
|
||||
src/main/javacc/mods
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
plugins {
|
||||
kotlin("jvm").version("1.3.61")
|
||||
id("net.minecraftforge.gradle").version("3.0.194")
|
||||
kotlin("jvm").version("1.4.20")
|
||||
id("net.minecraftforge.gradle").version("4.1.12")
|
||||
id("org.spongepowered.mixin").version("0.7-SNAPSHOT")
|
||||
id("com.intershop.gradle.javacc").version("4.0.0")
|
||||
}
|
||||
apply(plugin = "org.spongepowered.mixin")
|
||||
|
||||
repositories {
|
||||
maven("http://files.minecraftforge.net/maven")
|
||||
maven("https://files.minecraftforge.net/maven")
|
||||
maven("https://repo.spongepowered.org/maven")
|
||||
maven("https://minecraft.curseforge.com/api/maven")
|
||||
maven("https://maven.shedaniel.me/")
|
||||
maven("https://www.cursemaven.com")
|
||||
maven("https://thedarkcolour.github.io/KotlinForForge/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"minecraft"("net.minecraftforge:forge:${properties["mcVersion"]}-${properties["forgeVersion"]}")
|
||||
|
||||
"api"(fg.deobf("curse.maven:clothconfig-348521:2938583"))
|
||||
|
||||
"implementation"(fg.deobf("curse.maven:biomesoplenty-220318:2988999"))
|
||||
"implementation"("kottle:Kottle:${properties["kottleVersion"]}")
|
||||
// "implementation"("org.spongepowered:mixin:0.8-SNAPSHOT")
|
||||
"implementation"("thedarkcolour:kotlinforforge:1.7.0")
|
||||
"api"(fg.deobf("curse.maven:clothconfig-348521:3311352"))
|
||||
}
|
||||
|
||||
configurations["annotationProcessor"].extendsFrom(configurations["implementation"])
|
||||
sourceSets {
|
||||
get("main").ext["refMap"] = "betterfoliage.refmap.json"
|
||||
get("main").java.srcDir("src/main/javacc/")
|
||||
}
|
||||
|
||||
minecraft {
|
||||
@@ -42,6 +38,17 @@ minecraft {
|
||||
}
|
||||
}
|
||||
|
||||
javacc {
|
||||
configs {
|
||||
create("blockconfig") {
|
||||
staticParam = "false"
|
||||
inputFile = file("src/main/javacc/BlockConfig.jj")
|
||||
outputDir = file("src/main/javacc/")
|
||||
packageName = "mods.betterfoliage.config.match.parser"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
@@ -51,7 +58,6 @@ kotlin {
|
||||
target.compilations.configureEach {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ org.gradle.daemon=false
|
||||
group = com.github.octarine-noise
|
||||
jarName = BetterFoliage-Forge
|
||||
|
||||
version = 2.6.3
|
||||
version = 2.7.0
|
||||
|
||||
mcVersion = 1.15.2
|
||||
forgeVersion = 31.2.44
|
||||
mappingsChannel = snapshot
|
||||
mappingsVersion = 20200514-1.15.1
|
||||
mcVersion = 1.16.5
|
||||
forgeVersion = 36.1.17
|
||||
mappingsChannel = official
|
||||
mappingsVersion = 1.16.5
|
||||
|
||||
kottleVersion = 1.4.0
|
||||
#kottleVersion = 1.4.0
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
53
gradlew
vendored
53
gradlew
vendored
@@ -1,5 +1,21 @@
|
||||
#!/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
|
||||
@@ -28,7 +44,7 @@ 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=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
@@ -66,6 +82,7 @@ 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
|
||||
@@ -109,10 +126,11 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# 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
|
||||
@@ -138,19 +156,19 @@ if $cygwin ; then
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
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" ;;
|
||||
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
|
||||
|
||||
@@ -159,14 +177,9 @@ save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
43
gradlew.bat
vendored
43
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
||||
@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
|
||||
@@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
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
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -35,7 +54,7 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
@@ -45,28 +64,14 @@ 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%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven("http://files.minecraftforge.net/maven")
|
||||
maven("https://files.minecraftforge.net/maven")
|
||||
maven("https://repo.spongepowered.org/maven")
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
*/
|
||||
@Mixin(Block.class)
|
||||
public class MixinBlock {
|
||||
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
|
||||
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldRenderFace(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
|
||||
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||
private static final String isOpaqueCube = "Lnet/minecraft/block/Block;isOpaqueCube(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)Z";
|
||||
|
||||
@@ -8,7 +8,7 @@ import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.BlockModelRenderer;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -19,12 +19,12 @@ import java.util.Random;
|
||||
@Mixin(BlockModelRenderer.class)
|
||||
public class MixinBlockModelRenderer {
|
||||
|
||||
private static final String renderModel = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModel(Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelFlat = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelFlat(Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelSmooth = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelSmooth(Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModel = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModel(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelFlat = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelFlat(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelSmooth = "Lnet/minecraft/client/renderer/BlockModelRenderer;renderModelSmooth(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
|
||||
@Redirect(method = renderModel, at = @At(value = "INVOKE", target = renderModelSmooth), remap = false)
|
||||
public boolean onRenderModelSmooth(BlockModelRenderer renderer, ILightReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
||||
public boolean onRenderModelSmooth(BlockModelRenderer renderer, IBlockDisplayReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
||||
if (model instanceof SpecialRenderModel)
|
||||
return RenderCtxVanilla.render(renderer, world, (SpecialRenderModel) model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData, true);
|
||||
else
|
||||
@@ -32,10 +32,10 @@ public class MixinBlockModelRenderer {
|
||||
}
|
||||
|
||||
@Redirect(method = renderModel, at = @At(value = "INVOKE", target = renderModelFlat), remap = false)
|
||||
public boolean onRenderModelFlat(BlockModelRenderer renderer, ILightReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
||||
public boolean onRenderModelFlat(BlockModelRenderer renderer, IBlockDisplayReader world, IBakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, IVertexBuilder buffer, boolean checkSides, Random random, long rand, int combinedOverlay, IModelData modelData) {
|
||||
if (model instanceof SpecialRenderModel)
|
||||
return RenderCtxVanilla.render(renderer, world, (SpecialRenderModel) model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData, false);
|
||||
else
|
||||
return renderer.renderModelSmooth(world, model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData);
|
||||
return renderer.renderModelFlat(world, model, state, pos, matrixStack, buffer, checkSides, random, rand, combinedOverlay, modelData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.Hooks;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import org.spongepowered.asm.mixin.Debug;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
/**
|
||||
@@ -14,14 +17,15 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
*
|
||||
* Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
|
||||
*/
|
||||
@Mixin(BlockState.class)
|
||||
@Mixin(AbstractBlock.AbstractBlockState.class)
|
||||
@SuppressWarnings({"deprecation"})
|
||||
public class MixinBlockState {
|
||||
private static final String callFrom = "Lnet/minecraft/block/BlockState;getAmbientOcclusionLightValue(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
private static final String callTo = "Lnet/minecraft/block/Block;getAmbientOcclusionLightValue(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
private static final String callFrom = "Lnet/minecraft/block/AbstractBlock$AbstractBlockState;getShadeBrightness(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
// why is the INVOKEVIRTUAL target class Block in the bytecode, not AbstractBlock?
|
||||
private static final String callTo = "Lnet/minecraft/block/Block;getShadeBrightness(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
|
||||
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
|
||||
float getAmbientOcclusionValue(Block block, BlockState state, IBlockReader reader, BlockPos pos) {
|
||||
return Hooks.getAmbientOcclusionLightValueOverride(block.getAmbientOcclusionLightValue(state, reader, pos), state);
|
||||
return Hooks.getAmbientOcclusionLightValueOverride(block.getShadeBrightness(state, reader, pos), state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.Hooks;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Mixin(ClientWorld.class)
|
||||
public class MixinClientWorld {
|
||||
|
||||
private static final String worldAnimateTick = "Lnet/minecraft/client/world/ClientWorld;animateTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)V";
|
||||
private static final String worldAnimateTick = "Lnet/minecraft/client/world/ClientWorld;doAnimateTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)V";
|
||||
private static final String blockAnimateTick = "Lnet/minecraft/block/Block;animateTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
||||
|
||||
private static final String worldNotify = "Lnet/minecraft/client/world/ClientWorld;notifyBlockUpdate(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||
private static final String rendererNotify = "Lnet/minecraft/client/renderer/WorldRenderer;notifyBlockUpdate(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||
private static final String worldNotify = "Lnet/minecraft/client/world/ClientWorld;sendBlockUpdated(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||
private static final String rendererNotify = "Lnet/minecraft/client/renderer/WorldRenderer;blockChanged(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||
|
||||
/**
|
||||
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
|
||||
*/
|
||||
@Redirect(method = worldAnimateTick, at = @At(value = "INVOKE", target = blockAnimateTick))
|
||||
void onAnimateTick(Block block, BlockState state, World world, BlockPos pos, Random random) {
|
||||
Hooks.onRandomDisplayTick(block, state, world, pos, random);
|
||||
block.animateTick(state, world, pos, random);
|
||||
@Inject(method = worldAnimateTick, at = @At(value = "INVOKE", target = blockAnimateTick))
|
||||
void onAnimateTick(int x, int y, int z, int range, Random random, boolean doBarrier, BlockPos.Mutable pos, CallbackInfo ci) {
|
||||
Hooks.onRandomDisplayTick((ClientWorld) (Object) this, pos, random);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +38,6 @@ public class MixinClientWorld {
|
||||
@Redirect(method = worldNotify, at = @At(value = "INVOKE", target = rendererNotify))
|
||||
void onClientBlockChanged(WorldRenderer renderer, IBlockReader world, BlockPos pos, BlockState oldState, BlockState newState, int flags) {
|
||||
Hooks.onClientBlockChanged((ClientWorld) world, pos, oldState, newState, flags);
|
||||
renderer.notifyBlockUpdate(world, pos, oldState, newState, flags);
|
||||
renderer.blockChanged(world, pos, oldState, newState, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxForge;
|
||||
import mods.betterfoliage.model.SpecialRenderModel;
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxForge;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import net.minecraftforge.client.model.pipeline.ForgeBlockModelRenderer;
|
||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
|
||||
@@ -19,14 +19,14 @@ import java.util.Random;
|
||||
@Mixin(ForgeBlockModelRenderer.class)
|
||||
public class MixinForgeBlockModelRenderer {
|
||||
|
||||
private static final String renderModelFlat = "renderModelFlat(Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelSmooth = "renderModelSmooth(Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String render = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;render(Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;Lnet/minecraft/world/ILightReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelFlat = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;renderModelFlat(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String renderModelSmooth = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;renderModelSmooth(Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;ZLjava/util/Random;JILnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
private static final String render = "Lnet/minecraftforge/client/model/pipeline/ForgeBlockModelRenderer;render(Lnet/minecraftforge/client/model/pipeline/VertexLighterFlat;Lnet/minecraft/world/IBlockDisplayReader;Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lcom/mojang/blaze3d/matrix/MatrixStack;ZLjava/util/Random;JLnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||
|
||||
@Redirect(method = {renderModelFlat, renderModelSmooth}, at = @At(value = "INVOKE", target = render), remap = false)
|
||||
public boolean render(
|
||||
VertexLighterFlat lighter,
|
||||
ILightReader world,
|
||||
IBlockDisplayReader world,
|
||||
IBakedModel model,
|
||||
BlockState state,
|
||||
BlockPos pos,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.BetterFoliage;
|
||||
import mods.betterfoliage.BetterFoliageMod;
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
||||
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent;
|
||||
@@ -20,13 +21,13 @@ abstract public class MixinModelBakery {
|
||||
|
||||
private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;I)V";
|
||||
private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/util/stream/Stream;Lnet/minecraft/profiler/IProfiler;I)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;";
|
||||
private static final String profilerSection = "Lnet/minecraft/profiler/IProfiler;endStartSection(Ljava/lang/String;)V";
|
||||
private static final String profilerSection = "Lnet/minecraft/profiler/IProfiler;popPush(Ljava/lang/String;)V";
|
||||
private static final String getBakedModel = "Lnet/minecraft/client/renderer/model/ModelBakery;getBakedModel(Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/model/IModelTransform;Ljava/util/function/Function;)Lnet/minecraft/client/renderer/model/IBakedModel;";
|
||||
private static final String bakeModel = "Lnet/minecraft/client/renderer/model/IUnbakedModel;bakeModel(Lnet/minecraft/client/renderer/model/ModelBakery;Ljava/util/function/Function;Lnet/minecraft/client/renderer/model/IModelTransform;Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/model/IBakedModel;";
|
||||
private static final String bakeModel = "Lnet/minecraft/client/renderer/model/IUnbakedModel;bake(Lnet/minecraft/client/renderer/model/ModelBakery;Ljava/util/function/Function;Lnet/minecraft/client/renderer/model/IModelTransform;Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/model/IBakedModel;";
|
||||
|
||||
@Inject(method = processLoading, at = @At(value = "INVOKE", target = profilerSection, ordinal = 4))
|
||||
void onBeforeTextures(IProfiler profiler, int maxMipmapLevel, CallbackInfo ci) {
|
||||
profiler.endStartSection("betterfoliage");
|
||||
profiler.popPush("betterfoliage");
|
||||
BetterFoliageMod.INSTANCE.getBus().post(new ModelDefinitionsLoadedEvent(ModelBakery.class.cast(this)));
|
||||
}
|
||||
|
||||
@@ -34,10 +35,10 @@ abstract public class MixinModelBakery {
|
||||
IBakedModel onBakeModel(
|
||||
IUnbakedModel unbaked,
|
||||
ModelBakery bakery,
|
||||
Function<Material, TextureAtlasSprite> spriteGetter,
|
||||
Function<RenderMaterial, TextureAtlasSprite> spriteGetter,
|
||||
IModelTransform transform,
|
||||
ResourceLocation locationIn
|
||||
) {
|
||||
return BakeWrapperManager.INSTANCE.onBake(unbaked, bakery, spriteGetter, transform, locationIn);
|
||||
return BetterFoliage.INSTANCE.getModelManager().onBake(unbaked, bakery, spriteGetter, transform, locationIn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
||||
import net.minecraft.client.renderer.chunk.VisGraph;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask")
|
||||
public class MixinOptifineChunkRendererDispatcher {
|
||||
|
||||
private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;";
|
||||
private static final String getBlockStateMcp = "Lnet/optifine/override/ChunkCacheOF;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
||||
private static final String getBlockStateSrg = "Lnet/optifine/override/ChunkCacheOF;func_180495_p(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
||||
|
||||
@Inject(method = compile, locals = LocalCapture.CAPTURE_FAILHARD, require = 1, at = {
|
||||
@At(value = "INVOKE", target = getBlockStateMcp),
|
||||
@At(value = "INVOKE", target = getBlockStateSrg),
|
||||
})
|
||||
void onStartBlockRender(
|
||||
float xIn, float yIn, float zIn,
|
||||
ChunkRenderDispatcher.CompiledChunk compiledChunkIn, RegionRenderCacheBuilder builderIn,
|
||||
CallbackInfoReturnable<Set> cir,
|
||||
int i, BlockPos blockpos, BlockPos blockpos1, VisGraph visgraph, Set set, MatrixStack matrixstack,
|
||||
@Coerce IBlockDisplayReader chunkrendercache, RenderType[] singleLayer,
|
||||
boolean shaders, boolean shadersMidBlock, Random random,
|
||||
BlockRendererDispatcher blockrendererdispatcher, Iterator var18,
|
||||
@Coerce BlockPos blockpos2
|
||||
) {
|
||||
RenderCtxBase.reset(chunkrendercache, blockrendererdispatcher, blockpos2, random);
|
||||
}
|
||||
|
||||
// @Inject(method = compile, locals = LocalCapture.PRINT, require = 1, at = {
|
||||
// @At(value = "INVOKE", target = getBlockStateMcp),
|
||||
// @At(value = "INVOKE", target = getBlockStateSrg),
|
||||
// })
|
||||
// void printLocals(
|
||||
// float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
||||
// ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
||||
// CallbackInfoReturnable<BlockState> ci) {
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
||||
import net.minecraft.client.renderer.chunk.VisGraph;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask")
|
||||
public class MixinVanillaChunkRendererDispatcher {
|
||||
|
||||
private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;";
|
||||
private static final String getBlockState = "Lnet/minecraft/client/renderer/chunk/ChunkRenderCache;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;";
|
||||
|
||||
@Inject(method = compile, at = @At(value = "INVOKE", target = getBlockState), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
void onStartBlockRender(
|
||||
float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
||||
ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
||||
CallbackInfoReturnable ci,
|
||||
int i, BlockPos blockpos, BlockPos blockpos1, VisGraph visgraph, Set set,
|
||||
ChunkRenderCache chunkrendercache, MatrixStack matrixstack,
|
||||
Random random,
|
||||
BlockRendererDispatcher blockrendererdispatcher, Iterator var15,
|
||||
BlockPos blockpos2) {
|
||||
RenderCtxBase.reset(chunkrendercache, blockrendererdispatcher, blockpos2, random);
|
||||
}
|
||||
|
||||
// @Inject(method = compile, at = @At(value = "INVOKE", target = getBlockState), locals = LocalCapture.PRINT)
|
||||
// void printLocals(
|
||||
// float p_228940_1_, float p_228940_2_, float p_228940_3_,
|
||||
// ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_,
|
||||
// CallbackInfoReturnable ci) {
|
||||
// }
|
||||
}
|
||||
101
src/main/javacc/BlockConfig.jj
Normal file
101
src/main/javacc/BlockConfig.jj
Normal file
@@ -0,0 +1,101 @@
|
||||
PARSER_BEGIN(BlockConfigParser)
|
||||
package mods.betterfoliage.config.match.parser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import mods.betterfoliage.config.match.*;
|
||||
|
||||
public class BlockConfigParser {
|
||||
public String configFile;
|
||||
|
||||
ConfigSource getSource(Token t) {
|
||||
return new ConfigSource(configFile, t.beginLine, t.beginColumn);
|
||||
}
|
||||
}
|
||||
|
||||
PARSER_END(BlockConfigParser)
|
||||
|
||||
// Whitespace definition
|
||||
SKIP : { " " | "\n" | "\t" | "\r" }
|
||||
|
||||
// Single-line comment
|
||||
SPECIAL_TOKEN : { <lineComment: "//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")> }
|
||||
|
||||
// Lexical state for string literal in quotes
|
||||
SPECIAL_TOKEN : { < quoteStart : "\"" > : withinQuotes }
|
||||
<withinQuotes> SPECIAL_TOKEN : { < quoteEnd : "\"" > : DEFAULT }
|
||||
<withinQuotes> TOKEN : { < stringLiteral : (["a"-"z"] | ["0"-"9"] | "/" | "." | "_" | "-" | ":" )* > }
|
||||
|
||||
// Symbol tokens
|
||||
TOKEN : {
|
||||
< parenStart : "(" > |
|
||||
< parenEnd : ")" > |
|
||||
< dot : "." > |
|
||||
< comma : "," > |
|
||||
< exclamation : "!" >
|
||||
}
|
||||
|
||||
List<Node.MatchAll> matchFile() : {
|
||||
Token t; Node n; List<Node.MatchAll> rules = new LinkedList<Node.MatchAll>();
|
||||
} {
|
||||
(
|
||||
t = "match"
|
||||
{ List<Node> nodes = new LinkedList<Node>(); }
|
||||
(n = match() { nodes.add(n); })*
|
||||
"end"
|
||||
{ rules.add(new Node.MatchAll(getSource(t), nodes)); }
|
||||
)*
|
||||
{ return rules; }
|
||||
}
|
||||
|
||||
Node match() : {
|
||||
Token t; Token t2; MatchMethod mm; List<Node.Value> values; Node.Value v; Node n;
|
||||
} {
|
||||
<exclamation> n = match() { return new Node.Negate(n); }
|
||||
|
|
||||
t = "block.class." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
||||
{ return new Node.MatchValueList(Node.MatchSource.BLOCK_CLASS, mm, getSource(t), values); }
|
||||
|
|
||||
t = "block.name." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
||||
{ return new Node.MatchValueList(Node.MatchSource.BLOCK_NAME, mm, getSource(t), values); }
|
||||
|
|
||||
t = "model." mm = matchMethod() <parenStart> values = matchValueList() <parenEnd>
|
||||
{ return new Node.MatchValueList(Node.MatchSource.MODEL_LOCATION, mm, getSource(t), values); }
|
||||
|
|
||||
t = "isParam" <parenStart> t2 = <stringLiteral> <comma> values = matchValueList() <parenEnd>
|
||||
{ return new Node.MatchParam(t2.image, values, getSource(t)); }
|
||||
|
|
||||
t = "setParam" <parenStart> t2 = <stringLiteral> <comma> v = matchValue() <parenEnd>
|
||||
{ return new Node.SetParam(t2.image, v, getSource(t)); }
|
||||
}
|
||||
|
||||
MatchMethod matchMethod() : {} {
|
||||
"matches" { return MatchMethod.EXACT_MATCH; } |
|
||||
"extends" { return MatchMethod.EXTENDS; } |
|
||||
"contains" { return MatchMethod.CONTAINS; }
|
||||
}
|
||||
|
||||
List<Node.Value> matchValueList() : {
|
||||
List<Node.Value> values = new LinkedList<Node.Value>();
|
||||
Node.Value v;
|
||||
} {
|
||||
v = matchValue() { values.add(v); }
|
||||
(<comma> v = matchValue() { values.add(v); } )*
|
||||
{ return values; }
|
||||
}
|
||||
|
||||
Node.Value matchValue() : {
|
||||
Token t;
|
||||
} {
|
||||
t = <stringLiteral>
|
||||
{ return new Node.Value.Literal(getSource(t), t.image); }
|
||||
|
|
||||
"classOf" <parenStart> t = <stringLiteral> <parenEnd>
|
||||
{ return new Node.Value.ClassOf(getSource(t), t.image); }
|
||||
|
|
||||
"model.texture" <parenStart> t = <stringLiteral> <parenEnd>
|
||||
{ return new Node.Value.Texture(getSource(t), t.image); }
|
||||
|
|
||||
"model.tint" <parenStart> t = <stringLiteral> <parenEnd>
|
||||
{ return new Node.Value.Tint(getSource(t), t.image); }
|
||||
}
|
||||
@@ -15,26 +15,24 @@ import mods.betterfoliage.render.block.vanilla.StandardLeafDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardLeafModel
|
||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadModel
|
||||
import mods.betterfoliage.render.block.vanilla.StandardRoundLogDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumModel
|
||||
import mods.betterfoliage.render.block.vanilla.StandardNetherrackDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardNetherrackModel
|
||||
import mods.betterfoliage.render.block.vanilla.StandardRoundLogDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardRoundLogModel
|
||||
import mods.betterfoliage.render.block.vanilla.StandardSandDiscovery
|
||||
import mods.betterfoliage.render.block.vanilla.StandardSandModel
|
||||
import mods.betterfoliage.render.lighting.AoSideHelper
|
||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||
import mods.betterfoliage.render.particle.LeafWindTracker
|
||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
||||
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent
|
||||
import mods.betterfoliage.resource.discovery.RuleBasedDiscovery
|
||||
import mods.betterfoliage.resource.generated.GeneratedTexturePack
|
||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||
import mods.betterfoliage.util.resourceManager
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.resources.IReloadableResourceManager
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
|
||||
/**
|
||||
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
||||
@@ -47,32 +45,41 @@ object BetterFoliage {
|
||||
/** List of recognized [BlockState]s */
|
||||
var blockTypes = BlockTypeCache()
|
||||
|
||||
val blockConfig = BlockConfig()
|
||||
|
||||
val standardModelSupport = RuleBasedDiscovery().apply {
|
||||
discoverers["cactus"] = StandardCactusDiscovery
|
||||
discoverers["dirt"] = StandardDirtDiscovery
|
||||
discoverers["grass"] = StandardGrassDiscovery
|
||||
discoverers["leaf"] = StandardLeafDiscovery
|
||||
discoverers["lilypad"] = StandardLilypadDiscovery
|
||||
discoverers["mycelium"] = StandardMyceliumDiscovery
|
||||
discoverers["netherrack"] = StandardNetherrackDiscovery
|
||||
discoverers["round-log"] = StandardRoundLogDiscovery
|
||||
discoverers["sand"] = StandardSandDiscovery
|
||||
}
|
||||
val modelManager = BakeWrapperManager().apply {
|
||||
discoverers.add(standardModelSupport)
|
||||
}
|
||||
|
||||
fun init() {
|
||||
// discoverers
|
||||
BetterFoliageMod.bus.register(BakeWrapperManager)
|
||||
BetterFoliageMod.bus.register(modelManager)
|
||||
BetterFoliageMod.bus.register(LeafParticleRegistry)
|
||||
(Minecraft.getInstance().resourceManager as IReloadableResourceManager).addReloadListener(LeafParticleRegistry)
|
||||
resourceManager.registerReloadListener(LeafParticleRegistry)
|
||||
|
||||
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
|
||||
|
||||
listOf(
|
||||
StandardLeafDiscovery,
|
||||
StandardGrassDiscovery,
|
||||
StandardDirtDiscovery,
|
||||
StandardMyceliumDiscovery,
|
||||
StandardSandDiscovery,
|
||||
StandardLilypadDiscovery,
|
||||
StandardCactusDiscovery,
|
||||
StandardNetherrackDiscovery,
|
||||
StandardRoundLogDiscovery
|
||||
StandardRoundLogDiscovery,
|
||||
).forEach {
|
||||
BakeWrapperManager.discoverers.add(it)
|
||||
}
|
||||
|
||||
// init singletons
|
||||
val singletons = listOf(
|
||||
AoSideHelper,
|
||||
BlockConfig,
|
||||
ChunkOverlayManager,
|
||||
LeafWindTracker
|
||||
)
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
package mods.betterfoliage
|
||||
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.Config
|
||||
import net.alexwells.kottle.FMLKotlinModLoadingContext
|
||||
import mods.betterfoliage.config.MainConfig
|
||||
import mods.betterfoliage.config.clothGuiRoot
|
||||
import mods.betterfoliage.config.forgeSpecRoot
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.fml.ExtensionPoint.CONFIGGUIFACTORY
|
||||
import net.minecraftforge.fml.ExtensionPoint.DISPLAYTEST
|
||||
import net.minecraftforge.fml.ModLoadingContext
|
||||
import net.minecraftforge.fml.common.Mod
|
||||
import net.minecraftforge.fml.config.ModConfig
|
||||
import org.apache.commons.lang3.tuple.Pair
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.simple.SimpleLogger
|
||||
import org.apache.logging.log4j.util.PropertiesUtil
|
||||
import thedarkcolour.kotlinforforge.forge.MOD_BUS
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.util.Properties
|
||||
import java.util.function.BiFunction
|
||||
import java.util.function.BiPredicate
|
||||
import java.util.function.Supplier
|
||||
|
||||
@Mod(BetterFoliageMod.MOD_ID)
|
||||
object BetterFoliageMod {
|
||||
const val MOD_ID = "betterfoliage"
|
||||
|
||||
val bus = FMLKotlinModLoadingContext.get().modEventBus
|
||||
val bus = MOD_BUS
|
||||
val config = MainConfig()
|
||||
|
||||
val detailLogStream = PrintStream(File("logs/betterfoliage.log").apply {
|
||||
parentFile.mkdirs()
|
||||
@@ -32,9 +43,34 @@ object BetterFoliageMod {
|
||||
)
|
||||
|
||||
init {
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build())
|
||||
Minecraft.getInstance().resourcePackList.addPackFinder(BetterFoliage.generatedPack.finder)
|
||||
bus.register(BlockConfig)
|
||||
val ctx = ModLoadingContext.get()
|
||||
|
||||
val configSpec = config.forgeSpecRoot()
|
||||
ctx.registerConfig(ModConfig.Type.CLIENT, configSpec)
|
||||
|
||||
// Add config GUI extension if Cloth Config is available
|
||||
val clothLoaded = tryDefault(false) { Class.forName("me.shedaniel.clothconfig2.forge.api.ConfigBuilder"); true }
|
||||
if (clothLoaded) {
|
||||
logger(this).log(Level.INFO, "Cloth Config found, registering GUI")
|
||||
ctx.registerExtensionPoint(CONFIGGUIFACTORY) { BiFunction<Minecraft, Screen, Screen> { client, parent ->
|
||||
config.clothGuiRoot(
|
||||
parentScreen = parent,
|
||||
prefix = listOf(MOD_ID),
|
||||
background = ResourceLocation("minecraft:textures/block/spruce_log.png"),
|
||||
saveAction = { configSpec.save() }
|
||||
)
|
||||
} }
|
||||
}
|
||||
|
||||
// Accept-all version tester (we are client-only)
|
||||
ctx.registerExtensionPoint(DISPLAYTEST) {
|
||||
Pair.of(
|
||||
Supplier { "Honk if you see this!" },
|
||||
BiPredicate<String, Boolean> { _, _ -> true }
|
||||
)
|
||||
}
|
||||
|
||||
Minecraft.getInstance().resourcePackRepository.addPackFinder(BetterFoliage.generatedPack.finder)
|
||||
BetterFoliage.init()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package mods.octarinecore
|
||||
package mods.betterfoliage
|
||||
|
||||
import mods.betterfoliage.util.ClassRef
|
||||
import mods.betterfoliage.util.ClassRef.Companion.float
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.BlockModelRenderer
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||
import net.minecraft.client.renderer.model.BakedQuad
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
@@ -17,11 +18,15 @@ import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraft.world.IBlockReader
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraftforge.client.model.pipeline.BlockInfo
|
||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat
|
||||
import java.util.*
|
||||
import net.minecraftforge.registries.IRegistryDelegate
|
||||
import java.util.Random
|
||||
import java.util.function.Predicate
|
||||
|
||||
typealias Sprite = TextureAtlasSprite
|
||||
|
||||
// Java
|
||||
val String = ClassRef<String>("java.lang.String")
|
||||
@@ -32,7 +37,7 @@ fun <K, V> mapRefMutable() = ClassRef<MutableMap<K, V>>("java.util.Map")
|
||||
|
||||
// Minecraft
|
||||
val IBlockReader = ClassRef<IBlockReader>("net.minecraft.world.IBlockReader")
|
||||
val ILightReader = ClassRef<ILightReader>("net.minecraft.world.ILightReader")
|
||||
val ILightReader = ClassRef<IBlockDisplayReader>("net.minecraft.world.IBlockDisplayReader")
|
||||
val BlockState = ClassRef<BlockState>("net.minecraft.block.BlockState")
|
||||
val BlockPos = ClassRef<BlockPos>("net.minecraft.util.math.BlockPos")
|
||||
val Block = ClassRef<Block>("net.minecraft.block.Block")
|
||||
@@ -59,6 +64,10 @@ object ModelBakery : ClassRef<ModelBakery>("net.minecraft.client.renderer.model.
|
||||
val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>())
|
||||
}
|
||||
|
||||
object RenderTypeLookup : ClassRef<RenderTypeLookup>("net.minecraft.client.renderer.RenderTypeLookup") {
|
||||
val blockRenderChecks = FieldRef(this, "blockRenderChecks", mapRefMutable<IRegistryDelegate<Block>, Predicate<RenderType>>())
|
||||
}
|
||||
|
||||
// Optifine
|
||||
val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer")
|
||||
val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM")
|
||||
|
||||
@@ -20,8 +20,8 @@ import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.shapes.VoxelShape
|
||||
import net.minecraft.util.math.shapes.VoxelShapes
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraft.world.IBlockReader
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.World
|
||||
import java.util.Random
|
||||
|
||||
@@ -35,11 +35,12 @@ fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: Bloc
|
||||
ChunkOverlayManager.onBlockChange(worldClient, pos)
|
||||
}
|
||||
|
||||
fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: BlockPos, random: Random) {
|
||||
fun onRandomDisplayTick(world: ClientWorld, pos: BlockPos, random: Random) {
|
||||
val state = world.getBlockState(pos)
|
||||
if (Config.enabled &&
|
||||
Config.risingSoul.enabled &&
|
||||
state.block == Blocks.SOUL_SAND &&
|
||||
world.isAirBlock(pos.offset(UP)) &&
|
||||
world.getBlockState(pos.relative(UP)).isAir &&
|
||||
Math.random() < Config.risingSoul.chance) {
|
||||
RisingSoulParticle(world, pos).addIfValid()
|
||||
}
|
||||
@@ -47,7 +48,7 @@ fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: Bloc
|
||||
if (Config.enabled &&
|
||||
Config.fallingLeaves.enabled &&
|
||||
random.nextDouble() < Config.fallingLeaves.chance &&
|
||||
world.isAirBlock(pos.offset(DOWN))
|
||||
world.getBlockState(pos.relative(DOWN)).isAir
|
||||
) {
|
||||
(getActualRenderModel(world, pos, state, random) as? LeafBlockModel)?.let { leafModel ->
|
||||
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
|
||||
@@ -63,13 +64,13 @@ fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos
|
||||
}
|
||||
|
||||
fun shouldForceSideRenderOF(state: BlockState, world: IBlockReader, pos: BlockPos, face: Direction) =
|
||||
world.getBlockState(pos.offset(face)).let { neighbor -> BetterFoliage.blockTypes.hasTyped<RoundLogKey>(neighbor) }
|
||||
world.getBlockState(pos.relative(face)).let { neighbor -> BetterFoliage.blockTypes.hasTyped<RoundLogKey>(neighbor) }
|
||||
|
||||
fun getActualRenderModel(world: ILightReader, pos: BlockPos, state: BlockState, random: Random): SpecialRenderModel? {
|
||||
val model = Minecraft.getInstance().blockRendererDispatcher.blockModelShapes.getModel(state) as? SpecialRenderModel
|
||||
fun getActualRenderModel(world: IBlockDisplayReader, pos: BlockPos, state: BlockState, random: Random): SpecialRenderModel? {
|
||||
val model = Minecraft.getInstance().blockRenderer.blockModelShaper.getBlockModel(state) as? SpecialRenderModel
|
||||
?: return null
|
||||
if (model is WeightedModelWrapper) {
|
||||
random.setSeed(state.getPositionRandom(pos))
|
||||
random.setSeed(state.getSeed(pos))
|
||||
return model.getModel(random).model
|
||||
}
|
||||
return model
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mods.betterfoliage.chunk
|
||||
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.semiRandom
|
||||
@@ -10,7 +9,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraft.world.IWorldReader
|
||||
import net.minecraft.world.biome.Biome
|
||||
import net.minecraft.world.level.ColorResolver
|
||||
@@ -20,9 +19,11 @@ import net.minecraft.world.level.ColorResolver
|
||||
* block-relative coordinates.
|
||||
*/
|
||||
interface BlockCtx {
|
||||
val world: ILightReader
|
||||
val world: IBlockDisplayReader
|
||||
val pos: BlockPos
|
||||
|
||||
val seed: Long get() = state.getSeed(pos)
|
||||
|
||||
fun offset(dir: Direction) = offset(dir.offset)
|
||||
fun offset(offset: Int3): BlockCtx
|
||||
|
||||
@@ -35,13 +36,13 @@ interface BlockCtx {
|
||||
|
||||
val biome: Biome? get() =
|
||||
(world as? IWorldReader)?.getBiome(pos) ?:
|
||||
(world as? ChunkRenderCache)?.world?.getBiome(pos)
|
||||
(world as? ChunkRenderCache)?.level?.getBiome(pos)
|
||||
|
||||
val isNormalCube: Boolean get() = state.isNormalCube(world, pos)
|
||||
val isFullBlock: Boolean get() = state.isCollisionShapeFullBlock(world, pos)
|
||||
|
||||
fun isNeighborSolid(dir: Direction) = offset(dir).let { it.state.isSolidSide(it.world, it.pos, dir.opposite) }
|
||||
fun isNeighborSturdy(dir: Direction) = offset(dir).let { it.state.isFaceSturdy(it.world, it.pos, dir.opposite) }
|
||||
|
||||
fun shouldSideBeRendered(side: Direction) = Block.shouldSideBeRendered(state, world, pos, side)
|
||||
fun shouldSideBeRendered(side: Direction) = Block.shouldRenderFace(state, world, pos, side)
|
||||
|
||||
/** Get a semi-random value based on the block coordinate and the given seed. */
|
||||
fun semiRandom(seed: Int) = pos.semiRandom(seed)
|
||||
@@ -49,11 +50,11 @@ interface BlockCtx {
|
||||
/** Get an array of semi-random values based on the block coordinate. */
|
||||
fun semiRandomArray(num: Int): Array<Int> = Array(num) { semiRandom(it) }
|
||||
|
||||
fun color(resolver: ColorResolver) = world.getBlockColor(pos, resolver)
|
||||
fun color(resolver: ColorResolver) = world.getBlockTint(pos, resolver)
|
||||
}
|
||||
|
||||
class BasicBlockCtx(
|
||||
override val world: ILightReader,
|
||||
override val world: IBlockDisplayReader,
|
||||
override val pos: BlockPos
|
||||
) : BlockCtx {
|
||||
override val state: BlockState = world.getBlockState(pos)
|
||||
|
||||
@@ -1,124 +1,95 @@
|
||||
package mods.betterfoliage.chunk
|
||||
|
||||
import mods.octarinecore.ChunkCacheOF
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.isInstance
|
||||
import mods.betterfoliage.ChunkCacheOF
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.DimensionType
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraft.world.IWorldReader
|
||||
import net.minecraft.world.dimension.DimensionType
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import net.minecraftforge.event.world.ChunkEvent
|
||||
import net.minecraftforge.event.world.WorldEvent
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import java.util.*
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.MutableMap
|
||||
import kotlin.collections.associateWith
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
|
||||
val ILightReader.dimType: DimensionType get() = when {
|
||||
this is IWorldReader -> dimension.type
|
||||
this is ChunkRenderCache -> world.dimension.type
|
||||
this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].world.dimension.type
|
||||
val IBlockDisplayReader.dimType: DimensionType get() = when {
|
||||
this is IWorldReader -> dimensionType()
|
||||
this is ChunkRenderCache -> level.dimensionType()
|
||||
this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].level.dimensionType()
|
||||
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
|
||||
*/
|
||||
interface ChunkOverlayLayer<T> {
|
||||
fun calculate(ctx: BlockCtx): T
|
||||
fun onBlockUpdate(world: ILightReader, pos: BlockPos)
|
||||
abstract class ChunkOverlayLayer<T> {
|
||||
val dimData = IdentityHashMap<DimensionType, SparseChunkedMap<T>>()
|
||||
abstract fun calculate(ctx: BlockCtx): T
|
||||
abstract fun onBlockUpdate(world: IBlockDisplayReader, pos: BlockPos)
|
||||
|
||||
operator fun get(ctx: BlockCtx): T {
|
||||
return dimData
|
||||
.getOrPut(ctx.world.dimType) { SparseChunkedMap() }
|
||||
.getOrPut(ctx.pos) { calculate(ctx) }
|
||||
}
|
||||
|
||||
fun remove(world: IBlockDisplayReader, pos: BlockPos) {
|
||||
dimData[world.dimType]?.remove(pos)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
|
||||
* Event forwarder for multiple layers of chunk overlay data.
|
||||
*/
|
||||
object ChunkOverlayManager {
|
||||
|
||||
var tempCounter = 0
|
||||
|
||||
init {
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
}
|
||||
|
||||
val chunkData = IdentityHashMap<DimensionType, MutableMap<ChunkPos, ChunkOverlayData>>()
|
||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
||||
val layers = mutableListOf<ChunkOverlayLayer<*>>()
|
||||
|
||||
/**
|
||||
* Get the overlay data for a given layer and position
|
||||
*
|
||||
* @param layer Overlay layer to query
|
||||
* @param reader World to use if calculation of overlay value is necessary
|
||||
* @param pos Block position
|
||||
*/
|
||||
fun <T> get(layer: ChunkOverlayLayer<T>, ctx: BlockCtx): T? {
|
||||
val data = chunkData[ctx.world.dimType]?.get(ChunkPos(ctx.pos)) ?: return null
|
||||
data.get(layer, ctx.pos).let { value ->
|
||||
if (value !== ChunkOverlayData.UNCALCULATED) return value
|
||||
val newValue = layer.calculate(ctx)
|
||||
data.set(layer, ctx.pos, newValue)
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the overlay data for a given layer and position
|
||||
*
|
||||
* @param layer Overlay layer to clear
|
||||
* @param pos Block position
|
||||
*/
|
||||
fun <T> clear(dimension: DimensionType, layer: ChunkOverlayLayer<T>, pos: BlockPos) {
|
||||
chunkData[dimension]?.get(ChunkPos(pos))?.clear(layer, pos)
|
||||
}
|
||||
|
||||
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
|
||||
if (chunkData[world.dimType]?.containsKey(ChunkPos(pos)) == true) {
|
||||
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world ->
|
||||
chunkData[world.dimType] = mutableMapOf()
|
||||
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
||||
chunkData.remove(world.dimType)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleLoadChunk(event: ChunkEvent.Load) = (event.world as? ClientWorld)?.let { world ->
|
||||
chunkData[world.dimType]?.let { chunks ->
|
||||
// check for existence first because Optifine fires a TON of these
|
||||
if (event.chunk.pos !in chunks.keys) chunks[event.chunk.pos] = ChunkOverlayData(layers)
|
||||
}
|
||||
layers.forEach { layer -> layer.dimData.remove(world.dimType) }
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleUnloadChunk(event: ChunkEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
||||
chunkData[world.dimType]?.remove(event.chunk.pos)
|
||||
layers.forEach { layer -> layer.dimData[world.dimType]?.removeChunk(event.chunk.pos) }
|
||||
}
|
||||
}
|
||||
|
||||
class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
|
||||
val BlockPos.isValid: Boolean get() = y in validYRange
|
||||
val rawData = layers.associateWith { emptyOverlay() }
|
||||
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T? else null
|
||||
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data) else null
|
||||
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED) else null
|
||||
interface DoubleMap<K1, K2, V> {
|
||||
val map1: MutableMap<K1, MutableMap<K2, V>>
|
||||
fun createMap2(): MutableMap<K2, V>
|
||||
|
||||
companion object {
|
||||
val UNCALCULATED = object {}
|
||||
fun emptyOverlay() = Array(16) { Array(16) { Array<Any?>(256) { UNCALCULATED }}}
|
||||
val validYRange = 0 until 256
|
||||
fun remove(key1: K1) {
|
||||
map1.remove(key1)
|
||||
}
|
||||
fun remove(key1: K1, key2: K2) {
|
||||
map1[key1]?.remove(key2)
|
||||
}
|
||||
fun contains(key1: K1) = map1.contains(key1)
|
||||
|
||||
fun getOrSet(key1: K1, key2: K2, factory: () -> V) =
|
||||
(map1[key1] ?: createMap2().apply { map1[key1] = this }).let { subMap ->
|
||||
subMap[key2] ?: factory().apply { subMap[key2] = this }
|
||||
}
|
||||
}
|
||||
|
||||
class SparseChunkedMap<V> {
|
||||
val map = object : DoubleMap<ChunkPos, BlockPos, V> {
|
||||
override val map1 = mutableMapOf<ChunkPos, MutableMap<BlockPos, V>>()
|
||||
override fun createMap2() = mutableMapOf<BlockPos, V>()
|
||||
}
|
||||
|
||||
fun getOrPut(pos: BlockPos, factory: () -> V) = map.getOrSet(ChunkPos(pos), pos, factory)
|
||||
fun remove(pos: BlockPos) = map.remove(ChunkPos(pos), pos)
|
||||
fun removeChunk(pos: ChunkPos) = map.map1.remove(pos)
|
||||
}
|
||||
40
src/main/kotlin/mods/betterfoliage/config/BlockConfig.kt
Normal file
40
src/main/kotlin/mods/betterfoliage/config/BlockConfig.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.config.match.Node
|
||||
import mods.betterfoliage.config.match.parser.BlockConfigParser
|
||||
import mods.betterfoliage.config.match.parser.ParseException
|
||||
import mods.betterfoliage.config.match.parser.TokenMgrError
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.minecraft.resources.IResourceManager
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Level.ERROR
|
||||
|
||||
class BlockConfig : HasLogger() {
|
||||
lateinit var rules: List<Node.MatchAll>
|
||||
|
||||
fun readConfig(manager: IResourceManager) {
|
||||
val configs = manager.listResources("config/betterfoliage") { it.endsWith(".rules") }
|
||||
rules = configs.flatMap { configLocation ->
|
||||
val resource = manager.getResource(configLocation)
|
||||
val parser = BlockConfigParser(resource.inputStream)
|
||||
.apply { configFile = configLocation.stripStart("config/betterfoliage/").toString() }
|
||||
try {
|
||||
parser.matchFile()
|
||||
} catch (e: ParseException) {
|
||||
parseError(e, configLocation)
|
||||
} catch (e: TokenMgrError) {
|
||||
parseError(e, configLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseError(e: Throwable, location: ResourceLocation): List<Node.MatchAll> {
|
||||
"Error parsing block config $location: ${e.message}".let {
|
||||
logger.log(ERROR, it)
|
||||
detailLogger.log(ERROR, it)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureListConfiguration
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.config.ModConfig
|
||||
import java.util.Random
|
||||
|
||||
private fun featureEnable() = boolean(true).lang("enabled")
|
||||
|
||||
abstract class PopulationConfigCategory() : ConfigCategory() {
|
||||
abstract val enabled: Boolean
|
||||
abstract val population: Int
|
||||
|
||||
fun enabled(random: Random) = random.nextInt(64) < population && enabled
|
||||
}
|
||||
|
||||
// Config singleton
|
||||
object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_ID) {
|
||||
|
||||
val enabled by boolean(true)
|
||||
val nVidia by boolean(false)
|
||||
|
||||
object leaves : ConfigCategory() {
|
||||
val enabled by featureEnable()
|
||||
val snowEnabled by boolean(true)
|
||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
|
||||
val size by double(min=0.75, max=2.5, default=1.4).lang("size")
|
||||
val dense by boolean(false)
|
||||
val hideInternal by boolean(true)
|
||||
val saturationThreshold by double(default=0.1)
|
||||
}
|
||||
|
||||
object shortGrass : PopulationConfigCategory(){
|
||||
override val enabled by featureEnable()
|
||||
val myceliumEnabled by boolean(true)
|
||||
val snowEnabled by boolean(true)
|
||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||
val heightMin by double(min=0.1, max=2.5, default=0.6).lang("heightMin")
|
||||
val heightMax by double(min=0.1, max=2.5, default=0.8).lang("heightMax")
|
||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||
override val population by int(max=64, default=64).lang("population")
|
||||
val useGenerated by boolean(false)
|
||||
val shaderWind by boolean(true).lang("shaderWind")
|
||||
val saturationThreshold by double(default=0.1)
|
||||
}
|
||||
|
||||
object connectedGrass : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val snowEnabled by boolean(false)
|
||||
}
|
||||
|
||||
object roundLogs : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val radiusSmall by double(max=0.5, default=0.25)
|
||||
val radiusLarge by double(max=0.5, default=0.44)
|
||||
val dimming by double(default = 0.7)
|
||||
val connectSolids by boolean(false)
|
||||
val lenientConnect by boolean(true)
|
||||
val connectPerpendicular by boolean(true)
|
||||
val connectGrass by boolean(true)
|
||||
val defaultY by boolean(false)
|
||||
val zProtection by double(min = 0.9, default = 0.99)
|
||||
}
|
||||
|
||||
object cactus : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val size by double(min=1.0, max=2.0, default=1.3).lang("size")
|
||||
val sizeVariation by double(max=0.5, default=0.1)
|
||||
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
|
||||
}
|
||||
|
||||
object lilypad : PopulationConfigCategory(){
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
||||
override val population by int(max=64, default=16, min=0)
|
||||
val shaderWind by boolean(true).lang("shaderWind")
|
||||
}
|
||||
|
||||
object reed : PopulationConfigCategory(){
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||
val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin")
|
||||
val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax")
|
||||
override val population by int(max=64, default=32).lang("population")
|
||||
val minBiomeTemp by double(default=0.4)
|
||||
val minBiomeRainfall by double(default=0.4)
|
||||
// val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) }
|
||||
val shaderWind by boolean(true).lang("shaderWind")
|
||||
}
|
||||
|
||||
object algae : PopulationConfigCategory(){
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||
val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin")
|
||||
val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax")
|
||||
override val population by int(max=64, default=48).lang("population")
|
||||
// val biomes by biomeList { it.filterClass("river", "ocean") }
|
||||
val shaderWind by boolean(true).lang("shaderWind")
|
||||
}
|
||||
|
||||
object coral : PopulationConfigCategory(){
|
||||
override val enabled by featureEnable()
|
||||
val shallowWater by boolean(false)
|
||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
|
||||
val size by double(min=0.5, max=1.5, default=0.7).lang("size")
|
||||
val crustSize by double(min=0.5, max=1.5, default=1.4)
|
||||
val chance by int(max=64, default=32)
|
||||
override val population by int(max=64, default=48).lang("population")
|
||||
// val biomes by biomeList { it.filterClass("river", "ocean", "beach") }
|
||||
}
|
||||
|
||||
object netherrack : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||
val heightMin by double(min=0.1, max=1.5, default=0.2).lang("heightMin")
|
||||
val heightMax by double(min=0.1, max=1.5, default=0.5).lang("heightMax")
|
||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||
}
|
||||
|
||||
object fallingLeaves : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val speed by double(min=0.01, max=0.15, default=0.05)
|
||||
val windStrength by double(min=0.1, max=2.0, default=0.5)
|
||||
val stormStrength by double(min=0.1, max=2.0, default=0.8)
|
||||
val size by double(min=0.25, max=1.5, default=0.75).lang("size")
|
||||
val chance by double(min=0.001, max=1.0, default=0.02)
|
||||
val perturb by double(min=0.01, max=1.0, default=0.25)
|
||||
val lifetime by double(min=1.0, max=15.0, default=5.0)
|
||||
val opacityHack by boolean(true)
|
||||
}
|
||||
|
||||
object risingSoul : ConfigCategory(){
|
||||
val enabled by featureEnable()
|
||||
val chance by double(min=0.001, max=1.0, default=0.02)
|
||||
val perturb by double(min=0.01, max=0.25, default=0.05)
|
||||
val headSize by double(min=0.25, max=1.5, default=1.0)
|
||||
val trailSize by double(min=0.25, max=1.5, default=0.75)
|
||||
val opacity by double(min=0.05, max=1.0, default=0.5)
|
||||
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
|
||||
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
|
||||
val lifetime by double(min=1.0, max=15.0, default=4.0)
|
||||
val trailLength by int(min=2, max=128, default=48)
|
||||
val trailDensity by int(min=1, max=16, default=3)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BlockConfig {
|
||||
private val list = mutableListOf<Any>()
|
||||
|
||||
val leafBlocks = blocks("leaves_blocks_default.cfg")
|
||||
val leafModels = models("leaves_models_default.cfg")
|
||||
val grassBlocks = blocks("grass_blocks_default.cfg")
|
||||
val grassModels = models("grass_models_default.cfg")
|
||||
val mycelium = blocks("mycelium_blocks_default.cfg")
|
||||
// val dirt = blocks("dirt_default.cfg")
|
||||
val crops = blocks("crop_default.cfg")
|
||||
val logBlocks = blocks("log_blocks_default.cfg")
|
||||
val logModels = models("log_models_default.cfg")
|
||||
val lilypad = blocks("lilypad_default.cfg")
|
||||
|
||||
init { BetterFoliageMod.bus.register(this) }
|
||||
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
private fun models(cfgName: String) = ModelTextureListConfiguration(ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
|
||||
@SubscribeEvent
|
||||
fun onConfig(event: ModConfig.ModConfigEvent) {
|
||||
list.forEach { when(it) {
|
||||
is ConfigurableBlockMatcher -> it.readDefaults()
|
||||
is ModelTextureListConfiguration -> it.readDefaults()
|
||||
} }
|
||||
}
|
||||
}
|
||||
170
src/main/kotlin/mods/betterfoliage/config/Delegate.kt
Normal file
170
src/main/kotlin/mods/betterfoliage/config/Delegate.kt
Normal file
@@ -0,0 +1,170 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import me.shedaniel.clothconfig2.forge.api.AbstractConfigListEntry
|
||||
import me.shedaniel.clothconfig2.forge.api.ConfigBuilder
|
||||
import me.shedaniel.clothconfig2.forge.api.ConfigEntryBuilder
|
||||
import me.shedaniel.clothconfig2.forge.gui.entries.SubCategoryListEntry
|
||||
import mods.betterfoliage.util.asText
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.resources.I18n
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.common.ForgeConfigSpec
|
||||
import java.util.Optional
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
const val MAX_LINE_LEN = 30
|
||||
|
||||
fun DelegatingConfigGroup.forgeSpecRoot() =
|
||||
ForgeConfigSpec.Builder()
|
||||
.also { createForgeNode(it) }
|
||||
.build()
|
||||
|
||||
fun DelegatingConfigGroup.clothGuiRoot(
|
||||
parentScreen: Screen,
|
||||
prefix: List<String>,
|
||||
background: ResourceLocation,
|
||||
saveAction: ()->Unit
|
||||
) = ConfigBuilder.create()
|
||||
.setParentScreen(parentScreen)
|
||||
.setTitle(I18n.get((prefix + "title").joinToString(".")).asText())
|
||||
.setDefaultBackgroundTexture(background)
|
||||
.setSavingRunnable(saveAction)
|
||||
.also { builder ->
|
||||
createClothNode(prefix).value.forEach { rootCategory ->
|
||||
builder.getOrCreateCategory("main".asText()).addEntry(rootCategory)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
sealed class DelegatingConfigNode {
|
||||
abstract fun createClothNode(path: List<String>): AbstractConfigListEntry<*>
|
||||
}
|
||||
|
||||
abstract class DelegatingConfigValue<T> : DelegatingConfigNode(), ReadOnlyProperty<DelegatingConfigGroup, T> {
|
||||
lateinit var forgeValue: ForgeConfigSpec.ConfigValue<T>
|
||||
abstract fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String)
|
||||
}
|
||||
|
||||
open class DelegatingConfigGroup : DelegatingConfigNode() {
|
||||
val children = mutableMapOf<String, DelegatingConfigNode>()
|
||||
|
||||
fun createForgeNode(builder: ForgeConfigSpec.Builder) {
|
||||
children.forEach { (name, node) ->
|
||||
when(node) {
|
||||
is DelegatingConfigGroup -> {
|
||||
builder.push(name)
|
||||
node.createForgeNode(builder)
|
||||
builder.pop()
|
||||
}
|
||||
is DelegatingConfigValue<*> -> node.createForgeNode(builder, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createClothNode(path: List<String>): SubCategoryListEntry {
|
||||
val builder = ConfigEntryBuilder.create()
|
||||
.startSubCategory(path.joinToString(".").translate())
|
||||
.setTooltip(*path.joinToString(".").translateTooltip())
|
||||
.setExpanded(false)
|
||||
children.forEach { (name, node) -> builder.add(node.createClothNode(path + name)) }
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
interface DelegatingConfigGroupFactory<T> {
|
||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T>
|
||||
}
|
||||
|
||||
fun <T: DelegatingConfigGroup> subNode(factory: ()->T) = object : DelegatingConfigGroupFactory<T> {
|
||||
override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
||||
val child = factory()
|
||||
parent.children[property.name] = child
|
||||
return ReadOnlyProperty { _, _ -> child }
|
||||
}
|
||||
}
|
||||
|
||||
interface DelegatingConfigValueFactory<T> {
|
||||
fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String): ForgeConfigSpec.ConfigValue<T>
|
||||
fun createClothNode(prop: CachingConfigProperty<T>, path: List<String>): AbstractConfigListEntry<T>
|
||||
|
||||
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
|
||||
return object : CachingConfigProperty<T>(parent, property) {
|
||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) {
|
||||
forgeValue = this@DelegatingConfigValueFactory.createForgeNode(builder, name)
|
||||
}
|
||||
|
||||
override fun createClothNode(path: List<String>): AbstractConfigListEntry<*> = createClothNode(this, path)
|
||||
|
||||
}.apply { parent.children[property.name] = this }
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CachingConfigProperty<T>(parent: DelegatingConfigGroup, property: KProperty<*>) : DelegatingConfigValue<T>() {
|
||||
var value: T? = null
|
||||
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) =
|
||||
value ?: forgeValue.get().apply { value = this }
|
||||
}
|
||||
|
||||
fun String.translate() = I18n.get(this).asText()
|
||||
fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) =
|
||||
I18n.get("$this.tooltip").splitToSequence(" ").fold(mutableListOf("")) { tooltips, word ->
|
||||
if (tooltips.last().length + word.length < lineLength) {
|
||||
tooltips[tooltips.lastIndex] += "$word "
|
||||
} else {
|
||||
tooltips.add("$word ")
|
||||
}
|
||||
tooltips
|
||||
}.map { it.trim().asText() }.toTypedArray()
|
||||
|
||||
fun boolean(
|
||||
default: Boolean,
|
||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
||||
valueOverride: (Boolean)->Boolean = { it }
|
||||
) = object : DelegatingConfigValueFactory<Boolean> {
|
||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
||||
builder.define(name, default)
|
||||
|
||||
override fun createClothNode(prop: CachingConfigProperty<Boolean>, path: List<String>) = ConfigEntryBuilder.create()
|
||||
.startBooleanToggle(langKey(path).translate(), prop.forgeValue.get())
|
||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
||||
.build()
|
||||
}
|
||||
|
||||
fun integer(
|
||||
default: Int = 0, min: Int = 0, max: Int,
|
||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
||||
valueOverride: (Int)->Int = { it }
|
||||
) = object : DelegatingConfigValueFactory<Int> {
|
||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
||||
builder.defineInRange(name, default, min, max)
|
||||
|
||||
override fun createClothNode(prop: CachingConfigProperty<Int>, path: List<String>) = ConfigEntryBuilder.create()
|
||||
.startIntField(langKey(path).translate(), prop.forgeValue.get())
|
||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.setMin(min).setMax(max)
|
||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
||||
.build()
|
||||
}
|
||||
|
||||
fun double(
|
||||
default: Double = 0.0, min: Double = 0.0, max: Double = 1.0,
|
||||
langKey: (List<String>)->String = { it.joinToString(".") },
|
||||
valueOverride: (Double)->Double = { it }
|
||||
) = object : DelegatingConfigValueFactory<Double> {
|
||||
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
|
||||
builder.defineInRange(name, default, min, max)
|
||||
|
||||
override fun createClothNode(prop: CachingConfigProperty<Double>, path: List<String>) = ConfigEntryBuilder.create()
|
||||
.startDoubleField(langKey(path).translate(), prop.forgeValue.get())
|
||||
.setTooltip(langKey(path).let { if (I18n.exists("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.setMin(min).setMax(max)
|
||||
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
|
||||
.build()
|
||||
}
|
||||
|
||||
val recurring = { path: List<String> -> "${path.first()}.${path.last()}" }
|
||||
fun fakeCategory(name: String) = { names: List<String> ->
|
||||
(listOf(names.first(), name) + names.drop(1)).joinToString(".")
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
@file:JvmName("DelegatingConfigKt")
|
||||
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.util.reflectDelegates
|
||||
import mods.betterfoliage.util.reflectNestedObjects
|
||||
import net.minecraftforge.common.ForgeConfigSpec
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
open class DelegatingConfig(val modId: String, val langPrefix: String) {
|
||||
fun build() = ForgeConfigSpec.Builder().apply { ConfigBuildContext(langPrefix, emptyList(), this).addCategory(this@DelegatingConfig) }.build()
|
||||
}
|
||||
|
||||
class ConfigBuildContext(val langPrefix: String, val path: List<String>, val builder: ForgeConfigSpec.Builder) {
|
||||
|
||||
fun addCategory(configObj: Any) {
|
||||
configObj.reflectNestedObjects.forEach { (name, category) ->
|
||||
builder.push(name)
|
||||
descend(name).addCategory(category)
|
||||
builder.pop()
|
||||
}
|
||||
configObj.reflectDelegates(ConfigDelegate::class.java).forEach { (name, delegate) ->
|
||||
descend(name).apply { delegate.addToBuilder(this) }
|
||||
}
|
||||
}
|
||||
|
||||
fun descend(pathName: String) = ConfigBuildContext(langPrefix, path + pathName, builder)
|
||||
}
|
||||
|
||||
open class ConfigCategory(val comment: String? = null) {
|
||||
}
|
||||
|
||||
abstract class ConfigDelegate<T> : ReadOnlyProperty<Any, T> {
|
||||
lateinit var configValue: ForgeConfigSpec.ConfigValue<T>
|
||||
var cachedValue: T? = null
|
||||
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): T {
|
||||
if (cachedValue == null) cachedValue = configValue.get()
|
||||
return cachedValue!!
|
||||
}
|
||||
|
||||
abstract fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder): ForgeConfigSpec.ConfigValue<T>
|
||||
fun addToBuilder(ctx: ConfigBuildContext) {
|
||||
val langKey = ctx.langPrefix + "." + (langPrefixOverride ?: ctx.path.joinToString("."))
|
||||
ctx.builder.translation(langKey)
|
||||
configValue = getConfigValue(ctx.path.last(), ctx.builder)
|
||||
}
|
||||
|
||||
var langPrefixOverride: String? = null
|
||||
fun lang(prefix: String) = apply { langPrefixOverride = prefix }
|
||||
|
||||
}
|
||||
|
||||
class DelegatingBooleanValue(val defaultValue: Boolean) : ConfigDelegate<Boolean>() {
|
||||
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.define(name, defaultValue)
|
||||
}
|
||||
|
||||
class DelegatingIntValue(
|
||||
val minValue: Int = 0,
|
||||
val maxValue: Int = 1,
|
||||
val defaultValue: Int = 0
|
||||
) : ConfigDelegate<Int>() {
|
||||
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
|
||||
}
|
||||
|
||||
class DelegatingLongValue(
|
||||
val minValue: Long = 0,
|
||||
val maxValue: Long = 1,
|
||||
val defaultValue: Long = 0
|
||||
) : ConfigDelegate<Long>() {
|
||||
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
|
||||
}
|
||||
|
||||
class DelegatingDoubleValue(
|
||||
val minValue: Double = 0.0,
|
||||
val maxValue: Double = 1.0,
|
||||
val defaultValue: Double = 0.0
|
||||
) : ConfigDelegate<Double>() {
|
||||
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Delegate factory methods
|
||||
// ============================
|
||||
fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = DelegatingDoubleValue(min, max, default)
|
||||
fun int(min: Int = 0, max: Int, default: Int) = DelegatingIntValue(min, max, default)
|
||||
fun long(min: Long = 0, max: Long, default: Long) = DelegatingLongValue(min, max, default)
|
||||
fun boolean(default: Boolean) = DelegatingBooleanValue(default)
|
||||
158
src/main/kotlin/mods/betterfoliage/config/MainConfig.kt
Normal file
158
src/main/kotlin/mods/betterfoliage/config/MainConfig.kt
Normal file
@@ -0,0 +1,158 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import java.util.Random
|
||||
|
||||
fun featureEnable(default: Boolean = true) = boolean(default, langKey = recurring)
|
||||
|
||||
val Config get() = BetterFoliageMod.config
|
||||
|
||||
abstract class PopulationConfigGroup : DelegatingConfigGroup() {
|
||||
abstract val enabled: Boolean
|
||||
abstract val population: Int
|
||||
|
||||
fun enabled(random: Random) = random.nextInt(64) < population && enabled
|
||||
}
|
||||
|
||||
class MainConfig : DelegatingConfigGroup() {
|
||||
val enabled by boolean(true, langKey = { "betterfoliage.global.enabled" })
|
||||
val nVidia by boolean(false)
|
||||
|
||||
val leaves by subNode(::LeavesConfig)
|
||||
val shortGrass by subNode(::ShortGrassConfig)
|
||||
val connectedGrass by subNode(::ConnectedGrassConfig)
|
||||
val roundLogs by subNode(::RoundLogConfig)
|
||||
val cactus by subNode(::CactusConfig)
|
||||
val lilypad by subNode(::LilypadConfig)
|
||||
val reed by subNode(::ReedConfig)
|
||||
val algae by subNode(::AlgaeConfig)
|
||||
val coral by subNode(::CoralConfig)
|
||||
val netherrack by subNode(::NetherrackConfig)
|
||||
val fallingLeaves by subNode(::FallingLeavesConfig)
|
||||
val risingSoul by subNode(::RisingSoulConfig)
|
||||
}
|
||||
|
||||
class LeavesConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val snowEnabled by boolean(true)
|
||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
||||
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
|
||||
val size by double(min=0.75, max=2.5, default=1.4, langKey = recurring)
|
||||
val dense by boolean(false)
|
||||
val hideInternal by boolean(true)
|
||||
val saturationThreshold by double(default=0.1, langKey = recurring)
|
||||
}
|
||||
|
||||
class ShortGrassConfig : PopulationConfigGroup() {
|
||||
override val enabled by featureEnable()
|
||||
val grassEnabled by boolean(true)
|
||||
val myceliumEnabled by boolean(true)
|
||||
val snowEnabled by boolean(true)
|
||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
||||
val heightMin by double(min=0.1, max=2.5, default=0.6, langKey = recurring)
|
||||
val heightMax by double(min=0.1, max=2.5, default=0.8, langKey = recurring)
|
||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
||||
override val population by integer(max=64, default=64, langKey = recurring)
|
||||
val useGenerated by boolean(false)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
val saturationThreshold by double(default=0.1, langKey = recurring)
|
||||
}
|
||||
|
||||
class ConnectedGrassConfig : DelegatingConfigGroup() {
|
||||
val enabled by boolean(true)
|
||||
val snowEnabled by boolean(false)
|
||||
}
|
||||
|
||||
class RoundLogConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val plantsOnly by boolean(true)
|
||||
val radiusSmall by double(max=0.5, default=0.25)
|
||||
val radiusLarge by double(max=0.5, default=0.44)
|
||||
val dimming by double(default = 0.7)
|
||||
val connectSolids by boolean(false)
|
||||
val lenientConnect by boolean(true)
|
||||
val connectPerpendicular by boolean(true)
|
||||
val connectGrass by boolean(true)
|
||||
val defaultY by boolean(false)
|
||||
val zProtection by double(min = 0.9, default = 0.99)
|
||||
}
|
||||
|
||||
class CactusConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val size by double(min=0.5, max=1.5, default=0.8, langKey = recurring)
|
||||
val sizeVariation by double(max=0.5, default=0.1)
|
||||
val hOffset by double(max=0.5, default=0.1, langKey = recurring)
|
||||
}
|
||||
|
||||
class LilypadConfig : PopulationConfigGroup() {
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
|
||||
override val population by integer(max=64, default=16, min=0, langKey = recurring)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
}
|
||||
|
||||
class ReedConfig : PopulationConfigGroup() {
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
||||
val heightMin by double(min=1.5, max=3.5, default=1.7, langKey = recurring)
|
||||
val heightMax by double(min=1.5, max=3.5, default=2.2, langKey = recurring)
|
||||
override val population by integer(max=64, default=32, langKey = recurring)
|
||||
val minBiomeTemp by double(default=0.4)
|
||||
val minBiomeRainfall by double(default=0.4)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
}
|
||||
|
||||
class AlgaeConfig : PopulationConfigGroup() {
|
||||
override val enabled by featureEnable()
|
||||
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
|
||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
||||
val heightMin by double(min=0.1, max=1.5, default=0.5, langKey = recurring)
|
||||
val heightMax by double(min=0.1, max=1.5, default=1.0, langKey = recurring)
|
||||
override val population by integer(max=64, default=48, langKey = recurring)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
}
|
||||
|
||||
class CoralConfig : PopulationConfigGroup() {
|
||||
override val enabled by featureEnable()
|
||||
val shallowWater by boolean(false)
|
||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
||||
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
|
||||
val size by double(min=0.5, max=1.5, default=0.7, langKey = recurring)
|
||||
val crustSize by double(min=0.5, max=1.5, default=1.4)
|
||||
val chance by integer(max=64, default=32)
|
||||
override val population by integer(max=64, default=48, langKey = recurring)
|
||||
}
|
||||
|
||||
class NetherrackConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
|
||||
val heightMin by double(min=0.1, max=1.5, default=0.6, langKey = recurring)
|
||||
val heightMax by double(min=0.1, max=1.5, default=0.8, langKey = recurring)
|
||||
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
|
||||
}
|
||||
|
||||
class FallingLeavesConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val speed by double(min=0.01, max=0.15, default=0.05)
|
||||
val windStrength by double(min=0.1, max=2.0, default=0.5)
|
||||
val stormStrength by double(min=0.1, max=2.0, default=0.8)
|
||||
val size by double(min=0.25, max=1.5, default=0.75, langKey = recurring)
|
||||
val chance by double(min=0.001, max=1.0, default=0.02)
|
||||
val perturb by double(min=0.01, max=1.0, default=0.25)
|
||||
val lifetime by double(min=1.0, max=15.0, default=5.0)
|
||||
val opacityHack by boolean(true)
|
||||
}
|
||||
|
||||
class RisingSoulConfig : DelegatingConfigGroup() {
|
||||
val enabled by featureEnable()
|
||||
val chance by double(min=0.001, max=1.0, default=0.02)
|
||||
val perturb by double(min=0.01, max=0.25, default=0.05)
|
||||
val headSize by double(min=0.25, max=1.5, default=1.0)
|
||||
val trailSize by double(min=0.25, max=1.5, default=0.75)
|
||||
val opacity by double(min=0.05, max=1.0, default=0.5)
|
||||
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
|
||||
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
|
||||
val lifetime by double(min=1.0, max=15.0, default=4.0)
|
||||
val trailLength by integer(min=2, max=128, default=48)
|
||||
val trailDensity by integer(min=1, max=16, default=3)
|
||||
}
|
||||
@@ -5,14 +5,9 @@ import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.material.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 LILYPAD_BLOCKS = listOf(Blocks.LILY_PAD)
|
||||
val MYCELIUM_BLOCKS = listOf(Blocks.MYCELIUM)
|
||||
|
||||
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
|
||||
|
||||
val SNOW_MATERIALS = listOf(Material.SNOW_BLOCK, Material.SNOW)
|
||||
val BlockState.isSnow: Boolean get() = material in SNOW_MATERIALS
|
||||
val SNOW_MATERIALS = listOf(Material.TOP_SNOW, Material.SNOW)
|
||||
val BlockState.isSnow: Boolean get() = material in SNOW_MATERIALS
|
||||
|
||||
val ACCEPTED_ROUND_LOG_MATERIALS = listOf(Material.WOOD, Material.GRASS)
|
||||
|
||||
162
src/main/kotlin/mods/betterfoliage/config/match/Match.kt
Normal file
162
src/main/kotlin/mods/betterfoliage/config/match/Match.kt
Normal file
@@ -0,0 +1,162 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
enum class MatchMethod {
|
||||
EXACT_MATCH, EXTENDS, CONTAINS;
|
||||
|
||||
fun description(isSuccess: Boolean) = when (this) {
|
||||
EXACT_MATCH -> if (isSuccess) "matches" else "does not match"
|
||||
EXTENDS -> if (isSuccess) "extends" else "does not extend"
|
||||
CONTAINS -> if (isSuccess) "contains" else "does not contain"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Either monad implementation
|
||||
*/
|
||||
sealed class Either<out L, out R> {
|
||||
class Left<L>(val left: L) : Either<L, Nothing>()
|
||||
class Right<R>(val right: R) : Either<Nothing, R>()
|
||||
|
||||
fun leftOrNull() = if (this is Left) left else null
|
||||
fun rightOrNull() = if (this is Right) right else null
|
||||
|
||||
fun <R2> map(func: (R) -> R2): Either<L, R2> = when (this) {
|
||||
is Left<L> -> this
|
||||
is Right<R> -> Right(func(right))
|
||||
}
|
||||
|
||||
fun <L2> mapLeft(func: (L) -> L2): Either<L2, R> = when (this) {
|
||||
is Left<L> -> Left(func(left))
|
||||
is Right<R> -> this
|
||||
}
|
||||
|
||||
fun ifRight(action: (R) -> Unit) {
|
||||
if (this is Right) action(right)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <L> ofLeft(left: L) = Left(left)
|
||||
fun <R> ofRight(right: R) = Right(right)
|
||||
}
|
||||
}
|
||||
|
||||
// this cannot be inside the class for variance reasons
|
||||
fun <L, R, R2> Either<L, R>.flatMap(func: (R) -> Either<L, R2>) = when (this) {
|
||||
is Either.Left<L> -> this
|
||||
is Either.Right<R> -> func(right)
|
||||
}
|
||||
|
||||
fun <L, R, L2> Either<L, R>.flatMapLeft(func: (L) -> Either<L2, R>) = when (this) {
|
||||
is Either.Left<L> -> func(left)
|
||||
is Either.Right<R> -> this
|
||||
}
|
||||
|
||||
fun <T> Either<T, T>.flatten() = when (this) {
|
||||
is Either.Left -> left
|
||||
is Either.Right -> right
|
||||
}
|
||||
|
||||
interface MAnything<out T> {
|
||||
val value: T
|
||||
val immutable: Boolean
|
||||
}
|
||||
|
||||
class MListAll(val list: List<MAnything<Boolean>>) : MAnything<Boolean> {
|
||||
override val value get() = list.all { it.value }
|
||||
override val immutable get() = list.all { it.immutable }
|
||||
}
|
||||
|
||||
class MListAny(val list: List<MValue<Boolean>>) : MAnything<Boolean> {
|
||||
override val value get() = list.any { it.value }
|
||||
override val immutable get() = list.all { it.immutable }
|
||||
}
|
||||
|
||||
class MNegated(val inner: MAnything<Boolean>) : MAnything<Boolean> {
|
||||
override val value get() = !inner.value
|
||||
override val immutable get() = inner.immutable
|
||||
}
|
||||
|
||||
/**
|
||||
* Value with metadata related to rule matching applied.
|
||||
*
|
||||
* @param value the wrapped value
|
||||
* @param description human-readable description of what the value represents
|
||||
* @param configSource identifies where the value is described in the config
|
||||
* @param immutable true if the value never changes
|
||||
* (another [MValue] constructed in the same way will have the same value)
|
||||
*
|
||||
*/
|
||||
class MValue<out T>(
|
||||
override val value: T,
|
||||
val description: String,
|
||||
val configSource: ConfigSource,
|
||||
override val immutable: Boolean,
|
||||
) : MAnything<T> {
|
||||
companion object {
|
||||
fun <T> right(value: T, description: String, configSource: ConfigSource, immutable: Boolean = true) =
|
||||
Either.ofRight(MValue(value, description, configSource, immutable))
|
||||
|
||||
fun left(description: String, configSource: ConfigSource, immutable: Boolean = true) =
|
||||
Either.ofLeft(MValue(false, description, configSource, immutable))
|
||||
}
|
||||
}
|
||||
|
||||
typealias MEither<T> = Either<MValue<Boolean>, MValue<T>>
|
||||
|
||||
val Node.Value.asEither get() = MValue.right(value, value, configSource, true)
|
||||
fun Node.Value.left(description: String) = MValue.left(description, configSource)
|
||||
fun Node.invalidTypeFor(type: String) = MValue.left("invalid type for $type: [${this::class.java.name}]", configSource)
|
||||
fun Node.error(description: String) = MValue.left(description, configSource)
|
||||
|
||||
fun <T, R> MEither<T>.mapValue(func: (T) -> R) = map {
|
||||
MValue(func(it.value), it.description, it.configSource, it.immutable)
|
||||
}
|
||||
|
||||
fun <T> MEither<T>.mapDescription(func: (MValue<T>) -> String) = map {
|
||||
MValue(it.value, func(it), it.configSource, it.immutable)
|
||||
}
|
||||
|
||||
fun <T, R> MEither<T>.map(
|
||||
func: (T) -> R,
|
||||
description: (MValue<T>, R) -> String
|
||||
) = map { t -> func(t.value).let { r -> MValue(r, description(t, r), t.configSource, t.immutable) } }
|
||||
|
||||
fun <T, R> MEither<T>.mapNotNull(
|
||||
func: (T) -> R?,
|
||||
dLeft: (MValue<T>) -> String = { it.description },
|
||||
dRight: (MValue<T>, R) -> String = { m, _ -> m.description }
|
||||
) = flatMap { t ->
|
||||
func(t.value)?.let { r ->
|
||||
MValue.right(r, dRight(t, r), t.configSource, t.immutable)
|
||||
} ?: MValue.left(dLeft(t), t.configSource, t.immutable)
|
||||
}
|
||||
|
||||
fun <T> MEither<T>.toRight(value: T) =
|
||||
flatMapLeft { MValue.right(value, it.description, it.configSource, it.immutable) }
|
||||
|
||||
data class MComparison<T1, T2>(
|
||||
private val opTrue: String,
|
||||
private val opFalse: String,
|
||||
val testFunc: (T1, T2) -> Boolean
|
||||
) {
|
||||
fun compare(value1: MEither<T1>, value2: MEither<T2>) = when {
|
||||
value1 is Either.Left -> value1
|
||||
value2 is Either.Left -> value2
|
||||
else -> {
|
||||
val isSuccess = testFunc((value1 as Either.Right).right.value, (value2 as Either.Right).right.value)
|
||||
MValue.right(
|
||||
isSuccess,
|
||||
"${value1.right.description} ${if (isSuccess) opTrue else opFalse} ${value2.right.description}",
|
||||
value2.right.configSource,
|
||||
value1.right.immutable && value2.right.immutable
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
|
||||
companion object {
|
||||
fun <T1, T2> of(matchMethod: MatchMethod, testFunc: (T1, T2) -> Boolean) =
|
||||
MComparison(matchMethod.description(true), matchMethod.description(false), testFunc)
|
||||
|
||||
val equals = of(MatchMethod.EXACT_MATCH) { t1: Any, t2: Any -> t1 == t2 }
|
||||
}
|
||||
}
|
||||
42
src/main/kotlin/mods/betterfoliage/config/match/ParseTree.kt
Normal file
42
src/main/kotlin/mods/betterfoliage/config/match/ParseTree.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
data class ConfigSource(
|
||||
val configFile: String,
|
||||
val line: Int,
|
||||
val column: Int
|
||||
) {
|
||||
override fun toString() = "$configFile @ R$line,C$column"
|
||||
}
|
||||
|
||||
sealed class Node {
|
||||
enum class MatchSource { BLOCK_CLASS, BLOCK_NAME, MODEL_LOCATION }
|
||||
abstract val configSource: ConfigSource
|
||||
|
||||
class MatchValueList(
|
||||
val matchSource: MatchSource,
|
||||
val matchMethod: MatchMethod,
|
||||
override val configSource: ConfigSource,
|
||||
val values: List<Value>
|
||||
) : Node()
|
||||
|
||||
class MatchParam(
|
||||
val name: String,
|
||||
val values: List<Value>,
|
||||
override val configSource: ConfigSource,
|
||||
) : Node()
|
||||
|
||||
class Negate(val node: Node) : Node() {
|
||||
override val configSource get() = node.configSource
|
||||
}
|
||||
|
||||
class SetParam(val name: String, val value: Value, override val configSource: ConfigSource) : Node()
|
||||
|
||||
class MatchAll(override val configSource: ConfigSource, val list: List<Node>) : Node()
|
||||
|
||||
abstract class Value(override val configSource: ConfigSource, val value: String) : Node() {
|
||||
class Literal(configSource: ConfigSource, value: String) : Value(configSource, value)
|
||||
class ClassOf(configSource: ConfigSource, value: String) : Value(configSource, value)
|
||||
class Texture(configSource: ConfigSource, value: String) : Value(configSource, value)
|
||||
class Tint(configSource: ConfigSource, value: String) : Value(configSource, value)
|
||||
}
|
||||
}
|
||||
172
src/main/kotlin/mods/betterfoliage/config/match/Rules.kt
Normal file
172
src/main/kotlin/mods/betterfoliage/config/match/Rules.kt
Normal file
@@ -0,0 +1,172 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
import mods.betterfoliage.config.match.MatchMethod.CONTAINS
|
||||
import mods.betterfoliage.config.match.MatchMethod.EXACT_MATCH
|
||||
import mods.betterfoliage.config.match.MatchMethod.EXTENDS
|
||||
import mods.betterfoliage.resource.discovery.RuleProcessingContext
|
||||
import mods.betterfoliage.util.findFirst
|
||||
import mods.betterfoliage.util.quoted
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
|
||||
typealias PartialLocation = Pair<String?, String>
|
||||
|
||||
object MatchRules {
|
||||
fun visitRoot(ctx: RuleProcessingContext, node: Node.MatchAll): MListAll {
|
||||
val results = mutableListOf<MAnything<Boolean>>()
|
||||
for (rule in node.list) {
|
||||
val result = mNode(ctx, rule)
|
||||
results.add(result)
|
||||
if (!result.value) break
|
||||
}
|
||||
return MListAll(results)
|
||||
}
|
||||
|
||||
fun mNode(ctx: RuleProcessingContext, node: Node): MAnything<Boolean> = when(node) {
|
||||
is Node.MatchValueList -> mMatchList(ctx, node)
|
||||
is Node.MatchParam -> mParam(ctx, node)
|
||||
is Node.SetParam -> mParamSet(ctx, node)
|
||||
is Node.Negate -> mNegate(ctx, node)
|
||||
else -> node.error("match type not implemented: ${node::class.java.name.quoted}").left
|
||||
}
|
||||
|
||||
fun mNegate(ctx: RuleProcessingContext, node: Node.Negate) = MNegated(mNode(ctx, node.node))
|
||||
|
||||
fun mMatchList(ctx: RuleProcessingContext, node: Node.MatchValueList) = node.values.map { value ->
|
||||
when (node.matchSource) {
|
||||
Node.MatchSource.BLOCK_CLASS -> mBlockClass(ctx, node, value)
|
||||
Node.MatchSource.BLOCK_NAME -> mBlockName(ctx, node, value)
|
||||
Node.MatchSource.MODEL_LOCATION -> mModel(ctx, node, value)
|
||||
}
|
||||
}.let { MListAny(it) }
|
||||
|
||||
fun mBlockClass(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
||||
val blockClass = ctx.discovery.blockState.block::class.java.let {
|
||||
MValue.right(it, "block class ${it.name.quoted}", node.configSource)
|
||||
}
|
||||
val target = when(value) {
|
||||
is Node.Value.Literal -> value.asEither.mapNotNull(
|
||||
func = { tryDefault(null) { Class.forName(it) }},
|
||||
dLeft = { "missing class ${it.value}" },
|
||||
dRight = { m, _ -> " class ${m.value}" }
|
||||
)
|
||||
is Node.Value.ClassOf -> value.asEither.mapValue(::ResourceLocation).mapNotNull(
|
||||
func = { loc -> ForgeRegistries.BLOCKS.getValue(loc)?.let { it::class.java } },
|
||||
dLeft = { "missing block ${it.value.toString().quoted}" },
|
||||
dRight = { m, r -> "class ${r.name.quoted} of block ${m.value}" }
|
||||
)
|
||||
else -> value.invalidTypeFor("block class")
|
||||
}
|
||||
return when(node.matchMethod) {
|
||||
EXACT_MATCH -> MComparison.equals.compare(blockClass, target)
|
||||
EXTENDS -> classExtends.compare(blockClass, target)
|
||||
CONTAINS -> node.error("invalid match type for block class: contains").left
|
||||
}
|
||||
}
|
||||
|
||||
fun mBlockName(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
||||
val blockName = MValue.right(ctx.discovery.blockState.block, "", node.configSource).mapNotNull(
|
||||
func = { it.registryName }, dLeft = { "missing block name" }, dRight = { _, r -> "block name ${r.toString().quoted}" }
|
||||
)
|
||||
val target = when(value) {
|
||||
is Node.Value.Literal -> value.asEither.map(::splitLocation, ::quoteString)
|
||||
else -> value.invalidTypeFor("block name")
|
||||
}
|
||||
return when(node.matchMethod) {
|
||||
EXACT_MATCH -> blockNameExact.compare(blockName, target)
|
||||
CONTAINS -> blockNameContains.compare(blockName, target)
|
||||
EXTENDS -> node.error("invalid match type for block name: extends").left
|
||||
}
|
||||
}
|
||||
|
||||
fun mModel(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MValue<Boolean> {
|
||||
val model = (ctx.discovery.getUnbaked() as? BlockModel)?.let {
|
||||
MValue.right(it, "model ${it.name.quoted}", node.configSource)
|
||||
} ?: node.error("unsupported model type: ${ctx.discovery.getUnbaked()::class.java.name.quoted}")
|
||||
|
||||
val target = when(value) {
|
||||
is Node.Value.Literal -> value.asEither.map(::splitLocation, ::quoteString)
|
||||
else -> value.invalidTypeFor("model")
|
||||
}
|
||||
val models = when(node.matchMethod) {
|
||||
EXTENDS -> model.mapValue { ctx.discovery.loadHierarchy(it).ancestors() }
|
||||
else -> model.mapValue { listOf(it) }
|
||||
}
|
||||
return when(node.matchMethod) {
|
||||
EXACT_MATCH, EXTENDS -> anyModel(node.matchMethod, ::locationMatches)
|
||||
CONTAINS -> anyModel(CONTAINS, ::locationContains)
|
||||
}.compare(models, target)
|
||||
}
|
||||
|
||||
fun mParam(ctx: RuleProcessingContext, node: Node.MatchParam) = node.values.map { value ->
|
||||
val paramValue = ctx.params[node.name] ?.let {
|
||||
MValue.right(it, "parameter ${node.name.quoted}", node.configSource, immutable = false)
|
||||
} ?: node.error("missing parameter ${node.name.quoted}")
|
||||
|
||||
val target = when(value) {
|
||||
is Node.Value.Literal -> value.asEither.mapDescription { it.description.quoted }
|
||||
else -> value.invalidTypeFor("parameter")
|
||||
}
|
||||
MComparison.equals.compare(paramValue, target)
|
||||
}.let { MListAny(it) }
|
||||
|
||||
fun mParamSet(ctx: RuleProcessingContext, node: Node.SetParam): MValue<Boolean> {
|
||||
val target = when(node.value) {
|
||||
is Node.Value.Literal -> node.value.asEither
|
||||
is Node.Value.Texture -> when(val model = ctx.discovery.getUnbaked()) {
|
||||
is BlockModel -> node.value.asEither.map(
|
||||
func = { model.getMaterial(it).texture().toString() },
|
||||
description = { m, r -> "texture \"${m.value}\" = \"$r\" of model ${model.name}"}
|
||||
)
|
||||
else -> node.error("unsupported model type: ${model::class.java.name.quoted}")
|
||||
}
|
||||
is Node.Value.Tint -> when(val model = ctx.discovery.getUnbaked()) {
|
||||
is BlockModel -> node.value.asEither.mapNotNull(
|
||||
func = { model.tintOf(it)?.toString() },
|
||||
dRight = { m, r -> "tint index $r for sprite ${m.value}" },
|
||||
dLeft = { m -> "tint index -1 for unused sprite ${m.value}"}
|
||||
).toRight("-1")
|
||||
|
||||
else -> node.error("unsupported model type: ${model::class.java.name.quoted}")
|
||||
}
|
||||
else -> node.value.invalidTypeFor("prameter")
|
||||
}
|
||||
target.ifRight { ctx.params[node.name] = it.value }
|
||||
return target.mapDescription { m -> "parameter ${node.name} set to ${m.value}" }.mapValue { true }.flatten()
|
||||
}
|
||||
|
||||
private val classExtends = MComparison.of<Class<*>, Class<*>>(EXTENDS) { c1, c2 -> c2.isAssignableFrom(c1) }
|
||||
|
||||
private val blockNameExact = MComparison.of<ResourceLocation, PartialLocation>(EXACT_MATCH) { block, partial ->
|
||||
locationMatches(block, partial)
|
||||
}
|
||||
private val blockNameContains = MComparison.of<ResourceLocation, Pair<String?, String>>(CONTAINS) { block, partial ->
|
||||
locationContains(block, partial)
|
||||
}
|
||||
private fun anyModel(matchMethod: MatchMethod, func: (ResourceLocation, PartialLocation)->Boolean) =
|
||||
MComparison.of<List<BlockModel>, PartialLocation>(matchMethod) { models, partial ->
|
||||
models.any { func(ResourceLocation(it.name), partial) }
|
||||
}
|
||||
|
||||
|
||||
fun locationMatches(loc: ResourceLocation, partial: PartialLocation) =
|
||||
(partial.first == null || loc.namespace == partial.first) && loc.path == partial.second
|
||||
fun locationContains(loc: ResourceLocation, partial: PartialLocation) =
|
||||
(partial.first == null || loc.namespace.contains(partial.first!!)) && loc.path.contains(partial.second)
|
||||
|
||||
fun splitLocation(str: String): PartialLocation =
|
||||
if (str.contains(":")) ResourceLocation(str).let { it.namespace to it.path } else null to str
|
||||
|
||||
fun <T, R> quoteString(mValue: MValue<T>, newValue: R) = mValue.description.quoted
|
||||
|
||||
fun BlockModel.ancestors(): List<BlockModel> = if (parent == null) listOf(this) else parent!!.ancestors() + this
|
||||
|
||||
fun BlockModel.tintOf(spriteName: String) =
|
||||
elements.findFirst { element ->
|
||||
element.faces.entries.findFirst { (_, face) ->
|
||||
if (face.texture == "#$spriteName") face.tintIndex else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.util.ThreadLocalDelegate
|
||||
import mods.betterfoliage.util.allAvailable
|
||||
import mods.betterfoliage.util.reflectField
|
||||
import mods.octarinecore.BlockPos
|
||||
import mods.octarinecore.BlockState
|
||||
import mods.octarinecore.CustomColors
|
||||
import mods.octarinecore.RenderEnv
|
||||
import mods.betterfoliage.BlockPos
|
||||
import mods.betterfoliage.BlockState
|
||||
import mods.betterfoliage.CustomColors
|
||||
import mods.betterfoliage.RenderEnv
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.BakedQuad
|
||||
@@ -34,7 +34,7 @@ object OptifineCustomColors {
|
||||
val fakeQuad = BakedQuad(IntArray(0), 1, UP, null, true)
|
||||
|
||||
fun getBlockColor(ctx: BlockCtx, resolver: ColorResolver): Int {
|
||||
val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
|
||||
val ofColor = if (isColorAvailable && Minecraft.getInstance().options.reflectField<Boolean>("ofCustomColors") == true) {
|
||||
renderEnv.reset(ctx.state, ctx.pos)
|
||||
CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
|
||||
} else null
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package mods.betterfoliage.integration
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BlockAliases
|
||||
import mods.betterfoliage.BufferBuilder_sVertexBuilder
|
||||
import mods.betterfoliage.SVertexBuilder
|
||||
import mods.betterfoliage.Shaders
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.allAvailable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.mapArray
|
||||
import mods.octarinecore.*
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockRenderType.MODEL
|
||||
import net.minecraft.block.BlockState
|
||||
@@ -20,7 +23,7 @@ import net.minecraft.util.Direction.NORTH
|
||||
import net.minecraft.util.Direction.SOUTH
|
||||
import net.minecraft.util.Direction.WEST
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraftforge.client.model.pipeline.LightUtil
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
|
||||
@@ -31,8 +34,8 @@ object ShadersModIntegration : HasLogger() {
|
||||
@JvmStatic val isEffectsAvailable = allAvailable(SVertexBuilder.pushState, SVertexBuilder.popState, BlockAliases.getAliasBlockId)
|
||||
@JvmStatic val isDiffuseAvailable = allAvailable(Shaders.shaderPackLoaded, Shaders.blockLightLevel05, Shaders.blockLightLevel06, Shaders.blockLightLevel08)
|
||||
|
||||
@JvmStatic val defaultLeaves = Blocks.OAK_LEAVES.defaultState!!
|
||||
@JvmStatic val defaultGrass = Blocks.GRASS.defaultState!!
|
||||
@JvmStatic val defaultLeaves = Blocks.OAK_LEAVES.defaultBlockState()
|
||||
@JvmStatic val defaultGrass = Blocks.GRASS.defaultBlockState()
|
||||
|
||||
@JvmStatic var diffuseShades = Direction.values().mapArray { LightUtil.diffuseLight(it) }
|
||||
|
||||
@@ -40,8 +43,8 @@ object ShadersModIntegration : HasLogger() {
|
||||
* Called from transformed ShadersMod code.
|
||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
||||
*/
|
||||
@JvmStatic fun getBlockStateOverride(state: BlockState, world: ILightReader, pos: BlockPos): BlockState {
|
||||
// if (LeafRegistry[state, world, pos] != null) return defaultLeaves
|
||||
@JvmStatic fun getBlockStateOverride(state: BlockState, world: IBlockDisplayReader, pos: BlockPos): BlockState {
|
||||
if (state in BetterFoliage.blockTypes.leaf) return defaultLeaves
|
||||
// if (BlockConfig.crops.matchesClass(state.block)) return defaultGrass
|
||||
return state
|
||||
}
|
||||
@@ -50,8 +53,8 @@ object ShadersModIntegration : HasLogger() {
|
||||
logger.log(INFO, "ShadersMod diffuse shading integration is ${if (isDiffuseAvailable) "enabled" else "disabled" }")
|
||||
logger.log(INFO, "ShadersMod vertex shader integration is ${if (isEffectsAvailable) "enabled" else "disabled" }")
|
||||
|
||||
// Recalculate the diffsuse shading values used when resources are reloaded
|
||||
if (isDiffuseAvailable) BakeWrapperManager.onInvalidate {
|
||||
// Recalculate the diffuse shading values used when resources are reloaded
|
||||
if (isDiffuseAvailable) BetterFoliage.modelManager.onInvalidate {
|
||||
if (Shaders.shaderPackLoaded.getStatic()) {
|
||||
diffuseShades = Direction.values().mapArray { face ->
|
||||
when(face) {
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.render.pipeline.WrappedLayerPredicate
|
||||
import mods.betterfoliage.render.pipeline.layerPredicate
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.directionsAndNull
|
||||
import mods.betterfoliage.util.mapArray
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.RenderTypeLookup
|
||||
import net.minecraft.client.renderer.model.BakedQuad
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.SimpleBakedModel
|
||||
@@ -27,15 +34,23 @@ data class HalfBakedQuad(
|
||||
open class HalfBakedSimpleModelWrapper(baseModel: SimpleBakedModel): IBakedModel by baseModel, SpecialRenderModel {
|
||||
val baseQuads = baseModel.unbakeQuads()
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
ctx.renderQuads(baseQuads)
|
||||
override fun prepare(ctx: BlockCtx, random: Random) = Unit
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
// if the passed data is a BlockState, render on the same layer(s) as that block
|
||||
val testState = (data as? BlockState) ?: ctx.state
|
||||
|
||||
// this could get called for more layers than the underlying model is on
|
||||
// ignore extra decoration layers
|
||||
val shouldRender = when(val predicate = testState.block.layerPredicate) {
|
||||
is WrappedLayerPredicate -> predicate.original.test(layer)
|
||||
else -> RenderTypeLookup.canRenderInLayer(testState, layer)
|
||||
}
|
||||
if (shouldRender) ctx.renderQuads(baseQuads)
|
||||
}
|
||||
}
|
||||
|
||||
open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): IBakedModel by baseModel, SpecialRenderModel {
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
baseModel.render(ctx, noDecorations)
|
||||
}
|
||||
open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): SpecialRenderModel by baseModel {
|
||||
}
|
||||
|
||||
abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() {
|
||||
@@ -68,8 +83,8 @@ fun List<Quad>.bake(applyDiffuseLighting: Boolean) = map { quad ->
|
||||
)
|
||||
// don't fill lightmap UV coords
|
||||
element.usage == VertexFormatElement.Usage.UV && element.type == VertexFormatElement.Type.FLOAT -> builder.put(idx,
|
||||
quad.sprite.minU + (quad.sprite.maxU - quad.sprite.minU) * (vertex.uv.u + 0.5).toFloat(),
|
||||
quad.sprite.minV + (quad.sprite.maxV - quad.sprite.minV) * (vertex.uv.v + 0.5).toFloat(),
|
||||
quad.sprite.u0 + (quad.sprite.u1 - quad.sprite.u0) * (vertex.uv.u + 0.5).toFloat(),
|
||||
quad.sprite.v0 + (quad.sprite.v1 - quad.sprite.v0) * (vertex.uv.v + 0.5).toFloat(),
|
||||
0.0f, 1.0f
|
||||
)
|
||||
element.usage == VertexFormatElement.Usage.COLOR -> builder.put(idx,
|
||||
@@ -96,18 +111,18 @@ fun Array<List<Quad>>.bake(applyDiffuseLighting: Boolean) = mapArray { it.bake(a
|
||||
fun BakedQuad.unbake(): HalfBakedQuad {
|
||||
val size = DefaultVertexFormats.BLOCK.integerSize
|
||||
val verts = Array(4) { vIdx ->
|
||||
val x = java.lang.Float.intBitsToFloat(vertexData[vIdx * size + 0]) - 0.5f
|
||||
val y = java.lang.Float.intBitsToFloat(vertexData[vIdx * size + 1]) - 0.5f
|
||||
val z = java.lang.Float.intBitsToFloat(vertexData[vIdx * size + 2]) - 0.5f
|
||||
val color = vertexData[vIdx * size + 3]
|
||||
val u = java.lang.Float.intBitsToFloat(vertexData[vIdx * size + 4])
|
||||
val v = java.lang.Float.intBitsToFloat(vertexData[vIdx * size + 5])
|
||||
val x = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 0]) - 0.5f
|
||||
val y = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 1]) - 0.5f
|
||||
val z = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 2]) - 0.5f
|
||||
val color = vertices[vIdx * size + 3]
|
||||
val u = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 4])
|
||||
val v = java.lang.Float.intBitsToFloat(vertices[vIdx * size + 5])
|
||||
Vertex(Double3(x, y, z), UV(u.toDouble(), v.toDouble()), Color(color))
|
||||
}
|
||||
val unbaked = Quad(
|
||||
verts[0], verts[1], verts[2], verts[3],
|
||||
colorIndex = if (hasTintIndex()) tintIndex else -1,
|
||||
face = face
|
||||
colorIndex = if (isTinted) tintIndex else -1,
|
||||
face = direction
|
||||
)
|
||||
return HalfBakedQuad(unbaked, this)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ data class Color(val alpha: Int, val red: Int, val green: Int, val blue: Int) {
|
||||
)
|
||||
|
||||
val asInt get() = (alpha shl 24) or (red shl 16) or (green shl 8) or blue
|
||||
val asHSB get() = HSB.fromColor(this)
|
||||
|
||||
operator fun times(f: Float) = Color(
|
||||
alpha,
|
||||
(f * red.toFloat()).toInt().coerceIn(0 until 256),
|
||||
@@ -89,8 +91,17 @@ data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
||||
val hsbVals = java.awt.Color.RGBtoHSB(color and 255, (color shr 8) and 255, (color shr 16) and 255, null)
|
||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||
}
|
||||
fun fromColorBGRA(color: Int): HSB {
|
||||
val hsbVals = java.awt.Color.RGBtoHSB((color shr 16) and 255, (color shr 8) and 255, color and 255, null)
|
||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||
}
|
||||
fun fromColor(color: Color): HSB {
|
||||
val hsbVals = java.awt.Color.RGBtoHSB(color.red, color.green, color.blue, null)
|
||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||
}
|
||||
}
|
||||
val asColor: Int get() = java.awt.Color.HSBtoRGB(hue, saturation, brightness)
|
||||
val asInt: Int get() = java.awt.Color.HSBtoRGB(hue, saturation, brightness)
|
||||
val asColor: Color get() = Color(asInt)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,34 +1,57 @@
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.client.renderer.model.WeightedBakedModel
|
||||
import net.minecraft.util.WeightedRandom
|
||||
import org.apache.logging.log4j.Level.WARN
|
||||
import java.util.Random
|
||||
import java.util.function.Function
|
||||
|
||||
/**
|
||||
* Model that makes use of advanced rendering features.
|
||||
*/
|
||||
interface SpecialRenderModel : IBakedModel {
|
||||
fun render(ctx: RenderCtxBase, noDecorations: Boolean = false)
|
||||
/**
|
||||
* Create custom renderdata object. Called once per block. Result is passed to renderLayer().
|
||||
*/
|
||||
fun prepare(ctx: BlockCtx, random: Random): Any
|
||||
fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType)
|
||||
|
||||
/**
|
||||
* Get the actual model that will be rendered. Useful for container models (like [WeightedBakedModel]).
|
||||
*/
|
||||
fun resolve(random: Random) = this
|
||||
}
|
||||
|
||||
interface SpecialRenderData {
|
||||
fun canRenderInLayer(layer: RenderType) = false
|
||||
}
|
||||
|
||||
class WeightedModelWrapper(
|
||||
val models: List<WeightedModel>, baseModel: SpecialRenderModel
|
||||
): IBakedModel by baseModel, SpecialRenderModel {
|
||||
) : IBakedModel by baseModel, SpecialRenderModel {
|
||||
class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight)
|
||||
val totalWeight = models.sumBy { it.itemWeight }
|
||||
|
||||
fun getModel(random: Random) = WeightedRandom.getRandomItem(models, random.nextInt(totalWeight))
|
||||
val totalWeight = models.sumBy { it.weight }
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
getModel(ctx.random).model.render(ctx, noDecorations)
|
||||
fun getModel(random: Random) = WeightedRandom.getWeightedItem(models, random.nextInt(totalWeight))
|
||||
|
||||
override fun resolve(random: Random) = getModel(random).model.resolve(random)
|
||||
|
||||
override fun prepare(ctx: BlockCtx, random: Random) = getModel(random).model.let { actual ->
|
||||
WeightedRenderData(actual, actual.prepare(ctx, random))
|
||||
}
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) = when (data) {
|
||||
is WeightedRenderData -> data.model.renderLayer(ctx, data.modelRenderData, layer)
|
||||
else -> getModel(ctx.random).model.renderLayer(ctx, data, layer)
|
||||
}
|
||||
}
|
||||
|
||||
data class WeightedRenderData(
|
||||
val model: SpecialRenderModel,
|
||||
val modelRenderData: Any
|
||||
) : SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = (modelRenderData as? SpecialRenderData)?.canRenderInLayer(layer) ?: false
|
||||
}
|
||||
@@ -59,7 +59,7 @@ class SpriteSetDelegate(
|
||||
|
||||
@SubscribeEvent
|
||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureLocation != Atlas.BLOCKS.resourceId) return
|
||||
if (event.map.location() != atlas.resourceId) return
|
||||
spriteSet = null
|
||||
idList = (0 until 16)
|
||||
.map(idFunc)
|
||||
|
||||
@@ -18,7 +18,6 @@ import kotlin.math.sin
|
||||
|
||||
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx.toDouble() / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
|
||||
|
||||
|
||||
data class TuftShapeKey(
|
||||
val size: Double,
|
||||
val height: Double,
|
||||
@@ -50,13 +49,13 @@ fun tuftQuadSingle(size: Double, height: Double, flipU: Boolean) =
|
||||
)
|
||||
.mirrorUV(flipU, false)
|
||||
|
||||
fun tuftModelSet(shapes: Array<TuftShapeKey>, tintIndex: Int, spriteGetter: (Int) -> TextureAtlasSprite) =
|
||||
fun tuftModelSet(shapes: Array<TuftShapeKey>, color: Color, tint: Int, spriteGetter: (Int) -> TextureAtlasSprite) =
|
||||
shapes.mapIndexed { idx, shape ->
|
||||
listOf(
|
||||
tuftQuadSingle(shape.size, shape.height, shape.flipU1),
|
||||
tuftQuadSingle(shape.size, shape.height, shape.flipU2).rotate(rot(UP))
|
||||
).map { it.move(shape.offset) }
|
||||
.map { it.colorIndex(tintIndex) }
|
||||
.map { it.color(color).colorIndex(tint) }
|
||||
.map { it.sprite(spriteGetter(idx)) }
|
||||
}
|
||||
|
||||
@@ -85,20 +84,20 @@ fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Li
|
||||
}
|
||||
}
|
||||
|
||||
fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, tintIndex: Int,scrambleUV: Boolean) =
|
||||
fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, color: Color, tint: Int, scrambleUV: Boolean) =
|
||||
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
||||
.map { it.colorIndex(tintIndex) }
|
||||
.map { it.color(color).colorIndex(tint) }
|
||||
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
||||
.withOpposites()
|
||||
.bake(false)
|
||||
|
||||
fun crossModelsTextured(
|
||||
leafBase: Iterable<List<Quad>>,
|
||||
tintIndex: Int,
|
||||
color: Color, tint: Int,
|
||||
scrambleUV: Boolean,
|
||||
spriteGetter: (Int) -> ResourceLocation
|
||||
) = leafBase.mapIndexed { idx, leaf ->
|
||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], color, tint, scrambleUV)
|
||||
}.toTypedArray()
|
||||
|
||||
fun Iterable<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.config.CACTUS_BLOCKS
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
@@ -17,31 +18,25 @@ import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.horizontalDirections
|
||||
import mods.betterfoliage.util.idx
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.util.Direction.DOWN
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.util.Random
|
||||
|
||||
object StandardCactusDiscovery : AbstractModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
if (model is BlockModel && ctx.blockState.block in CACTUS_BLOCKS) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardCactusKey)
|
||||
ctx.sprites.add(StandardCactusModel.cactusCrossSprite)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
object StandardCactusDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
ctx.addReplacement(StandardCactusKey)
|
||||
ctx.sprites.add(StandardCactusModel.cactusCrossSprite)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,22 +44,30 @@ object StandardCactusKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped)
|
||||
}
|
||||
|
||||
class CactusRenderData(val armSide: Int, val armIdx: Int, val crossIdx: Int)
|
||||
|
||||
class StandardCactusModel(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any = when {
|
||||
!Config.enabled || !Config.cactus.enabled -> Unit
|
||||
else -> CactusRenderData(
|
||||
armSide = random.nextInt() and 3,
|
||||
armIdx = random.idx(cactusArmModels),
|
||||
crossIdx = random.idx(cactusCrossModels)
|
||||
)
|
||||
}
|
||||
|
||||
val armLighting = horizontalDirections.map { LightingPreferredFace(it) }.toTypedArray()
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
ctx.checkSides = false
|
||||
super.render(ctx, noDecorations)
|
||||
if (!Config.enabled || !Config.cactus.enabled) return
|
||||
|
||||
val armSide = ctx.random.nextInt() and 3
|
||||
ctx.vertexLighter = armLighting[armSide]
|
||||
ctx.renderQuads(cactusArmModels[armSide][ctx.random])
|
||||
ctx.vertexLighter = RoundLeafLighting
|
||||
ctx.renderQuads(cactusCrossModels[ctx.random])
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (data is CactusRenderData) {
|
||||
ctx.vertexLighter = armLighting[data.armSide]
|
||||
ctx.renderQuads(cactusArmModels[data.armSide][data.armIdx])
|
||||
ctx.vertexLighter = RoundLeafLighting
|
||||
ctx.renderQuads(cactusCrossModels[data.crossIdx])
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -72,19 +75,19 @@ class StandardCactusModel(
|
||||
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx")
|
||||
}
|
||||
val cactusArmModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val cactusArmModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = Config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
|
||||
val models = tuftModelSet(shapes, -1) { cactusArmSprites[randomI()] }
|
||||
val models = tuftModelSet(shapes, Color.white, -1) { cactusArmSprites[randomI()] }
|
||||
horizontalDirections.map { side ->
|
||||
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
|
||||
}.toTypedArray()
|
||||
}
|
||||
val cactusCrossModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val cactusCrossModels by BetterFoliage.modelManager.lazy {
|
||||
val models = Config.cactus.let { config ->
|
||||
crossModelsRaw(64, config.size, 0.0, 0.0)
|
||||
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
|
||||
}
|
||||
crossModelsTextured(models, -1, true) { cactusCrossSprite }
|
||||
crossModelsTextured(models, Color.white, -1, true) { cactusCrossSprite }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,47 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.DIRT_BLOCKS
|
||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.config.isSnow
|
||||
import mods.betterfoliage.integration.ShadersModIntegration
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.pipeline.Layers
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.render.pipeline.extendLayers
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.resource.generated.CenteredSprite
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.getBlockModel
|
||||
import mods.betterfoliage.util.idxOrNull
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.material.Material
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.RenderTypeLookup
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.world.biome.Biome
|
||||
import java.util.Random
|
||||
|
||||
object StandardDirtDiscovery : AbstractModelDiscovery() {
|
||||
fun canRenderInLayer(layer: RenderType) = when {
|
||||
!Config.enabled -> layer == RenderType.getSolid()
|
||||
!Config.connectedGrass.enabled && !Config.algae.enabled && !Config.reed.enabled -> layer == RenderType.getSolid()
|
||||
else -> layer == RenderType.getCutoutMipped()
|
||||
}
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is BlockModel && 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 StandardDirtDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardDirtKey)
|
||||
ctx.blockState.block.extendLayers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,45 +49,77 @@ object StandardDirtKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardDirtModel(wrapped)
|
||||
}
|
||||
|
||||
class DirtRenderData(
|
||||
val connectedGrassModel: SpecialRenderModel?,
|
||||
val algaeIdx: Int?,
|
||||
val reedIdx: Int?
|
||||
) : SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = when {
|
||||
connectedGrassModel != null && layer == Layers.connectedDirt -> true
|
||||
(algaeIdx != null || reedIdx != null) && layer == Layers.tufts -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
class StandardDirtModel(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
val vanillaTuftLighting = LightingPreferredFace(UP)
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations)
|
||||
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
val stateUp = ctx.state(UP)
|
||||
val state2Up = ctx.state(Int3(0, 2, 0))
|
||||
val isConnectedGrass = Config.connectedGrass.enabled && stateUp in BetterFoliage.blockTypes.grass
|
||||
if (isConnectedGrass) {
|
||||
(ctx.blockModelShapes.getModel(stateUp) as? SpecialRenderModel)?.let { grassModel ->
|
||||
ctx.renderMasquerade(UP.offset) {
|
||||
grassModel.render(ctx, true)
|
||||
}
|
||||
return
|
||||
}
|
||||
return super.render(ctx, false)
|
||||
}
|
||||
|
||||
super.render(ctx, false)
|
||||
val isConnectedGrass = Config.connectedGrass.enabled &&
|
||||
stateUp in BetterFoliage.blockTypes.grass &&
|
||||
(Config.connectedGrass.snowEnabled || !state2Up.isSnow)
|
||||
|
||||
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 isDeepWater = isWater && state2Up.material == Material.WATER
|
||||
val isShallowWater = isWater && state2Up.isAir
|
||||
val isSaltWater = isWater && ctx.biome?.biomeCategory in SALTWATER_BIOMES
|
||||
|
||||
if (Config.algae.enabled(ctx.random) && isDeepWater) {
|
||||
ctx.vertexLighter = vanillaTuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
|
||||
ctx.renderQuads(algaeModels[ctx.random])
|
||||
}
|
||||
} else if (Config.reed.enabled(ctx.random) && isShallowWater && !isSaltWater) {
|
||||
ctx.vertexLighter = vanillaTuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.reed.shaderWind) {
|
||||
ctx.renderQuads(reedModels[ctx.random])
|
||||
// get the actual grass model to use for connected grass rendering
|
||||
// return null if the grass specifically does not want to connect
|
||||
val connectedGrassModel = if (!isConnectedGrass) null else getBlockModel(stateUp).let { model ->
|
||||
(model as? SpecialRenderModel)?.resolve(random)?.let { grassModel ->
|
||||
if ((grassModel as? StandardGrassModel)?.key?.noConnect == true) null else grassModel
|
||||
}
|
||||
}
|
||||
|
||||
return DirtRenderData(
|
||||
connectedGrassModel = connectedGrassModel,
|
||||
algaeIdx = random.idxOrNull(algaeModels) { Config.algae.enabled(random) && isDeepWater && isSaltWater },
|
||||
reedIdx = random.idxOrNull(reedModels) { Config.reed.enabled(random) && isShallowWater && !isSaltWater }
|
||||
)
|
||||
}
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
if (data is DirtRenderData) {
|
||||
if (data.connectedGrassModel != null) {
|
||||
ctx.renderMasquerade(UP.offset) {
|
||||
data.connectedGrassModel.renderLayer(ctx, ctx.state(UP), layer)
|
||||
}
|
||||
} else {
|
||||
// render non-connected grass
|
||||
super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
|
||||
if (layer == Layers.tufts) {
|
||||
data.algaeIdx?.let {
|
||||
ctx.vertexLighter = vanillaTuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
|
||||
ctx.renderQuads(algaeModels[it])
|
||||
}
|
||||
}
|
||||
data.reedIdx?.let {
|
||||
ctx.vertexLighter = vanillaTuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.reed.shaderWind) {
|
||||
ctx.renderQuads(reedModels[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
} else super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -107,13 +131,13 @@ class StandardDirtModel(
|
||||
idFunc = { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_reed_$idx") },
|
||||
idRegister = { id -> CenteredSprite(id, aspectHeight = 2).register(BetterFoliage.generatedPack) }
|
||||
)
|
||||
val algaeModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val algaeModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = Config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, -1) { algaeSprites[randomI()] }.buildTufts()
|
||||
tuftModelSet(shapes, Color.white, -1) { algaeSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
val reedModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val reedModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = Config.reed.let { tuftShapeSet(2.0, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, -1) { reedSprites[randomI()] }.buildTufts()
|
||||
tuftModelSet(shapes, Color.white, -1) { reedSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,14 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.isSnow
|
||||
import mods.betterfoliage.integration.ShadersModIntegration
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
@@ -16,53 +17,68 @@ import mods.betterfoliage.model.fullCubeTextured
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.pipeline.Layers
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||
import mods.betterfoliage.render.pipeline.extendLayers
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||
import mods.betterfoliage.util.averageColor
|
||||
import mods.betterfoliage.util.colorOverride
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.logColorOverride
|
||||
import mods.betterfoliage.util.averageHSB
|
||||
import mods.betterfoliage.util.idxOrNull
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.lazyMap
|
||||
import mods.betterfoliage.util.brighten
|
||||
import mods.betterfoliage.util.logTextureColor
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.util.Direction.DOWN
|
||||
import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import java.util.Random
|
||||
|
||||
object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||
ctx.addReplacement(StandardGrassKey(textureMatch[0], null))
|
||||
object StandardGrassDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
val texture = params.location("texture") ?: return
|
||||
val tint = params.int("tint") ?: -1
|
||||
val color = Atlas.BLOCKS.file(texture).averageHSB.let {
|
||||
detailLogger.logTextureColor(INFO, "grass texture \"$texture\"", it)
|
||||
it.brighten().asColor
|
||||
}
|
||||
val noConnect = params["no-connect"] == "true"
|
||||
ctx.addReplacement(StandardGrassKey(texture, tint, color, noConnect))
|
||||
BetterFoliage.blockTypes.grass.add(ctx.blockState)
|
||||
ctx.blockState.block.extendLayers()
|
||||
}
|
||||
}
|
||||
|
||||
data class StandardGrassKey(
|
||||
val grassLocation: ResourceLocation,
|
||||
val overrideColor: Color?
|
||||
val sprite: ResourceLocation,
|
||||
val tintIndex: Int,
|
||||
val avgColor: Color,
|
||||
val noConnect: Boolean
|
||||
) : HalfBakedWrapperKey() {
|
||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
||||
logColorOverride(BetterFoliageMod.detailLogger(this), Config.shortGrass.saturationThreshold, hsb)
|
||||
hsb.colorOverride(Config.shortGrass.saturationThreshold)
|
||||
}
|
||||
return StandardGrassModel(wrapped, this.copy(overrideColor = grassSpriteColor))
|
||||
return StandardGrassModel(wrapped, this)
|
||||
}
|
||||
}
|
||||
|
||||
class GrassRenderData(
|
||||
val isSnowed: Boolean,
|
||||
val connectedGrassIdx: Int?,
|
||||
val tuftIdx: Int?
|
||||
): SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = when {
|
||||
connectedGrassIdx != null && layer == Layers.connectedGrass -> true
|
||||
tuftIdx != null && layer == Layers.tufts -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
class StandardGrassModel(
|
||||
wrapped: SpecialRenderModel,
|
||||
key: StandardGrassKey
|
||||
val key: StandardGrassKey
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
|
||||
val tuftNormal by grassTuftMeshesNormal.delegate(key)
|
||||
@@ -70,48 +86,62 @@ class StandardGrassModel(
|
||||
val fullBlock by grassFullBlockMeshes.delegate(key)
|
||||
val tuftLighting = LightingPreferredFace(UP)
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations)
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
|
||||
val stateBelow = ctx.state(DOWN)
|
||||
val stateAbove = ctx.state(UP)
|
||||
val isAir = ctx.isAir(UP)
|
||||
val isSnowed = stateAbove.isSnow
|
||||
val connected = Config.connectedGrass.enabled &&
|
||||
val connected = !key.noConnect && Config.connectedGrass.enabled &&
|
||||
(!isSnowed || Config.connectedGrass.snowEnabled) &&
|
||||
BetterFoliage.blockTypes.run { stateBelow in grass || stateBelow in dirt }
|
||||
|
||||
if (connected) {
|
||||
ctx.renderQuads(if (isSnowed) snowFullBlockMeshes[ctx.random] else fullBlock[ctx.random])
|
||||
} else {
|
||||
super.render(ctx, noDecorations)
|
||||
}
|
||||
|
||||
if (Config.shortGrass.enabled(ctx.random) && (isAir || isSnowed)) {
|
||||
ctx.vertexLighter = tuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
|
||||
ctx.renderQuads(if (isSnowed) tuftSnowed[ctx.random] else tuftNormal[ctx.random])
|
||||
return GrassRenderData(
|
||||
isSnowed = isSnowed,
|
||||
connectedGrassIdx = random.idxOrNull(if (isSnowed) snowFullBlockMeshes else fullBlock) { connected },
|
||||
tuftIdx = random.idxOrNull(if (isSnowed) tuftSnowed else tuftNormal) {
|
||||
Config.shortGrass.enabled(random) &&
|
||||
Config.shortGrass.grassEnabled &&
|
||||
(isAir || isSnowed)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
if (data is GrassRenderData) {
|
||||
if (data.connectedGrassIdx != null) {
|
||||
if (layer == Layers.connectedGrass)
|
||||
ctx.renderQuads((if (data.isSnowed) snowFullBlockMeshes else fullBlock)[data.connectedGrassIdx])
|
||||
} else {
|
||||
super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
if (data.tuftIdx != null && layer == Layers.tufts) {
|
||||
ctx.vertexLighter = tuftLighting
|
||||
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
|
||||
ctx.renderQuads((if (data.isSnowed) tuftSnowed else tuftNormal)[data.tuftIdx])
|
||||
}
|
||||
}
|
||||
} else super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val grassTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx")
|
||||
}
|
||||
val grassTuftShapes by LazyInvalidatable(BakeWrapperManager) {
|
||||
val grassTuftShapes by BetterFoliage.modelManager.lazy {
|
||||
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
}
|
||||
val grassTuftMeshesNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes, key.tintIndex) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||
val grassTuftMeshesNormal = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes, key.avgColor, key.tintIndex) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
val grassTuftMeshesSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes, -1) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||
val grassTuftMeshesSnowed = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes, Color.white, -1) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
val grassFullBlockMeshes = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
Array(64) { fullCubeTextured(key.grassLocation, key.tintIndex) }
|
||||
val grassFullBlockMeshes = BetterFoliage.modelManager.lazyMap { key: StandardGrassKey ->
|
||||
Array(64) { fullCubeTextured(key.sprite, key.tintIndex) }
|
||||
}
|
||||
val snowFullBlockMeshes by LazyInvalidatable(BakeWrapperManager) {
|
||||
val snowFullBlockMeshes by BetterFoliage.modelManager.lazy {
|
||||
Array(64) { fullCubeTextured(ResourceLocation("block/snow"), -1) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.isSnow
|
||||
import mods.betterfoliage.integration.ShadersModIntegration
|
||||
@@ -18,50 +17,49 @@ import mods.betterfoliage.render.particle.LeafBlockModel
|
||||
import mods.betterfoliage.render.particle.LeafParticleKey
|
||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.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.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.resource.generated.GeneratedLeafSprite
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||
import mods.betterfoliage.util.averageColor
|
||||
import mods.betterfoliage.util.colorOverride
|
||||
import mods.betterfoliage.util.logColorOverride
|
||||
import mods.betterfoliage.util.averageHSB
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.lazyMap
|
||||
import mods.betterfoliage.util.brighten
|
||||
import mods.betterfoliage.util.logTextureColor
|
||||
import mods.betterfoliage.util.saturate
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
|
||||
object StandardLeafDiscovery : ConfigurableModelDiscovery() {
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||
val leafType = LeafParticleRegistry.typeMappings.getType(textureMatch[0]) ?: "default"
|
||||
val generated = GeneratedLeafSprite(textureMatch[0], leafType)
|
||||
object StandardLeafDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
val texture = params.location("texture") ?: return
|
||||
val tint = params.int("tint") ?: -1
|
||||
val color = Atlas.BLOCKS.file(texture).averageHSB.let {
|
||||
detailLogger.logTextureColor(INFO, "leaf texture \"$texture\"", it)
|
||||
it.brighten().asColor
|
||||
}
|
||||
val leafType = params["particle"] ?: "default"
|
||||
val generated = GeneratedLeafSprite(texture, leafType)
|
||||
.register(BetterFoliage.generatedPack)
|
||||
.apply { ctx.sprites.add(this) }
|
||||
|
||||
detailLogger.log(INFO, " particle $leafType")
|
||||
ctx.addReplacement(StandardLeafKey(generated, leafType, null))
|
||||
ctx.addReplacement(StandardLeafKey(generated, leafType, tint, color))
|
||||
BetterFoliage.blockTypes.leaf.add(ctx.blockState)
|
||||
}
|
||||
}
|
||||
|
||||
data class StandardLeafKey(
|
||||
val roundLeafTexture: ResourceLocation,
|
||||
override val leafType: String,
|
||||
override val overrideColor: Color?
|
||||
override val tintIndex: Int,
|
||||
override val avgColor: Color
|
||||
) : HalfBakedWrapperKey(), LeafParticleKey {
|
||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
||||
logColorOverride(BetterFoliageMod.detailLogger(this), Config.leaves.saturationThreshold, hsb)
|
||||
hsb.colorOverride(Config.leaves.saturationThreshold)
|
||||
}
|
||||
return StandardLeafModel(wrapped, this.copy(overrideColor = leafSpriteColor))
|
||||
return StandardLeafModel(wrapped, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +70,15 @@ class StandardLeafModel(
|
||||
val leafNormal by leafModelsNormal.delegate(key)
|
||||
val leafSnowed by leafModelsSnowed.delegate(key)
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
ShadersModIntegration.leaves(ctx, true) {
|
||||
super.render(ctx, noDecorations)
|
||||
if (!Config.enabled || !Config.leaves.enabled || noDecorations) return
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (!Config.enabled || !Config.leaves.enabled) return
|
||||
|
||||
ctx.vertexLighter = RoundLeafLightingPreferUp
|
||||
val leafIdx = ctx.random.nextInt(64)
|
||||
ctx.renderQuads(leafNormal[leafIdx])
|
||||
if (ctx.state(UP).isSnow) ctx.renderQuads(leafSnowed[leafIdx])
|
||||
if (Config.leaves.snowEnabled && ctx.state(UP).isSnow) ctx.renderQuads(leafSnowed[leafIdx])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,14 +86,16 @@ class StandardLeafModel(
|
||||
val leafSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_leaves_snowed_$idx")
|
||||
}
|
||||
val leafModelsBase = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
val leafModelsBase by BetterFoliage.modelManager.lazy {
|
||||
Config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
||||
}
|
||||
val leafModelsNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], key.tintIndex, true) { key.roundLeafTexture }
|
||||
val leafModelsNormal = BetterFoliage.modelManager.lazyMap { key: StandardLeafKey ->
|
||||
// generated leaf textures naturally carry the color of their source textures
|
||||
// no need to color the quad a second time
|
||||
crossModelsTextured(leafModelsBase, Color.white, key.tintIndex, true) { key.roundLeafTexture }
|
||||
}
|
||||
val leafModelsSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], -1, false) { leafSpritesSnowed[it].name }
|
||||
val leafModelsSnowed = BetterFoliage.modelManager.lazyMap { key: StandardLeafKey ->
|
||||
crossModelsTextured(leafModelsBase, Color.white, -1, false) { leafSpritesSnowed[it].name }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.LILYPAD_BLOCKS
|
||||
import mods.betterfoliage.integration.ShadersModIntegration
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
@@ -13,27 +15,21 @@ import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import mods.betterfoliage.util.idx
|
||||
import mods.betterfoliage.util.idxOrNull
|
||||
import mods.betterfoliage.util.lazy
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.util.Direction.DOWN
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.util.Random
|
||||
|
||||
object StandardLilypadDiscovery : AbstractModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in LILYPAD_BLOCKS) {
|
||||
ctx.addReplacement(StandardLilypadKey)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
object StandardLilypadDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
ctx.addReplacement(StandardLilypadKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,18 +37,32 @@ object StandardLilypadKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped)
|
||||
}
|
||||
|
||||
class LilypadRenderData(
|
||||
val rootIdx: Int,
|
||||
val flowerIdx: Int?
|
||||
)
|
||||
|
||||
class StandardLilypadModel(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
ctx.checkSides = false
|
||||
super.render(ctx, noDecorations)
|
||||
if (!Config.enabled || !Config.lilypad.enabled) return
|
||||
|
||||
ShadersModIntegration.grass(ctx, Config.lilypad.shaderWind) {
|
||||
ctx.renderQuads(lilypadRootModels[ctx.random])
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
return LilypadRenderData(
|
||||
rootIdx = random.idx(lilypadRootModels),
|
||||
flowerIdx = random.idxOrNull(lilypadFlowerModels) { Config.lilypad.enabled(random) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
ctx.checkSides = false
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (data is LilypadRenderData) {
|
||||
data.flowerIdx?.let { ctx.renderQuads(lilypadFlowerModels[it]) }
|
||||
ShadersModIntegration.grass(ctx, Config.lilypad.shaderWind) {
|
||||
ctx.renderQuads(lilypadRootModels[data.rootIdx])
|
||||
}
|
||||
}
|
||||
if (Config.lilypad.enabled(ctx.random)) ctx.renderQuads(lilypadFlowerModels[ctx.random])
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -62,15 +72,15 @@ class StandardLilypadModel(
|
||||
val lilypadFlowerSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_flower_$idx")
|
||||
}
|
||||
val lilypadRootModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val lilypadRootModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = tuftShapeSet(1.0, 1.0, 1.0, Config.lilypad.hOffset)
|
||||
tuftModelSet(shapes, -1) { lilypadRootSprites[it] }
|
||||
tuftModelSet(shapes, Color.white, -1) { lilypadRootSprites[it] }
|
||||
.transform { move(2.0 to DOWN) }
|
||||
.buildTufts()
|
||||
}
|
||||
val lilypadFlowerModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val lilypadFlowerModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = tuftShapeSet(0.5, 0.5, 0.5, Config.lilypad.hOffset)
|
||||
tuftModelSet(shapes, -1) { lilypadFlowerSprites[it] }
|
||||
tuftModelSet(shapes, Color.white, -1) { lilypadFlowerSprites[it] }
|
||||
.transform { move(1.0 to DOWN) }
|
||||
.buildTufts()
|
||||
}
|
||||
|
||||
@@ -1,62 +1,87 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.MYCELIUM_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.pipeline.Layers
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.render.pipeline.extendLayers
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.averageHSB
|
||||
import mods.betterfoliage.util.idxOrNull
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.lazyMap
|
||||
import mods.betterfoliage.util.brighten
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.RenderTypeLookup
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.util.Random
|
||||
|
||||
object StandardMyceliumDiscovery : AbstractModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in MYCELIUM_BLOCKS) {
|
||||
ctx.addReplacement(StandardMyceliumKey)
|
||||
RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutout())
|
||||
}
|
||||
super.processModel(ctx)
|
||||
object StandardMyceliumDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
val texture = params.location("texture") ?: return
|
||||
val tint = params.int("tint") ?: -1
|
||||
val color = Atlas.BLOCKS.file(texture).averageHSB.brighten(multiplier = 1.5f).asColor
|
||||
ctx.addReplacement(StandardMyceliumKey(texture, tint, color))
|
||||
ctx.blockState.block.extendLayers()
|
||||
}
|
||||
}
|
||||
|
||||
object StandardMyceliumKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardMyceliumModel(wrapped)
|
||||
data class StandardMyceliumKey(
|
||||
val sprite: ResourceLocation,
|
||||
val tintIndex: Int,
|
||||
val avgColor: Color,
|
||||
) : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||
return StandardMyceliumModel(wrapped, this)
|
||||
}
|
||||
}
|
||||
|
||||
class MyceliumRenderData(
|
||||
val tuftIndex: Int?
|
||||
) : SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts
|
||||
}
|
||||
|
||||
class StandardMyceliumModel(
|
||||
wrapped: SpecialRenderModel
|
||||
wrapped: SpecialRenderModel,
|
||||
key: StandardMyceliumKey
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
|
||||
val tuftModels by myceliumTuftModels.delegate(key)
|
||||
val tuftLighting = LightingPreferredFace(Direction.UP)
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
super.render(ctx, noDecorations)
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
return MyceliumRenderData(
|
||||
random.idxOrNull(tuftModels) {
|
||||
Config.shortGrass.enabled(random) &&
|
||||
Config.shortGrass.myceliumEnabled &&
|
||||
ctx.state(Direction.UP).isAir(ctx.world, ctx.pos)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (Config.shortGrass.enabled &&
|
||||
Config.shortGrass.myceliumEnabled &&
|
||||
Config.shortGrass.enabled(ctx.random) &&
|
||||
ctx.state(Direction.UP).isAir(ctx.world, ctx.pos)
|
||||
) {
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (data is MyceliumRenderData && data.tuftIndex != null && layer == Layers.tufts) {
|
||||
ctx.vertexLighter = tuftLighting
|
||||
ctx.renderQuads(myceliumTuftModels[ctx.random])
|
||||
ctx.renderQuads(tuftModels[data.tuftIndex])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +89,11 @@ class StandardMyceliumModel(
|
||||
val myceliumTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_mycel_$idx")
|
||||
}
|
||||
val myceliumTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, -1) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
|
||||
val myceliumTuftShapes by BetterFoliage.modelManager.lazy {
|
||||
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
}
|
||||
val myceliumTuftModels = BetterFoliage.modelManager.lazyMap { key: StandardMyceliumKey ->
|
||||
tuftModelSet(myceliumTuftShapes, key.avgColor, key.tintIndex) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.NETHERRACK_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
@@ -13,37 +15,26 @@ import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.pipeline.Layers
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.render.pipeline.extendLayers
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.idxOrNull
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.RenderTypeLookup
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.Direction.DOWN
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.util.Random
|
||||
|
||||
object StandardNetherrackDiscovery : AbstractModelDiscovery() {
|
||||
fun canRenderInLayer(layer: RenderType) = when {
|
||||
!Config.enabled -> layer == RenderType.getSolid()
|
||||
!Config.netherrack.enabled -> layer == RenderType.getSolid()
|
||||
else -> layer == RenderType.getCutoutMipped()
|
||||
}
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is BlockModel && 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 StandardNetherrackDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
ctx.addReplacement(StandardNetherrackKey)
|
||||
ctx.blockState.block.extendLayers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,19 +42,33 @@ object StandardNetherrackKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardNetherrackModel(wrapped)
|
||||
}
|
||||
|
||||
class NetherrackRenderData(
|
||||
val tuftIndex: Int?
|
||||
): SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts
|
||||
}
|
||||
|
||||
class StandardNetherrackModel(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
|
||||
val tuftLighting = LightingPreferredFace(DOWN)
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
super.render(ctx, noDecorations)
|
||||
if (!Config.enabled || !Config.netherrack.enabled) return
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
return NetherrackRenderData(
|
||||
random.idxOrNull(netherrackTuftModels) {
|
||||
Config.netherrack.enabled &&
|
||||
ctx.isAir(DOWN)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (ctx.isAir(DOWN)) {
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (data is NetherrackRenderData && data.tuftIndex != null && layer == Layers.tufts) {
|
||||
ctx.vertexLighter = tuftLighting
|
||||
ctx.renderQuads(netherrackTuftModels[ctx.random])
|
||||
ctx.renderQuads(netherrackTuftModels[data.tuftIndex])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +76,9 @@ class StandardNetherrackModel(
|
||||
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_netherrack_$idx")
|
||||
}
|
||||
val netherrackTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val netherrackTuftModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = Config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, -1) { netherrackTuftSprites[randomI()] }
|
||||
tuftModelSet(shapes, Color.white, -1) { netherrackTuftSprites[randomI()] }
|
||||
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
|
||||
.buildTufts()
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.ACCEPTED_ROUND_LOG_MATERIALS
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.render.column.ColumnBlockKey
|
||||
import mods.betterfoliage.render.column.ColumnMeshSet
|
||||
import mods.betterfoliage.render.column.ColumnModelBase
|
||||
import mods.betterfoliage.render.column.ColumnRenderLayer
|
||||
import mods.betterfoliage.resource.discovery.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.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||
import mods.betterfoliage.util.lazyMap
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.LogBlock
|
||||
import net.minecraft.block.RotatedPillarBlock
|
||||
import net.minecraft.util.Direction.Axis
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
@@ -37,19 +34,20 @@ object RoundLogOverlayLayer : ColumnRenderLayer() {
|
||||
override val defaultToY: Boolean get() = Config.roundLogs.defaultY
|
||||
}
|
||||
|
||||
object StandardRoundLogDiscovery : ConfigurableModelDiscovery() {
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||
object StandardRoundLogDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
val barkSprite = params.location("texture-side") ?: return
|
||||
val endSprite = params.location("texture-end") ?: return
|
||||
val axis = getAxis(ctx.blockState)
|
||||
detailLogger.log(INFO, " axis $axis")
|
||||
ctx.addReplacement(StandardRoundLogKey(axis, textureMatch[0], textureMatch[1]))
|
||||
|
||||
detailLogger.log(INFO, " axis $axis, material ${ctx.blockState.material}")
|
||||
if (!Config.roundLogs.plantsOnly || ctx.blockState.material in ACCEPTED_ROUND_LOG_MATERIALS)
|
||||
ctx.addReplacement(StandardRoundLogKey(axis, barkSprite, endSprite))
|
||||
}
|
||||
|
||||
fun getAxis(state: BlockState): Axis? {
|
||||
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
|
||||
state.values.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
|
||||
val axis = tryDefault(null) { state.getValue(RotatedPillarBlock.AXIS).toString() } ?:
|
||||
state.values.entries.find { it.key.name.toLowerCase() == "axis" }?.value?.toString()
|
||||
return when (axis) {
|
||||
"x" -> Axis.X
|
||||
"y" -> Axis.Y
|
||||
@@ -79,7 +77,7 @@ class StandardRoundLogModel(
|
||||
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
|
||||
|
||||
companion object {
|
||||
val modelSets = LazyMapInvalidatable(BakeWrapperManager) { key: StandardRoundLogKey ->
|
||||
val modelSets = BetterFoliage.modelManager.lazyMap { key: StandardRoundLogKey ->
|
||||
val barkSprite = Atlas.BLOCKS[key.barkSprite]
|
||||
val endSprite = Atlas.BLOCKS[key.endSprite]
|
||||
Config.roundLogs.let { config ->
|
||||
|
||||
@@ -2,12 +2,14 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.config.SAND_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||
import mods.betterfoliage.model.Quad
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.bake
|
||||
@@ -16,36 +18,33 @@ import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||
import mods.betterfoliage.render.pipeline.Layers
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.render.pipeline.extendLayers
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ParametrizedModelDiscovery
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.idx
|
||||
import mods.betterfoliage.util.lazy
|
||||
import mods.betterfoliage.util.mapArray
|
||||
import mods.betterfoliage.util.randomB
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.block.material.Material
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.RenderTypeLookup
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.util.Random
|
||||
|
||||
object StandardSandDiscovery : AbstractModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is BlockModel && 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 StandardSandDiscovery : ParametrizedModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>) {
|
||||
ctx.addReplacement(StandardSandKey)
|
||||
ctx.blockState.block.extendLayers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,23 +52,46 @@ object StandardSandKey : HalfBakedWrapperKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped)
|
||||
}
|
||||
|
||||
class SandRenderData(
|
||||
val crustIdx: Array<Int?>,
|
||||
val tuftIdx: Array<Int?>
|
||||
): SpecialRenderData {
|
||||
override fun canRenderInLayer(layer: RenderType) = when {
|
||||
(crustIdx.any { it != null } || tuftIdx.any { it != null }) && layer == Layers.coral -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
class StandardSandModel(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) }
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
super.render(ctx, noDecorations)
|
||||
if (noDecorations || !Config.enabled || !Config.coral.enabled(ctx.random)) return
|
||||
if (ctx.biome?.category !in SALTWATER_BIOMES) return
|
||||
override fun prepare(ctx: BlockCtx, random: Random): Any {
|
||||
if (!Config.enabled) return Unit
|
||||
if (!Config.coral.enabled(random)) return Unit
|
||||
if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return Unit
|
||||
|
||||
allDirections.filter { ctx.random.nextInt(64) < Config.coral.chance }.forEach { face ->
|
||||
val crustIdx = Array<Int?>(6) { null }
|
||||
val tuftIdx = Array<Int?>(6) { null }
|
||||
allDirections.filter { random.nextInt(64) < Config.coral.chance }.forEach { face ->
|
||||
val isWater = ctx.state(face).material == Material.WATER
|
||||
val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER
|
||||
if (isDeepWater) {
|
||||
crustIdx[face.ordinal] = random.idx(coralCrustModels)
|
||||
tuftIdx[face.ordinal] = random.idx(coralTuftModels)
|
||||
}
|
||||
}
|
||||
return SandRenderData(crustIdx, tuftIdx)
|
||||
}
|
||||
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
super.renderLayer(ctx, data, layer)
|
||||
if (data is SandRenderData && layer == Layers.coral) {
|
||||
for (face in 0 until 6) {
|
||||
ctx.vertexLighter = coralLighting[face]
|
||||
ctx.renderQuads(coralCrustModels[face][ctx.random])
|
||||
ctx.renderQuads(coralTuftModels[face][ctx.random])
|
||||
data.crustIdx[face]?.let { ctx.renderQuads(coralCrustModels[face][it]) }
|
||||
data.tuftIdx[face]?.let { ctx.renderQuads(coralTuftModels[face][it]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,15 +103,15 @@ class StandardSandModel(
|
||||
val coralCrustSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_crust_$idx")
|
||||
}
|
||||
val coralTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val coralTuftModels by BetterFoliage.modelManager.lazy {
|
||||
val shapes = Config.coral.let { tuftShapeSet(it.size, 1.0, 1.0, it.hOffset) }
|
||||
allDirections.mapArray { face ->
|
||||
tuftModelSet(shapes, -1) { coralTuftSprites[randomI()] }
|
||||
tuftModelSet(shapes, Color.white, -1) { coralTuftSprites[randomI()] }
|
||||
.transform { rotate(Rotation.fromUp[face]) }
|
||||
.buildTufts()
|
||||
}
|
||||
}
|
||||
val coralCrustModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val coralCrustModels by BetterFoliage.modelManager.lazy {
|
||||
allDirections.map { face ->
|
||||
Array(64) { idx ->
|
||||
listOf(
|
||||
|
||||
@@ -13,6 +13,7 @@ import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantTy
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||
import mods.betterfoliage.render.lighting.ColumnLighting
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.util.Direction.Axis
|
||||
|
||||
abstract class ColumnModelBase(
|
||||
@@ -24,21 +25,21 @@ abstract class ColumnModelBase(
|
||||
abstract val connectPerpendicular: Boolean
|
||||
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
if (!enabled) return super.render(ctx, noDecorations)
|
||||
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
|
||||
if (!enabled) return super.renderLayer(ctx, data, layer)
|
||||
|
||||
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
|
||||
val roundLog = overlayLayer[ctx]
|
||||
when(roundLog) {
|
||||
ColumnLayerData.SkipRender -> return
|
||||
NormalRender -> return super.render(ctx, noDecorations)
|
||||
NormalRender -> return super.renderLayer(ctx, data, layer)
|
||||
ColumnLayerData.ResolveError, null -> {
|
||||
return super.render(ctx, noDecorations)
|
||||
return super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
}
|
||||
|
||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
|
||||
return super.render(ctx, noDecorations)
|
||||
return super.renderLayer(ctx, data, layer)
|
||||
}
|
||||
|
||||
ctx.vertexLighter = ColumnLighting
|
||||
|
||||
@@ -23,7 +23,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.Direction.Axis
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
|
||||
/** Index of SOUTH-EAST quadrant. */
|
||||
const val SE = 0
|
||||
@@ -73,7 +73,7 @@ sealed class ColumnLayerData {
|
||||
object ResolveError : ColumnLayerData()
|
||||
}
|
||||
|
||||
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData>() {
|
||||
|
||||
abstract val connectSolids: Boolean
|
||||
abstract val lenientConnect: Boolean
|
||||
@@ -83,13 +83,13 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||
|
||||
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
|
||||
|
||||
override fun onBlockUpdate(world: ILightReader, pos: BlockPos) {
|
||||
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(world.dimType, this, pos + offset) }
|
||||
override fun onBlockUpdate(world: IBlockDisplayReader, pos: BlockPos) {
|
||||
allNeighborOffsets.forEach { offset -> remove(world, pos + offset) }
|
||||
}
|
||||
|
||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
||||
// TODO detect round logs
|
||||
if (allDirections.all { dir -> ctx.offset(dir).let { it.isNormalCube } }) return ColumnLayerData.SkipRender
|
||||
if (allDirections.all { dir -> ctx.offset(dir).let { it.isFullBlock } }) return ColumnLayerData.SkipRender
|
||||
val columnTextures = getColumnKey(ctx.state) ?: return ColumnLayerData.ResolveError
|
||||
|
||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||
@@ -185,7 +185,7 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||
val offsetRot = offset.rotate(rotation)
|
||||
val key = getColumnKey(state(offsetRot))
|
||||
return if (key == null) {
|
||||
if (offset(offsetRot).isNormalCube) SOLID else NONSOLID
|
||||
if (offset(offsetRot).isFullBlock) SOLID else NONSOLID
|
||||
} else {
|
||||
(key.axis ?: if (Config.roundLogs.defaultY) Axis.Y else null)?.let {
|
||||
if (it == axis) PARALLEL else PERPENDICULAR
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.BlockModelRenderer
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
|
||||
data class LightingData(
|
||||
@JvmField var packedLight: Int = 0,
|
||||
@@ -33,7 +33,7 @@ const val OCCLUSION_OFFSET_FIX = true
|
||||
* Not thread-safe, always use a [ThreadLocal] instance
|
||||
*/
|
||||
class VanillaAoCalculator {
|
||||
lateinit var world: ILightReader
|
||||
lateinit var world: IBlockDisplayReader
|
||||
|
||||
/** [blockPos] is used to get block-related information (i.e. tint, opacity, etc.)
|
||||
* [lightPos] is used to get light-related information
|
||||
@@ -41,7 +41,7 @@ class VanillaAoCalculator {
|
||||
lateinit var blockPos: BlockPos
|
||||
lateinit var lightPos: BlockPos
|
||||
|
||||
private val probe = LightProbe(BlockModelRenderer.CACHE_COMBINED_LIGHT.get())
|
||||
private val probe = LightProbe(BlockModelRenderer.CACHE.get())
|
||||
|
||||
val isValid = BooleanArray(6)
|
||||
val aoData = Array(24) { LightingData() }
|
||||
@@ -74,13 +74,13 @@ class VanillaAoCalculator {
|
||||
|
||||
// Bit 0 of the bitset in vanilla calculations
|
||||
// true if the block model is planar with the block boundary
|
||||
val isFullBlock = forceFull ?: world.getBlockState(blockPos).isCollisionShapeOpaque(world, blockPos)
|
||||
val isFullBlock = forceFull ?: world.getBlockState(blockPos).isCollisionShapeFullBlock(world, blockPos)
|
||||
|
||||
val lightOrigin = if (isFullBlock) lightPos.offset(lightFace) else lightPos
|
||||
val lightOrigin = if (isFullBlock) lightPos.relative(lightFace) else lightPos
|
||||
|
||||
// AO calculation for the face center
|
||||
probe.position { setPos(lightOrigin) }.writeTo(centerAo)
|
||||
if (!isFullBlock && !probe.position { move(lightFace) }.state.isOpaqueCube(world, probe.pos)) {
|
||||
probe.position { set(lightOrigin) }.writeTo(centerAo)
|
||||
if (!isFullBlock && !probe.position { move(lightFace) }.state.isSolidRender(world, probe.pos)) {
|
||||
// if the neighboring block in the lightface direction is
|
||||
// transparent (non-opaque), use its packed light instead of our own
|
||||
// (if our block is a full block, we are already using this value)
|
||||
@@ -90,8 +90,9 @@ class VanillaAoCalculator {
|
||||
// AO calculation for the 4 sides
|
||||
sideHelper.sides.forEachIndexed { sideIdx, sideDir ->
|
||||
// record light data in the block 1 step to the side
|
||||
probe.position { setPos(lightOrigin).move(sideDir) }.writeTo(sideAo[sideIdx])
|
||||
// side is considered occluded if the block 1 step to that side is not fully transparent
|
||||
probe.position { set(lightOrigin).move(sideDir) }.writeTo(sideAo[sideIdx])
|
||||
// side is considered occluded if the block 1 step to that side and
|
||||
// 1 step forward (in the lightface direction) is not fully transparent
|
||||
if (!OCCLUSION_OFFSET_FIX) probe.position { move(lightFace) }
|
||||
isOccluded[sideIdx] = probe.isNonTransparent
|
||||
}
|
||||
@@ -107,7 +108,7 @@ class VanillaAoCalculator {
|
||||
else {
|
||||
// lookup actual packed light from the cornering block in the world
|
||||
probe.position {
|
||||
setPos(lightOrigin)
|
||||
set(lightOrigin)
|
||||
.move(sideHelper.sides[sideIndices.first])
|
||||
.move(sideHelper.sides[sideIndices.second])
|
||||
}.writeTo(cornerAo[cornerIdx])
|
||||
@@ -133,9 +134,9 @@ class VanillaAoCalculator {
|
||||
lateinit var state: BlockState
|
||||
val pos = BlockPos.Mutable()
|
||||
|
||||
val packedLight: Int get() = cache.getPackedLight(state, world, pos)
|
||||
val colorMultiplier: Float get() = cache.getBrightness(state, world, pos)
|
||||
val isNonTransparent: Boolean get() = state.getOpacity(world, pos) > 0
|
||||
val packedLight: Int get() = cache.getLightColor(state, world, pos)
|
||||
val colorMultiplier: Float get() = cache.getShadeBrightness(state, world, pos)
|
||||
val isNonTransparent: Boolean get() = state.getLightBlock(world, pos) > 0
|
||||
|
||||
fun writeTo(data: LightingData) {
|
||||
data.packedLight = packedLight
|
||||
|
||||
@@ -69,7 +69,7 @@ object VanillaFullBlockLighting : VanillaVertexLighter() {
|
||||
lighting.calc.fillLightData(face, true)
|
||||
lighting.updateWithCornerAo(quad) { nearestCornerOnFace(it, face) }
|
||||
lighting.updateBlockTint(quad.baked.tintIndex)
|
||||
if (quad.baked.shouldApplyDiffuseLighting()) lighting.applyDiffuseLighting(face)
|
||||
if (quad.baked.isShade) lighting.applyDiffuseLighting(face)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package mods.betterfoliage.render.particle
|
||||
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HSB
|
||||
import mods.betterfoliage.util.Double3
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.particle.SpriteTexturedParticle
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||
import net.minecraft.client.renderer.Vector3f
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.util.math.vector.Vector3f
|
||||
|
||||
abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) : SpriteTexturedParticle(world, x, y, z) {
|
||||
abstract class AbstractParticle(world: ClientWorld, x: Double, y: Double, z: Double) : SpriteTexturedParticle(world, x, y, z) {
|
||||
|
||||
companion object {
|
||||
// @JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
||||
@@ -24,12 +26,12 @@ abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) :
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
currentPos.setTo(posX, posY, posZ)
|
||||
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
||||
velocity.setTo(motionX, motionY, motionZ)
|
||||
currentPos.setTo(x, y, z)
|
||||
prevPos.setTo(xo, yo, zo)
|
||||
velocity.setTo(xd, yd, zd)
|
||||
update()
|
||||
posX = currentPos.x; posY = currentPos.y; posZ = currentPos.z;
|
||||
motionX = velocity.x; motionY = velocity.y; motionZ = velocity.z;
|
||||
x = currentPos.x; y = currentPos.y; z = currentPos.z;
|
||||
xd = velocity.x; yd = velocity.y; zd = velocity.z;
|
||||
}
|
||||
|
||||
/** Update particle on world tick. */
|
||||
@@ -39,10 +41,10 @@ abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) :
|
||||
abstract val isValid: Boolean
|
||||
|
||||
/** Add the particle to the effect renderer if it is valid. */
|
||||
fun addIfValid() { if (isValid) Minecraft.getInstance().particles.addEffect(this) }
|
||||
fun addIfValid() { if (isValid) Minecraft.getInstance().particleEngine.add(this) }
|
||||
|
||||
override fun renderParticle(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||
super.renderParticle(vertexBuilder, camera, tickDelta)
|
||||
override fun render(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||
super.render(vertexBuilder, camera, tickDelta)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,39 +65,45 @@ abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) :
|
||||
tickDelta: Float,
|
||||
currentPos: Double3 = this.currentPos,
|
||||
prevPos: Double3 = this.prevPos,
|
||||
size: Double = particleScale.toDouble(),
|
||||
currentAngle: Float = this.particleAngle,
|
||||
prevAngle: Float = this.prevParticleAngle,
|
||||
size: Double = quadSize.toDouble(),
|
||||
currentAngle: Float = this.roll,
|
||||
prevAngle: Float = this.oRoll,
|
||||
sprite: TextureAtlasSprite = this.sprite,
|
||||
alpha: Float = this.particleAlpha) {
|
||||
alpha: Float = this.alpha) {
|
||||
|
||||
val center = Double3.lerp(tickDelta.toDouble(), prevPos, currentPos)
|
||||
val angle = MathHelper.lerp(tickDelta, prevAngle, currentAngle)
|
||||
val rotation = camera.rotation.copy().apply { multiply(Vector3f.ZP.rotation(angle)) }
|
||||
val lightmapCoord = getBrightnessForRender(tickDelta)
|
||||
val rotation = camera.rotation().copy().apply { mul(Vector3f.ZP.rotation(angle)) }
|
||||
val lightmapCoord = getLightColor(tickDelta)
|
||||
|
||||
val coords = arrayOf(
|
||||
Double3(-1.0, -1.0, 0.0),
|
||||
Double3(-1.0, 1.0, 0.0),
|
||||
Double3(1.0, 1.0, 0.0),
|
||||
Double3(1.0, -1.0, 0.0)
|
||||
).map { it.rotate(rotation).mul(size).add(center).sub(camera.projectedView.x, camera.projectedView.y, camera.projectedView.z) }
|
||||
).map { it.rotate(rotation).mul(size).add(center).sub(camera.position.x, camera.position.y, camera.position.z) }
|
||||
|
||||
fun renderVertex(vertex: Double3, u: Float, v: Float) = vertexConsumer
|
||||
.pos(vertex.x, vertex.y, vertex.z).tex(u, v)
|
||||
.color(particleRed, particleGreen, particleBlue, alpha).lightmap(lightmapCoord)
|
||||
.vertex(vertex.x, vertex.y, vertex.z).uv(u, v)
|
||||
.color(rCol, gCol, bCol, alpha).uv2(lightmapCoord)
|
||||
.endVertex()
|
||||
|
||||
renderVertex(coords[0], sprite.maxU, sprite.maxV)
|
||||
renderVertex(coords[1], sprite.maxU, sprite.minV)
|
||||
renderVertex(coords[2], sprite.minU, sprite.minV)
|
||||
renderVertex(coords[3], sprite.minU, sprite.maxV)
|
||||
renderVertex(coords[0], sprite.u1, sprite.v1)
|
||||
renderVertex(coords[1], sprite.u1, sprite.v0)
|
||||
renderVertex(coords[2], sprite.u0, sprite.v0)
|
||||
renderVertex(coords[3], sprite.u0, sprite.v1)
|
||||
}
|
||||
|
||||
fun setColor(color: Int) {
|
||||
particleBlue = (color and 255) / 256.0f
|
||||
particleGreen = ((color shr 8) and 255) / 256.0f
|
||||
particleRed = ((color shr 16) and 255) / 256.0f
|
||||
fun setColor(color: Color) {
|
||||
rCol = color.red / 256.0f
|
||||
gCol = color.green / 256.0f
|
||||
bCol = color.blue / 256.0f
|
||||
}
|
||||
|
||||
/**
|
||||
* Set particle color to the "stronger" of the given colors, determined by higher color saturation
|
||||
*/
|
||||
fun setColor(color1: Color, color2: Color) =
|
||||
setColor(if (color1.asHSB.saturation > color2.asHSB.saturation) color1 else color2)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mods.betterfoliage.render.particle
|
||||
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.PI2
|
||||
import mods.betterfoliage.util.minmax
|
||||
@@ -8,22 +9,22 @@ import mods.betterfoliage.util.randomB
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomF
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.particle.IParticleRenderType
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import net.minecraftforge.event.TickEvent
|
||||
import net.minecraftforge.event.world.WorldEvent
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.LogicalSide
|
||||
import java.util.Random
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class FallingLeafParticle(
|
||||
world: World, pos: BlockPos, leaf: LeafParticleKey, blockColor: Int, random: Random
|
||||
world: ClientWorld, pos: BlockPos, leaf: LeafParticleKey, blockColor: Int, random: Random
|
||||
) : AbstractParticle(
|
||||
world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5
|
||||
) {
|
||||
@@ -37,14 +38,14 @@ class FallingLeafParticle(
|
||||
var wasCollided = false
|
||||
|
||||
init {
|
||||
particleAngle = random.randomF(max = PI2)
|
||||
prevParticleAngle = particleAngle - rotationSpeed
|
||||
roll = random.randomF(max = PI2)
|
||||
oRoll = roll - rotationSpeed
|
||||
|
||||
maxAge = MathHelper.floor(randomD(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
||||
motionY = -Config.fallingLeaves.speed
|
||||
lifetime = MathHelper.floor(randomD(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
||||
yd = -Config.fallingLeaves.speed
|
||||
|
||||
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
||||
setColor(leaf.overrideColor?.asInt ?: blockColor)
|
||||
quadSize = Config.fallingLeaves.size.toFloat() * 0.1f
|
||||
if (leaf.tintIndex == -1) setColor(leaf.avgColor) else setColor(leaf.avgColor, Color(blockColor))
|
||||
sprite = LeafParticleRegistry[leaf.leafType][randomI(max = 1024)]
|
||||
}
|
||||
|
||||
@@ -52,21 +53,21 @@ class FallingLeafParticle(
|
||||
|
||||
|
||||
override fun update() {
|
||||
if (rand.nextFloat() > 0.95f) rotationSpeed *= -1.0f
|
||||
if (age > maxAge - 20) particleAlpha = 0.05f * (maxAge - age)
|
||||
if (random.nextFloat() > 0.95f) rotationSpeed *= -1.0f
|
||||
if (age > lifetime - 20) alpha = 0.05f * (lifetime - age)
|
||||
|
||||
if (onGround || wasCollided) {
|
||||
velocity.setTo(0.0, 0.0, 0.0)
|
||||
if (!wasCollided) {
|
||||
age = age.coerceAtLeast(maxAge - 20)
|
||||
age = age.coerceAtLeast(lifetime - 20)
|
||||
wasCollided = true
|
||||
}
|
||||
} else {
|
||||
val cosRotation = cos(particleAngle).toDouble(); val sinRotation = sin(particleAngle).toDouble()
|
||||
val cosRotation = cos(roll).toDouble(); val sinRotation = sin(roll).toDouble()
|
||||
velocity.setTo(cosRotation, 0.0, sinRotation).mul(Config.fallingLeaves.perturb)
|
||||
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
|
||||
prevParticleAngle = particleAngle
|
||||
particleAngle += rotationSpeed
|
||||
oRoll = roll
|
||||
roll += rotationSpeed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ object LeafWindTracker {
|
||||
}
|
||||
|
||||
fun changeWind(world: World) {
|
||||
nextChange = world.worldInfo.gameTime + 120 + random.nextInt(80)
|
||||
nextChange = world.gameTime + 120 + random.nextInt(80)
|
||||
val direction = PI2 * random.nextDouble()
|
||||
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
|
||||
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
|
||||
@@ -92,10 +93,10 @@ object LeafWindTracker {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
|
||||
if (event.phase == TickEvent.Phase.START) Minecraft.getInstance().world?.let { world ->
|
||||
fun handleWorldTick(event: TickEvent.WorldTickEvent) {
|
||||
if (event.phase == TickEvent.Phase.START && event.side == LogicalSide.CLIENT) event.world.let { world ->
|
||||
// change target wind speed
|
||||
if (world.worldInfo.dayTime >= nextChange) changeWind(world)
|
||||
if (world.dayTime >= nextChange) changeWind(world)
|
||||
|
||||
// change current wind speed
|
||||
val changeRate = if (world.isRaining) 0.015 else 0.005
|
||||
@@ -107,6 +108,6 @@ object LeafWindTracker {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world.world) }
|
||||
// @SubscribeEvent
|
||||
// fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isClientSide) changeWind(event.world) }
|
||||
}
|
||||
@@ -10,8 +10,10 @@ import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.getLines
|
||||
import mods.betterfoliage.util.resourceManager
|
||||
import mods.betterfoliage.util.stripEnd
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||
import net.minecraft.resources.IResourceManager
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
@@ -23,25 +25,28 @@ interface LeafBlockModel {
|
||||
|
||||
interface LeafParticleKey {
|
||||
val leafType: String
|
||||
val overrideColor: Color?
|
||||
val tintIndex: Int
|
||||
val avgColor: Color
|
||||
}
|
||||
|
||||
object LeafParticleRegistry : HasLogger(), VeryEarlyReloadListener {
|
||||
val typeMappings = TextureMatcher()
|
||||
val allTypes get() = (typeMappings.mappings.map { it.type } + "default").distinct()
|
||||
|
||||
val allTypes = mutableSetOf<String>()
|
||||
val particles = hashMapOf<String, SpriteSet>()
|
||||
|
||||
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
||||
|
||||
override fun onReloadStarted() {
|
||||
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
||||
detailLogger.log(INFO, "Loaded leaf particle mappings, types = [${allTypes.joinToString(", ")}]")
|
||||
override fun onReloadStarted(resourceManager: IResourceManager) {
|
||||
allTypes.clear()
|
||||
resourceManager.listResources("textures/particle") { it.startsWith("falling_leaf_") }
|
||||
.filter { it.namespace == BetterFoliageMod.MOD_ID }
|
||||
.map { it.stripStart("textures/particle/falling_leaf_").stripEnd(".png") }
|
||||
.map { it.path.substringBefore("_", "") }
|
||||
.forEach { leafType -> if (!leafType.isEmpty()) allTypes.add(leafType) }
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
||||
if (event.map.location() == Atlas.PARTICLES.resourceId) {
|
||||
allTypes.forEach { leafType ->
|
||||
val locations = (0 until 16).map { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
||||
@@ -55,8 +60,8 @@ object LeafParticleRegistry : HasLogger(), VeryEarlyReloadListener {
|
||||
|
||||
@SubscribeEvent
|
||||
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
||||
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
||||
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
||||
if (event.map.location() == Atlas.PARTICLES.resourceId) {
|
||||
allTypes.forEach { leafType ->
|
||||
val sprites = (0 until 16).map { idx ->
|
||||
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.client.particle.IParticleRenderType
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.MathHelper
|
||||
@@ -23,7 +24,7 @@ import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class RisingSoulParticle(
|
||||
world: World, pos: BlockPos
|
||||
world: ClientWorld, pos: BlockPos
|
||||
) : AbstractParticle(
|
||||
world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5
|
||||
) {
|
||||
@@ -32,10 +33,10 @@ class RisingSoulParticle(
|
||||
val initialPhase = randomD(max = PI2)
|
||||
|
||||
init {
|
||||
motionY = 0.1
|
||||
particleGravity = 0.0f
|
||||
yd = 0.1
|
||||
gravity = 0.0f
|
||||
sprite = headIcons[randomI(max = 1024)]
|
||||
maxAge = MathHelper.floor((0.6 + 0.4 * randomD()) * Config.risingSoul.lifetime * 20.0)
|
||||
lifetime = MathHelper.floor((0.6 + 0.4 * randomD()) * Config.risingSoul.lifetime * 20.0)
|
||||
}
|
||||
|
||||
override val isValid: Boolean get() = true
|
||||
@@ -49,12 +50,12 @@ class RisingSoulParticle(
|
||||
particleTrail.addFirst(currentPos.copy())
|
||||
while (particleTrail.size > Config.risingSoul.trailLength) particleTrail.removeLast()
|
||||
|
||||
if (!Config.enabled) setExpired()
|
||||
if (!Config.enabled) remove()
|
||||
}
|
||||
|
||||
override fun renderParticle(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||
override fun render(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||
var alpha = Config.risingSoul.opacity.toFloat()
|
||||
if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
|
||||
if (age > lifetime - 40) alpha *= (lifetime - age) / 40.0f
|
||||
|
||||
renderParticleQuad(
|
||||
vertexBuilder, camera, tickDelta,
|
||||
|
||||
42
src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt
Normal file
42
src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package mods.betterfoliage.render.pipeline
|
||||
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.betterfoliage.RenderTypeLookup
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import java.util.function.Predicate
|
||||
|
||||
object Layers {
|
||||
val tufts = RenderType.cutout()
|
||||
val connectedGrass = RenderType.solid()
|
||||
val connectedDirt = RenderType.cutoutMipped()
|
||||
val coral = RenderType.cutoutMipped()
|
||||
}
|
||||
|
||||
val defaultLayerBehaviour = Predicate<RenderType> { layer -> layer == RenderType.solid() }
|
||||
|
||||
class WrappedLayerPredicate(val original: Predicate<RenderType>, val func: (RenderType, Predicate<RenderType>) -> Boolean) : Predicate<RenderType> {
|
||||
override fun test(layer: RenderType) = func(layer, original)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension method to access the canRenderInLayer() predicate in [RenderTypeLookup]
|
||||
*/
|
||||
var Block.layerPredicate : Predicate<RenderType>?
|
||||
get() = RenderTypeLookup.blockRenderChecks.getStatic()[delegate]
|
||||
set(value) {
|
||||
RenderTypeLookup.blockRenderChecks.getStatic()[delegate] = value!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a wrapper to the block's canRenderInLayer() predicate to enable dynamic multi-layer rendering.
|
||||
* If the render data for the block implements [SpecialRenderData], the layers it enables will be
|
||||
* rendered _in addition to_ the block's normal layers.
|
||||
*/
|
||||
fun Block.extendLayers() {
|
||||
val original = layerPredicate ?: defaultLayerBehaviour
|
||||
if (original !is WrappedLayerPredicate) layerPredicate = WrappedLayerPredicate(original) { layer, original ->
|
||||
original.test(layer) ||
|
||||
(RenderCtxBase.specialRenderData.get() as? SpecialRenderData)?.canRenderInLayer(layer) ?: false
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,20 @@ package mods.betterfoliage.render.pipeline
|
||||
import com.mojang.blaze3d.matrix.MatrixStack
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.model.HalfBakedQuad
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.render.lighting.VanillaFullBlockLighting
|
||||
import mods.betterfoliage.render.lighting.VanillaQuadLighting
|
||||
import mods.betterfoliage.render.lighting.VanillaVertexLighter
|
||||
import mods.betterfoliage.model.HalfBakedQuad
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.plus
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraftforge.client.model.data.IModelData
|
||||
import java.util.Random
|
||||
|
||||
@@ -25,25 +27,29 @@ import java.util.Random
|
||||
* push-based partial rendering pipeline for [SpecialRenderModel] instances.
|
||||
*/
|
||||
abstract class RenderCtxBase(
|
||||
world: ILightReader,
|
||||
pos: BlockPos,
|
||||
blockCtx: BlockCtx,
|
||||
val matrixStack: MatrixStack,
|
||||
var checkSides: Boolean,
|
||||
val random: Random,
|
||||
val modelData: IModelData
|
||||
) : BlockCtx by BasicBlockCtx(world, pos) {
|
||||
|
||||
abstract fun renderQuad(quad: HalfBakedQuad)
|
||||
val modelData: IModelData,
|
||||
) : BlockCtx by blockCtx {
|
||||
|
||||
var hasRendered = false
|
||||
val blockModelShapes = Minecraft.getInstance().blockRendererDispatcher.blockModelShapes
|
||||
var modelRenderData: Any? = null
|
||||
inline fun <reified T> withRenderData(renderFunc: (T)->Boolean) = (modelRenderData as? T?).let {
|
||||
if (it == null) false else renderFunc(it)
|
||||
}
|
||||
|
||||
val blockModelShapes = Minecraft.getInstance().blockRenderer.blockModelShaper
|
||||
var vertexLighter: VanillaVertexLighter = VanillaFullBlockLighting
|
||||
protected val lightingData = RenderCtxBase.lightingData.get().apply {
|
||||
calc.reset(this@RenderCtxBase)
|
||||
blockColors = Minecraft.getInstance().blockColors
|
||||
}
|
||||
|
||||
inline fun Direction?.shouldRender() = this == null || !checkSides || Block.shouldSideBeRendered(state, world, pos, this)
|
||||
abstract fun renderQuad(quad: HalfBakedQuad)
|
||||
|
||||
inline fun Direction?.shouldRender() = this == null || !checkSides || Block.shouldRenderFace(state, world, pos, this)
|
||||
|
||||
fun renderQuads(quads: Iterable<HalfBakedQuad>) {
|
||||
quads.forEach { quad ->
|
||||
@@ -61,6 +67,17 @@ abstract class RenderCtxBase(
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun reset(chunkRenderCache: IBlockDisplayReader, blockRendererDispatcher: BlockRendererDispatcher, pos: BlockPos, random: Random) {
|
||||
// prepare render data
|
||||
val blockCtx = BasicBlockCtx(chunkRenderCache, pos)
|
||||
val model = blockRendererDispatcher.getBlockModel(blockCtx.state)
|
||||
random.setSeed(blockCtx.seed)
|
||||
val data = if (model is SpecialRenderModel) model.prepare(blockCtx, random) else Unit
|
||||
specialRenderData.set(data)
|
||||
}
|
||||
|
||||
val lightingData = ThreadLocal.withInitial { VanillaQuadLighting() }
|
||||
val specialRenderData = ThreadLocal<Any?>()
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,31 @@
|
||||
package mods.betterfoliage.render.pipeline
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.model.HalfBakedQuad
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.render.lighting.ForgeVertexLighter
|
||||
import mods.betterfoliage.render.lighting.ForgeVertexLighterAccess
|
||||
import mods.betterfoliage.model.HalfBakedQuad
|
||||
import mods.betterfoliage.util.getWithDefault
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.LightTexture
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraftforge.client.ForgeHooksClient
|
||||
import net.minecraftforge.client.MinecraftForgeClient
|
||||
import net.minecraftforge.client.model.data.IModelData
|
||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat
|
||||
import java.util.Random
|
||||
|
||||
class RenderCtxForge(
|
||||
world: ILightReader,
|
||||
pos: BlockPos,
|
||||
blockCtx: BlockCtx,
|
||||
val lighter: VertexLighterFlat,
|
||||
matrixStack: MatrixStack,
|
||||
checkSides: Boolean,
|
||||
random: Random,
|
||||
modelData: IModelData
|
||||
): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData), ForgeVertexLighter {
|
||||
modelData: IModelData,
|
||||
) : RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData), ForgeVertexLighter {
|
||||
|
||||
override fun renderQuad(quad: HalfBakedQuad) {
|
||||
// set Forge lighter AO calculator to us
|
||||
@@ -33,12 +37,20 @@ class RenderCtxForge(
|
||||
var vIdx = 0
|
||||
override fun updateVertexLightmap(normal: FloatArray, lightmap: FloatArray, x: Float, y: Float, z: Float) {
|
||||
lightingData.packedLight[vIdx].let { packedLight ->
|
||||
lightmap[0] = LightTexture.getLightBlock(packedLight) / 0xF.toFloat()
|
||||
lightmap[1] = LightTexture.getLightSky(packedLight) / 0xF.toFloat()
|
||||
lightmap[0] = LightTexture.block(packedLight) / 0xF.toFloat()
|
||||
lightmap[1] = LightTexture.sky(packedLight) / 0xF.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateVertexColor(normal: FloatArray, color: FloatArray, x: Float, y: Float, z: Float, tint: Float, multiplier: Int) {
|
||||
override fun updateVertexColor(
|
||||
normal: FloatArray,
|
||||
color: FloatArray,
|
||||
x: Float,
|
||||
y: Float,
|
||||
z: Float,
|
||||
tint: Float,
|
||||
multiplier: Int
|
||||
) {
|
||||
color[0] = lightingData.tint[0] * lightingData.colorMultiplier[vIdx]
|
||||
color[1] = lightingData.tint[1] * lightingData.colorMultiplier[vIdx]
|
||||
color[2] = lightingData.tint[2] * lightingData.colorMultiplier[vIdx]
|
||||
@@ -49,23 +61,27 @@ class RenderCtxForge(
|
||||
@JvmStatic
|
||||
fun render(
|
||||
lighter: VertexLighterFlat,
|
||||
world: ILightReader,
|
||||
world: IBlockDisplayReader,
|
||||
model: SpecialRenderModel,
|
||||
state: BlockState,
|
||||
pos: BlockPos,
|
||||
matrixStack: MatrixStack,
|
||||
checkSides: Boolean,
|
||||
rand: Random, seed: Long,
|
||||
random: Random, seed: Long,
|
||||
modelData: IModelData
|
||||
): Boolean {
|
||||
lighter.setWorld(world)
|
||||
lighter.setState(state)
|
||||
lighter.setBlockPos(pos)
|
||||
rand.setSeed(seed)
|
||||
lighter.updateBlockInfo()
|
||||
return RenderCtxForge(world, pos, lighter, matrixStack, checkSides, rand, modelData).let {
|
||||
val blockCtx = BasicBlockCtx(world, pos)
|
||||
val ctx = RenderCtxForge(blockCtx, lighter, matrixStack, checkSides, random, modelData).apply {
|
||||
lighter.setWorld(world)
|
||||
lighter.setState(state)
|
||||
lighter.setBlockPos(pos)
|
||||
lighter.updateBlockInfo()
|
||||
}
|
||||
|
||||
// render layer
|
||||
return ctx.let {
|
||||
(lighter as ForgeVertexLighterAccess).vertexLighter = it
|
||||
model.render(it, false)
|
||||
model.renderLayer(it, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer())
|
||||
lighter.resetBlockInfo()
|
||||
it.hasRendered
|
||||
}
|
||||
|
||||
@@ -2,33 +2,36 @@ package mods.betterfoliage.render.pipeline
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.model.HalfBakedQuad
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.util.getWithDefault
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.BlockModelRenderer
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ILightReader
|
||||
import net.minecraft.world.IBlockDisplayReader
|
||||
import net.minecraftforge.client.MinecraftForgeClient
|
||||
import net.minecraftforge.client.model.data.IModelData
|
||||
import java.util.Random
|
||||
|
||||
class RenderCtxVanilla(
|
||||
val renderer: BlockModelRenderer,
|
||||
world: ILightReader,
|
||||
pos: BlockPos,
|
||||
blockCtx: BlockCtx,
|
||||
val buffer: IVertexBuilder,
|
||||
val combinedOverlay: Int,
|
||||
matrixStack: MatrixStack,
|
||||
checkSides: Boolean,
|
||||
random: Random,
|
||||
val seed: Long,
|
||||
val randomSeed: Long,
|
||||
modelData: IModelData,
|
||||
val useAO: Boolean
|
||||
): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData) {
|
||||
): RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData) {
|
||||
|
||||
override fun renderQuad(quad: HalfBakedQuad) {
|
||||
vertexLighter.updateLightmapAndColor(quad, lightingData)
|
||||
buffer.addQuad(
|
||||
matrixStack.last, quad.baked,
|
||||
buffer.putBulkData(
|
||||
matrixStack.last(), quad.baked,
|
||||
lightingData.colorMultiplier,
|
||||
lightingData.tint[0], lightingData.tint[1], lightingData.tint[2],
|
||||
lightingData.packedLight, combinedOverlay, true
|
||||
@@ -39,7 +42,7 @@ class RenderCtxVanilla(
|
||||
@JvmStatic
|
||||
fun render(
|
||||
renderer: BlockModelRenderer,
|
||||
world: ILightReader,
|
||||
world: IBlockDisplayReader,
|
||||
model: SpecialRenderModel,
|
||||
state: BlockState,
|
||||
pos: BlockPos,
|
||||
@@ -47,20 +50,16 @@ class RenderCtxVanilla(
|
||||
buffer: IVertexBuilder,
|
||||
checkSides: Boolean,
|
||||
random: Random,
|
||||
rand: Long,
|
||||
seed: Long,
|
||||
combinedOverlay: Int,
|
||||
modelData: IModelData,
|
||||
smooth: Boolean
|
||||
): Boolean {
|
||||
random.setSeed(rand)
|
||||
val ctx = RenderCtxVanilla(renderer, world, pos, buffer, combinedOverlay, matrixStack, checkSides, random, rand, modelData, smooth)
|
||||
lightingData.apply {
|
||||
|
||||
}
|
||||
model.render(ctx, false)
|
||||
val blockCtx = BasicBlockCtx(world, pos)
|
||||
// init context if missing (this is the first render layer)
|
||||
val ctx = RenderCtxVanilla(renderer, blockCtx, buffer, combinedOverlay, matrixStack, checkSides, random, seed, modelData, smooth)
|
||||
model.renderLayer(ctx, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer())
|
||||
return ctx.hasRendered
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ interface VeryEarlyReloadListener : IFutureReloadListener {
|
||||
backgroundExecutor: Executor,
|
||||
gameExecutor: Executor
|
||||
): CompletableFuture<Void> {
|
||||
onReloadStarted()
|
||||
return stage.markCompleteAwaitingOthers(null)
|
||||
onReloadStarted(resourceManager)
|
||||
return stage.wait(null)
|
||||
}
|
||||
|
||||
fun onReloadStarted() {}
|
||||
fun onReloadStarted(resourceManager: IResourceManager) {}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import mods.betterfoliage.util.resourceManager
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.IModelTransform
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.model.RenderMaterial
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.event.ModelBakeEvent
|
||||
@@ -30,7 +32,7 @@ data class ModelDefinitionsLoadedEvent(
|
||||
|
||||
interface ModelBakingKey {
|
||||
fun bake(ctx: ModelBakingContext): IBakedModel? =
|
||||
ctx.getUnbaked().bakeModel(ctx.bakery, ctx.spriteGetter, ctx.transform, ctx.location)
|
||||
ctx.getUnbaked().bake(ctx.bakery, ctx.spriteGetter, ctx.transform, ctx.location)
|
||||
}
|
||||
|
||||
interface ModelDiscovery {
|
||||
@@ -49,26 +51,29 @@ data class ModelDiscoveryContext(
|
||||
val replacements: MutableMap<ResourceLocation, ModelBakingKey>,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked(location: ResourceLocation = modelLocation) = bakery.getUnbakedModel(location)
|
||||
fun getUnbaked(location: ResourceLocation = modelLocation) = bakery.getModel(location)
|
||||
fun addReplacement(key: ModelBakingKey, addToStateKeys: Boolean = true) {
|
||||
replacements[modelLocation] = key
|
||||
if (addToStateKeys) BetterFoliage.blockTypes.stateKeys[blockState] = key
|
||||
logger.log(INFO, "Adding model replacement $modelLocation -> $key")
|
||||
}
|
||||
fun <T: IUnbakedModel> loadHierarchy(model: T) = model.apply {
|
||||
getMaterials(this@ModelDiscoveryContext::getUnbaked, mutableSetOf())
|
||||
}
|
||||
}
|
||||
|
||||
data class ModelBakingContext(
|
||||
val bakery: ModelBakery,
|
||||
val spriteGetter: Function<Material, TextureAtlasSprite>,
|
||||
val spriteGetter: Function<RenderMaterial, TextureAtlasSprite>,
|
||||
val location: ResourceLocation,
|
||||
val transform: IModelTransform,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked() = bakery.getUnbakedModel(location)
|
||||
fun getUnbaked() = bakery.getModel(location)
|
||||
fun getBaked() = bakery.getBakedModel(location, transform, spriteGetter)
|
||||
}
|
||||
|
||||
object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
class BakeWrapperManager : Invalidator, HasLogger() {
|
||||
val discoverers = mutableListOf<ModelDiscovery>()
|
||||
override val callbacks = mutableListOf<WeakReference<()->Unit>>()
|
||||
|
||||
@@ -79,6 +84,7 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
invalidate()
|
||||
BetterFoliage.blockConfig.readConfig(resourceManager)
|
||||
BetterFoliage.blockTypes = BlockTypeCache()
|
||||
|
||||
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
||||
@@ -94,7 +100,7 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleStitch(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureLocation == Atlas.BLOCKS.resourceId) {
|
||||
if (event.map.location() == Atlas.BLOCKS.resourceId) {
|
||||
logger.log(INFO, "Adding ${sprites.size} sprites to block atlas")
|
||||
sprites.forEach { event.addSprite(it) }
|
||||
sprites.clear()
|
||||
@@ -109,7 +115,7 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
fun onBake(
|
||||
unbaked: IUnbakedModel,
|
||||
bakery: ModelBakery,
|
||||
spriteGetter: Function<Material, TextureAtlasSprite>,
|
||||
spriteGetter: Function<RenderMaterial, TextureAtlasSprite>,
|
||||
transform: IModelTransform,
|
||||
location: ResourceLocation
|
||||
): IBakedModel? {
|
||||
@@ -124,6 +130,6 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
logger.log(WARN, "Error while baking $replacement", e)
|
||||
}
|
||||
}
|
||||
return unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||
return unbaked.bake(bakery, spriteGetter, transform, location)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.getJavaClass
|
||||
import mods.betterfoliage.util.getLines
|
||||
@@ -45,8 +44,8 @@ class ConfigurableBlockMatcher(val location: ResourceLocation) : HasLogger(), IB
|
||||
fun readDefaults() {
|
||||
blackList.clear()
|
||||
whiteList.clear()
|
||||
resourceManager.getAllResources(location).forEach { resource ->
|
||||
detailLogger.log(INFO, "Reading block class configuration $location from pack ${resource.packName}")
|
||||
resourceManager.getResources(location).forEach { resource ->
|
||||
detailLogger.log(INFO, "Reading block class configuration $location from pack ${resource.sourceName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
if (line.startsWith("-")) getJavaClass(line.substring(1))?.let { blackList.add(it) }
|
||||
else getJavaClass(line)?.let { whiteList.add(it) }
|
||||
@@ -63,8 +62,8 @@ data class ModelTextureList(val modelLocation: ResourceLocation, val textureName
|
||||
class ModelTextureListConfiguration(val location: ResourceLocation) : HasLogger() {
|
||||
val modelList = mutableListOf<ModelTextureList>()
|
||||
fun readDefaults() {
|
||||
resourceManager.getAllResources(location).forEach { resource ->
|
||||
detailLogger.log(INFO, "Reading model/texture configuration $location from pack ${resource.packName}")
|
||||
resourceManager.getResources(location).forEach { resource ->
|
||||
detailLogger.log(INFO, "Reading model/texture configuration $location from pack ${resource.sourceName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
val elements = line.split(",")
|
||||
modelList.add(ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)))
|
||||
|
||||
@@ -18,9 +18,9 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||
) {
|
||||
ForgeRegistries.BLOCKS
|
||||
.flatMap { block -> block.stateContainer.validStates }
|
||||
.flatMap { block -> block.stateDefinition.possibleStates }
|
||||
.forEach { state ->
|
||||
val location = BlockModelShapes.getModelLocation(state)
|
||||
val location = BlockModelShapes.stateToModelLocation(state)
|
||||
val ctx = ModelDiscoveryContext(bakery, state, location, sprites, replacements, detailLogger)
|
||||
try {
|
||||
processModel(ctx)
|
||||
@@ -31,6 +31,10 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
}
|
||||
|
||||
open fun processModel(ctx: ModelDiscoveryContext) {
|
||||
processContainerModel(ctx)
|
||||
}
|
||||
|
||||
fun processContainerModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
|
||||
// built-in support for container models
|
||||
@@ -38,7 +42,7 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
// per-location replacements need to be scoped to the variant list, as replacement models
|
||||
// may need information from the BlockState which is not available at baking time
|
||||
val scopedReplacements = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||
model.variantList.forEach { variant ->
|
||||
model.variants.forEach { variant ->
|
||||
processModel(ctx.copy(modelLocation = variant.modelLocation, replacements = scopedReplacements))
|
||||
}
|
||||
if (scopedReplacements.isNotEmpty()) {
|
||||
@@ -73,15 +77,18 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
||||
matches.forEach { match ->
|
||||
detailLogger.log(Level.INFO, " model $model matches ${match.modelLocation}")
|
||||
|
||||
val materials = match.textureNames.map { it to model.resolveTextureName(it) }
|
||||
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.textureLocation}" })
|
||||
detailLogger.log(Level.INFO, " sprites [$texMapString]")
|
||||
val materials = match.textureNames.map { it to model.getMaterial(it) }
|
||||
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.texture()}" })
|
||||
detailLogger.log(Level.INFO, " sprites [$texMapString]")
|
||||
|
||||
if (materials.all { it.second.textureLocation != MissingTextureSprite.getLocation() }) {
|
||||
// found a valid model (all required textures exist)
|
||||
processModel(ctx, materials.map { it.second.textureLocation })
|
||||
}
|
||||
if (materials.all { it.second.texture() != MissingTextureSprite.getLocation() }) {
|
||||
// found a valid model (all required textures exist)
|
||||
processModel(ctx, materials.map { it.second.texture() })
|
||||
}
|
||||
}
|
||||
if (matches.isEmpty()) {
|
||||
detailLogger.log(Level.INFO, " no matches for model ${ctx.modelLocation}, inheritance chain ${ancestry.joinToString(" -> ")}")
|
||||
}
|
||||
}
|
||||
return super.processModel(ctx)
|
||||
}
|
||||
@@ -90,12 +97,12 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
||||
fun ModelBakery.modelDerivesFrom(model: BlockModel, location: ResourceLocation, target: ResourceLocation): Boolean =
|
||||
if (location == target) true
|
||||
else model.parentLocation
|
||||
?.let { getUnbakedModel(it) as? BlockModel }
|
||||
?.let { getModel(it) as? BlockModel }
|
||||
?.let { parent -> modelDerivesFrom(parent, model.parentLocation!!, target) }
|
||||
?: false
|
||||
|
||||
fun ModelBakery.getAncestry(location: ResourceLocation): List<ResourceLocation> {
|
||||
val model = getUnbakedModel(location) as? BlockModel ?: return listOf(location)
|
||||
val model = getModel(location) as? BlockModel ?: return listOf(location)
|
||||
val parentAncestry = model.parentLocation?.let { getAncestry(it) } ?: emptyList()
|
||||
return listOf(location) + parentAncestry
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.config.match.MAnything
|
||||
import mods.betterfoliage.config.match.MListAll
|
||||
import mods.betterfoliage.config.match.MListAny
|
||||
import mods.betterfoliage.config.match.MNegated
|
||||
import mods.betterfoliage.config.match.MValue
|
||||
import mods.betterfoliage.config.match.MatchRules
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
abstract class ParametrizedModelDiscovery : HasLogger() {
|
||||
abstract fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>)
|
||||
|
||||
fun Map<String, String>.location(key: String): ResourceLocation? {
|
||||
val result = get(key)?.let { ResourceLocation(it) }
|
||||
if (result == null) detailLogger.log(Level.WARN, "Cannot find texture parameter \"$key\"")
|
||||
return result
|
||||
}
|
||||
|
||||
fun Map<String, String>.int(key: String): Int? {
|
||||
val result = get(key)?.toInt()
|
||||
if (result == null) detailLogger.log(Level.WARN, "Cannot find integer parameter \"$key\"")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class RuleProcessingContext(
|
||||
val discovery: ModelDiscoveryContext
|
||||
) {
|
||||
val params = mutableMapOf("type" to "none")
|
||||
}
|
||||
|
||||
class RuleBasedDiscovery : AbstractModelDiscovery() {
|
||||
val discoverers = mutableMapOf<String, ParametrizedModelDiscovery>()
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) = when(ctx.getUnbaked()) {
|
||||
is VariantList -> processContainerModel(ctx)
|
||||
is BlockModel -> processBlockModel(ctx)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
fun processBlockModel(ctx: ModelDiscoveryContext) {
|
||||
val ruleCtx = RuleProcessingContext(ctx)
|
||||
val rulesToCheck = BetterFoliage.blockConfig.rules.toMutableList()
|
||||
val ruleResults = mutableListOf<MListAll>()
|
||||
var previousSize = 0
|
||||
|
||||
// stop processing if nothing changes anymore
|
||||
while (rulesToCheck.size != previousSize) {
|
||||
previousSize = rulesToCheck.size
|
||||
val iterator = rulesToCheck.listIterator()
|
||||
while (iterator.hasNext()) iterator.next().let { rule ->
|
||||
// process single rule
|
||||
MatchRules.visitRoot(ruleCtx, rule).let { result ->
|
||||
ruleResults.add(result)
|
||||
// remove rule from active list if:
|
||||
// - rule succeeded (all directives returned success)
|
||||
// - rule is immutable (result will always be the same)
|
||||
if (result.value || result.immutable) iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log result of rule processing
|
||||
if (ruleResults.any { it.value }) {
|
||||
detailLogger.log(Level.INFO, "================================")
|
||||
detailLogger.log(Level.INFO, "block state: ${ctx.blockState}")
|
||||
detailLogger.log(Level.INFO, "block class: ${ctx.blockState.block::class.java.name}")
|
||||
detailLogger.log(Level.INFO, "model : ${ctx.modelLocation}")
|
||||
detailLogger.log(Level.INFO, "--------------------------------")
|
||||
ruleResults.forEach { logResult(it) }
|
||||
}
|
||||
|
||||
discoverers[ruleCtx.params["type"]]?.processModel(ctx, ruleCtx.params)
|
||||
}
|
||||
|
||||
fun logResult(match: MAnything<Boolean>) {
|
||||
when(match) {
|
||||
is MListAll -> if (match.list.any { it.value }) {
|
||||
var seenInvariantSuccess = false
|
||||
match.list.forEach { item ->
|
||||
if (item.immutable && item.value) seenInvariantSuccess = true
|
||||
if (seenInvariantSuccess) logResult(item)
|
||||
}
|
||||
}
|
||||
is MListAny -> if (match.value) match.list.first { it.value }.let { logResult(it) }
|
||||
else match.list.forEach { logResult(it) }
|
||||
is MNegated -> logResult(match.inner)
|
||||
is MValue<Boolean> -> detailLogger.log(Level.INFO, "[${match.configSource}] ${match.description}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class WeightedUnbakedKey(
|
||||
if (unbaked !is VariantList) return super.bake(ctx)
|
||||
|
||||
// bake all variants, replace as needed
|
||||
val bakedModels = unbaked.variantList.mapNotNull {
|
||||
val bakedModels = unbaked.variants.mapNotNull {
|
||||
val variantCtx = ctx.copy(location = it.modelLocation, transform = it)
|
||||
val replacement = replacements[it.modelLocation]
|
||||
val baked = replacement?.let { replacement ->
|
||||
@@ -40,10 +40,10 @@ class WeightedUnbakedKey(
|
||||
// let it through unchanged
|
||||
if (bakedModels.isEmpty()) return super.bake(ctx)
|
||||
|
||||
if (bakedModels.size < unbaked.variantList.size) {
|
||||
if (bakedModels.size < unbaked.variants.size) {
|
||||
detailLogger.log(
|
||||
WARN,
|
||||
"Dropped ${unbaked.variantList.size - bakedModels.size} variants from model ${ctx.location}"
|
||||
"Dropped ${unbaked.variants.size - bakedModels.size} variants from model ${ctx.location}"
|
||||
)
|
||||
}
|
||||
val weightedSpecials = bakedModels.map { (variant, model) ->
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package mods.betterfoliage.resource.generated
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.ClientResourcePackInfo
|
||||
import net.minecraft.resources.*
|
||||
import net.minecraft.resources.ResourcePackType.CLIENT_RESOURCES
|
||||
import net.minecraft.resources.data.IMetadataSectionSerializer
|
||||
@@ -12,6 +10,7 @@ import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.text.StringTextComponent
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Predicate
|
||||
import java.util.function.Supplier
|
||||
|
||||
@@ -25,10 +24,10 @@ class GeneratedTexturePack(
|
||||
val nameSpace: String, val packName: String
|
||||
) : HasLogger(), IResourcePack {
|
||||
override fun getName() = packName
|
||||
override fun getResourceNamespaces(type: ResourcePackType) = setOf(nameSpace)
|
||||
override fun <T : Any?> getMetadata(deserializer: IMetadataSectionSerializer<T>) = null
|
||||
override fun getRootResourceStream(id: String) = null
|
||||
override fun getAllResourceLocations(type: ResourcePackType, namespace:String, path: String, maxDepth: Int, filter: Predicate<String>) = emptyList<ResourceLocation>()
|
||||
override fun getNamespaces(type: ResourcePackType) = setOf(nameSpace)
|
||||
override fun <T : Any?> getMetadataSection(deserializer: IMetadataSectionSerializer<T>) = null
|
||||
override fun getRootResource(id: String) = null
|
||||
override fun getResources(type: ResourcePackType, namespace:String, path: String, maxDepth: Int, filter: Predicate<String>) = emptyList<ResourceLocation>()
|
||||
|
||||
override fun close() {}
|
||||
|
||||
@@ -49,21 +48,22 @@ class GeneratedTexturePack(
|
||||
return id
|
||||
}
|
||||
|
||||
override fun getResourceStream(type: ResourcePackType, id: ResourceLocation) =
|
||||
override fun getResource(type: ResourcePackType, id: ResourceLocation) =
|
||||
if (type != CLIENT_RESOURCES) null else resources[id]?.inputStream()
|
||||
|
||||
override fun resourceExists(type: ResourcePackType, id: ResourceLocation) =
|
||||
override fun hasResource(type: ResourcePackType, id: ResourceLocation) =
|
||||
type == CLIENT_RESOURCES && resources.containsKey(id)
|
||||
|
||||
val finder = object : IPackFinder {
|
||||
val packInfo = ClientResourcePackInfo(
|
||||
val packInfo = ResourcePackInfo(
|
||||
packName, true, Supplier { this@GeneratedTexturePack },
|
||||
StringTextComponent(packName),
|
||||
StringTextComponent("Generated block textures resource pack"),
|
||||
PackCompatibility.COMPATIBLE, ResourcePackInfo.Priority.TOP, true, null, true
|
||||
PackCompatibility.COMPATIBLE, ResourcePackInfo.Priority.TOP, true, IPackNameDecorator.DEFAULT, true
|
||||
)
|
||||
override fun <T : ResourcePackInfo> addPackInfosToMap(nameToPackMap: MutableMap<String, T>, packInfoFactory: ResourcePackInfo.IFactory<T>) {
|
||||
(nameToPackMap as MutableMap<String, ResourcePackInfo>).put(packName, packInfo)
|
||||
|
||||
override fun loadPacks(p0: Consumer<ResourcePackInfo>, p1: ResourcePackInfo.IFactory) {
|
||||
p0.accept(packInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.material.Material
|
||||
|
||||
|
||||
|
||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT)
|
||||
@@ -62,3 +62,6 @@ open class LazyMapInvalidatable<K, V>(val invalidator: Invalidator, val valueFac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <V> Invalidator.lazy(valueFactory: ()->V) = LazyInvalidatable(this, valueFactory)
|
||||
fun <K, V> Invalidator.lazyMap(valueFactory: (K)->V) = LazyMapInvalidatable(this, valueFactory)
|
||||
@@ -65,6 +65,9 @@ inline fun <T> MutableList<T>.exchange(idx1: Int, idx2: Int) {
|
||||
/** Return a random element from the array using the provided random generator */
|
||||
inline operator fun <T> Array<T>.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size)
|
||||
|
||||
inline fun Random.idx(array: Array<*>) = nextInt(Int.MAX_VALUE) % array.size
|
||||
inline fun Random.idxOrNull(array: Array<*>, predicate: ()->Boolean) = if (predicate()) idx(array) else null
|
||||
|
||||
fun <T> Iterable<T>.toImmutableList() = ImmutableList.builder<T>().let { builder ->
|
||||
forEach { builder.add(it) }
|
||||
builder.build()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.client.renderer.Quaternion
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.Direction.*
|
||||
import net.minecraft.util.Direction.Axis.*
|
||||
import net.minecraft.util.Direction.AxisDirection.NEGATIVE
|
||||
import net.minecraft.util.Direction.AxisDirection.POSITIVE
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.vector.Quaternion
|
||||
|
||||
val EPSILON_ZERO = 0.05
|
||||
val EPSILON_ONE = 0.95
|
||||
@@ -55,15 +55,15 @@ val ROTATION_MATRIX: Array<IntArray> get() = arrayOf(
|
||||
// Vectors
|
||||
// ================================
|
||||
operator fun Direction.times(scale: Double) =
|
||||
Double3(directionVec.x.toDouble() * scale, directionVec.y.toDouble() * scale, directionVec.z.toDouble() * scale)
|
||||
val Direction.vec: Double3 get() = Double3(directionVec.x.toDouble(), directionVec.y.toDouble(), directionVec.z.toDouble())
|
||||
Double3(normal.x.toDouble() * scale, normal.y.toDouble() * scale, normal.z.toDouble() * scale)
|
||||
val Direction.vec: Double3 get() = Double3(normal.x.toDouble(), normal.y.toDouble(), normal.z.toDouble())
|
||||
|
||||
operator fun BlockPos.plus(other: Int3) = BlockPos(x + other.x, y + other.y, z + other.z)
|
||||
|
||||
/** 3D vector of [Double]s. Offers both mutable operations, and immutable operations in operator notation. */
|
||||
data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||
constructor(x: Float, y: Float, z: Float) : this(x.toDouble(), y.toDouble(), z.toDouble())
|
||||
constructor(dir: Direction) : this(dir.directionVec.x.toDouble(), dir.directionVec.y.toDouble(), dir.directionVec.z.toDouble())
|
||||
constructor(dir: Direction) : this(dir.normal.x.toDouble(), dir.normal.y.toDouble(), dir.normal.z.toDouble())
|
||||
companion object {
|
||||
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
||||
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
||||
@@ -88,9 +88,9 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||
/** Rotate vector by the given [Quaternion] */
|
||||
fun rotate(quat: Quaternion) =
|
||||
quat.copy()
|
||||
.apply { multiply(Quaternion(x, y, z, 0.0F)) }
|
||||
.apply { multiply(quat.copy().apply(Quaternion::conjugate)) }
|
||||
.let { Double3(it.x, it.y, it.z) }
|
||||
.apply { mul(Quaternion(x.toFloat(), y.toFloat(), z.toFloat(), 0.0F)) }
|
||||
.apply { mul(quat.copy().apply(Quaternion::conj)) }
|
||||
.let { Double3(it.i().toDouble(), it.j().toDouble(), it.k().toDouble()) }
|
||||
|
||||
// mutable operations
|
||||
fun setTo(other: Double3): Double3 { x = other.x; y = other.y; z = other.z; return this }
|
||||
@@ -120,11 +120,11 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||
|
||||
/** 3D vector of [Int]s. Offers both mutable operations, and immutable operations in operator notation. */
|
||||
data class Int3(var x: Int, var y: Int, var z: Int) {
|
||||
constructor(dir: Direction) : this(dir.directionVec.x, dir.directionVec.y, dir.directionVec.z)
|
||||
constructor(dir: Direction) : this(dir.normal.x, dir.normal.y, dir.normal.z)
|
||||
constructor(offset: Pair<Int, Direction>) : this(
|
||||
offset.first * offset.second.directionVec.x,
|
||||
offset.first * offset.second.directionVec.y,
|
||||
offset.first * offset.second.directionVec.z
|
||||
offset.first * offset.second.normal.x,
|
||||
offset.first * offset.second.normal.y,
|
||||
offset.first * offset.second.normal.z
|
||||
)
|
||||
companion object {
|
||||
val zero = Int3(0, 0, 0)
|
||||
@@ -133,9 +133,9 @@ data class Int3(var x: Int, var y: Int, var z: Int) {
|
||||
// immutable operations
|
||||
operator fun plus(other: Int3) = Int3(x + other.x, y + other.y, z + other.z)
|
||||
operator fun plus(other: Pair<Int, Direction>) = Int3(
|
||||
x + other.first * other.second.directionVec.x,
|
||||
y + other.first * other.second.directionVec.y,
|
||||
z + other.first * other.second.directionVec.z
|
||||
x + other.first * other.second.normal.x,
|
||||
y + other.first * other.second.normal.y,
|
||||
z + other.first * other.second.normal.z
|
||||
)
|
||||
operator fun unaryMinus() = Int3(-x, -y, -z)
|
||||
operator fun minus(other: Int3) = Int3(x - other.x, y - other.y, z - other.z)
|
||||
|
||||
@@ -7,16 +7,19 @@ import net.minecraft.util.text.TextFormatting
|
||||
import net.minecraft.util.text.TextFormatting.AQUA
|
||||
import net.minecraft.util.text.TextFormatting.GRAY
|
||||
|
||||
fun stripTooltipDefaultText(tooltip: MutableList<String>) {
|
||||
var defaultRows = false
|
||||
val iter = tooltip.iterator()
|
||||
while (iter.hasNext()) {
|
||||
if (iter.next().startsWith(AQUA.toString())) defaultRows = true
|
||||
if (defaultRows) iter.remove()
|
||||
}
|
||||
}
|
||||
//fun stripTooltipDefaultText(tooltip: MutableList<String>) {
|
||||
// var defaultRows = false
|
||||
// val iter = tooltip.iterator()
|
||||
// while (iter.hasNext()) {
|
||||
// if (iter.next().startsWith(AQUA.toString())) defaultRows = true
|
||||
// if (defaultRows) iter.remove()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun textComponent(msg: String, color: TextFormatting = GRAY): StringTextComponent {
|
||||
// val style = Style().apply { this.color = color }
|
||||
// return StringTextComponent(msg).apply { this.style = style }
|
||||
//}
|
||||
val styleGray = Style.EMPTY.applyFormats(GRAY)
|
||||
|
||||
fun textComponent(msg: String, color: TextFormatting = GRAY): StringTextComponent {
|
||||
val style = Style().apply { this.color = color }
|
||||
return StringTextComponent(msg).apply { this.style = style }
|
||||
}
|
||||
fun String.asText() = StringTextComponent(this).setStyle(styleGray)
|
||||
@@ -2,6 +2,9 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.model.HSB
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
@@ -20,6 +23,8 @@ inline fun String.stripEnd(str: String, ignoreCase: Boolean = true) = if (endsWi
|
||||
inline fun ResourceLocation.stripStart(str: String) = ResourceLocation(namespace, path.stripStart(str))
|
||||
inline fun ResourceLocation.stripEnd(str: String) = ResourceLocation(namespace, path.stripEnd(str))
|
||||
|
||||
val String.quoted: String get() = "\"$this\""
|
||||
|
||||
/**
|
||||
* Property-level delegate backed by a [ThreadLocal].
|
||||
*
|
||||
@@ -31,6 +36,11 @@ class ThreadLocalDelegate<T>(init: () -> T) {
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { tlVal.set(value) }
|
||||
}
|
||||
|
||||
fun <T> ThreadLocal<T?>.getWithDefault(factory: ()->T): T {
|
||||
get()?.let { return it }
|
||||
return factory().apply { set(this) }
|
||||
}
|
||||
|
||||
/** Call the supplied lambda and return its result, or the given default value if an exception is thrown. */
|
||||
fun <T> tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable) { default }
|
||||
|
||||
@@ -56,6 +66,12 @@ abstract class HasLogger {
|
||||
val detailLogger = BetterFoliageMod.detailLogger(this)
|
||||
}
|
||||
|
||||
fun Logger.logTextureColor(level: Level, description: String, avgColor: HSB) {
|
||||
val rgb = avgColor.asColor
|
||||
log(level, "$description average color RGB[${rgb.red},${rgb.green},${rgb.blue}], HSB[${avgColor.hue},${avgColor.saturation},${avgColor.brightness}]")
|
||||
}
|
||||
|
||||
fun getBlockModel(state: BlockState) = Minecraft.getInstance().blockRenderer.blockModelShaper.getBlockModel(state)
|
||||
/**
|
||||
* Check if the Chunk containing the given [BlockPos] is loaded.
|
||||
* Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.resources.IReloadableResourceManager
|
||||
import net.minecraft.resources.IResource
|
||||
import net.minecraft.resources.IResourceManager
|
||||
@@ -19,11 +17,6 @@ operator fun ResourceLocation.plus(str: String) = ResourceLocation(namespace, pa
|
||||
fun ResourceLocation.prependLocation(basePath: String) =
|
||||
ResourceLocation(namespace, basePath.stripEnd("/").let { "$it/$path" })
|
||||
|
||||
val ResourceLocation.asBlockMaterial: Material get() = Material(
|
||||
AtlasTexture.LOCATION_BLOCKS_TEXTURE,
|
||||
this
|
||||
)
|
||||
|
||||
/** Index operator to get a resource. */
|
||||
operator fun IResourceManager.get(domain: String, path: String): IResource? = get(ResourceLocation(domain, path))
|
||||
/** Index operator to get a resource. */
|
||||
|
||||
@@ -3,7 +3,6 @@ package mods.betterfoliage.util
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HSB
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.resources.IResource
|
||||
@@ -17,11 +16,13 @@ import java.io.IOException
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sin
|
||||
|
||||
enum class Atlas(val resourceId: ResourceLocation) {
|
||||
BLOCKS(AtlasTexture.LOCATION_BLOCKS_TEXTURE),
|
||||
PARTICLES(AtlasTexture.LOCATION_PARTICLES_TEXTURE);
|
||||
BLOCKS(AtlasTexture.LOCATION_BLOCKS),
|
||||
PARTICLES(AtlasTexture.LOCATION_PARTICLES);
|
||||
|
||||
/** Get the fully-qualified resource name for sprites belonging to this atlas */
|
||||
fun file(resource: ResourceLocation) = ResourceLocation(resource.namespace, "textures/${resource.path}.png")
|
||||
@@ -33,7 +34,7 @@ enum class Atlas(val resourceId: ResourceLocation) {
|
||||
operator fun get(location: ResourceLocation) = atlas.getSprite(location)
|
||||
}
|
||||
|
||||
val Material.atlas: Atlas get() = Atlas.values().find { it.resourceId == atlasLocation } ?: Atlas.BLOCKS
|
||||
//val Spr.atlas: Atlas get() = Atlas.values().find { it.resourceId == atlasLocation } ?: Atlas.BLOCKS
|
||||
|
||||
inline operator fun AtlasTexture.get(res: ResourceLocation): TextureAtlasSprite? = this.getSprite(res)
|
||||
inline operator fun AtlasTexture.get(name: String): TextureAtlasSprite? = get(ResourceLocation(name))
|
||||
@@ -83,6 +84,36 @@ val TextureAtlasSprite.averageColor: HSB get() {
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat())
|
||||
}
|
||||
|
||||
val ResourceLocation.averageHSB: HSB get() = resourceManager.loadSprite(this).let { image ->
|
||||
var numOpaque = 0
|
||||
var sumHueX = 0.0
|
||||
var sumHueY = 0.0
|
||||
var sumSaturation = 0.0f
|
||||
var sumBrightness = 0.0f
|
||||
for (x in 0 until image.width)
|
||||
for (y in 0 until image.width) {
|
||||
val pixel = image[x, y]
|
||||
val alpha = (pixel shr 24) and 255
|
||||
val hsb = HSB.fromColorBGRA(pixel)
|
||||
if (alpha == 255) {
|
||||
numOpaque++
|
||||
sumHueX += cos((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumHueY += sin((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumSaturation += hsb.saturation
|
||||
sumBrightness += hsb.brightness
|
||||
}
|
||||
}
|
||||
|
||||
// circular average - transform sum vector to polar angle
|
||||
val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat()
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat())
|
||||
}
|
||||
|
||||
fun HSB.brighten(multiplier: Float = 1.5f, floor: Float = 0.1f, ceiling: Float = 0.9f) =
|
||||
copy(brightness = (brightness * multiplier).coerceAtMost(max(ceiling, brightness)).coerceAtLeast(min(floor, brightness)))
|
||||
fun HSB.saturate(multiplier: Float = 1.5f, floor: Float = 0.1f, ceiling: Float = 0.9f) =
|
||||
copy(saturation = (saturation * multiplier).coerceAtMost(max(ceiling, saturation)).coerceAtLeast(min(floor, saturation)))
|
||||
|
||||
/** Weighted blend of 2 packed RGB colors */
|
||||
fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
||||
val r = (((rgb1 shr 16) and 255) * weight1 + ((rgb2 shr 16) and 255) * weight2) / (weight1 + weight2)
|
||||
@@ -103,4 +134,4 @@ fun logColorOverride(logger: Logger, threshold: Double, hsb: HSB) {
|
||||
}
|
||||
|
||||
fun HSB.colorOverride(threshold: Double) =
|
||||
if (saturation < threshold) null else copy(brightness = (brightness * 2.0f).coerceAtMost(0.9f)).asColor.let { Color(it) }
|
||||
if (saturation < threshold) null else copy(brightness = (brightness * 2.0f).coerceAtMost(0.9f)).asInt.let { Color(it) }
|
||||
@@ -1,7 +1,7 @@
|
||||
modLoader="kotlinfml"
|
||||
loaderVersion="[1.4,)"
|
||||
modLoader="kotlinforforge"
|
||||
loaderVersion="[1,)"
|
||||
issueTrackerURL="https://github.com/octarine-noise/BetterFoliage/issues"
|
||||
|
||||
license="MIT"
|
||||
[[mods]]
|
||||
modId="betterfoliage"
|
||||
version="${version}"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
match isParam("type", "leaf")
|
||||
model.extends("biomesoplenty:block/leaves_overlay")
|
||||
setParam("texture", model.texture("leaves"))
|
||||
setParam("tint", model.tint("leaves"))
|
||||
end
|
||||
|
||||
match isParam("type", "grass")
|
||||
model.extends("biomesoplenty:block/origin_grass_block")
|
||||
setParam("texture", model.texture("top"))
|
||||
setParam("tint", model.tint("top"))
|
||||
end
|
||||
@@ -0,0 +1,48 @@
|
||||
// A lot of BYG leaf models are very sloppily made, extending "block/cube" or even "block/block"
|
||||
// These rules are meh, but there's no better way to do it, there's no method to the madness here
|
||||
|
||||
// snowy leaves
|
||||
match isParam("type", "leaf") block.name.contains("byg:leaves") model.contains("snowy")
|
||||
setParam("texture", model.texture("up")) setParam("tint", model.tint("up")) end
|
||||
|
||||
// list of leaves where texture is "up"
|
||||
match model.matches(
|
||||
"byg:block/aspen_leaves",
|
||||
"byg:block/baobab_leaves",
|
||||
"byg:block/blue_enchanted_leaves"
|
||||
) setParam("texture", model.texture("up")) setParam("tint", model.tint("up"))
|
||||
end
|
||||
|
||||
// list of leaves where texture is "top"
|
||||
match model.matches(
|
||||
"byg:block/flowering_orchard_leaves",
|
||||
"byg:block/joshua_leaves",
|
||||
"byg:block/mahogany_leaves",
|
||||
"byg:block/maple_leaves",
|
||||
"byg:block/orchard_leaves",
|
||||
"byg:block/rainbow_eucalyptus_leaves",
|
||||
"byg:block/willow_leaves"
|
||||
) setParam("texture", model.texture("top")) setParam("tint", model.tint("top"))
|
||||
end
|
||||
|
||||
// ripe leaves (tint comes from overlay)
|
||||
match model.matches(
|
||||
"byg:block/ripe_joshua_leaves",
|
||||
"byg:block/ripe_orchard_leaves"
|
||||
) setParam("texture", model.texture("top")) setParam("tint", model.tint("overlay"))
|
||||
end
|
||||
|
||||
//
|
||||
// other blocks
|
||||
//
|
||||
match block.name.matches("byg:meadow_dirt") setParam("type", "dirt") end
|
||||
match block.name.matches("byg:overgrown_crimson_blackstone") setParam("type", "mycelium") end
|
||||
|
||||
match model.matches("byg:block/meadow_grass_block", "byg:block/overgrown_stone", "byg:block/overgrown_dacite", "byg:block/overgrown_netherrack")
|
||||
setParam("type", "grass")
|
||||
setParam("texture", model.texture("top")) setParam("tint", model.tint("top"))
|
||||
end
|
||||
|
||||
match block.name.matches("byg:overgrown_stone", "byg:overgrown_dacite", "byg:overgrown_netherrack", "byg:podzol_dacite")
|
||||
setParam("no-connect", "true")
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
match block.class.extends(classOf("desolation:charred_branches")) setParam("type", "leaf") end
|
||||
@@ -0,0 +1 @@
|
||||
match block.class.extends(classOf("environmental:blue_wisteria_leaves")) setParam("type", "leaf") end
|
||||
@@ -0,0 +1,9 @@
|
||||
match isParam("type", "leaf")
|
||||
block.name.contains("spruce", "fir")
|
||||
setParam("particle", "spruce")
|
||||
end
|
||||
|
||||
match isParam("type", "leaf")
|
||||
block.name.contains("jungle", "palm")
|
||||
setParam("particle", "jungle")
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
// Leaves
|
||||
match block.class.extends(classOf("minecraft:oak_leaves")) setParam("type", "leaf") end
|
||||
|
||||
match isParam("type", "leaf")
|
||||
model.extends("minecraft:block/leaves", "minecraft:block/cube_all")
|
||||
setParam("texture", model.texture("all"))
|
||||
setParam("tint", model.tint("all"))
|
||||
end
|
||||
|
||||
// Podzol
|
||||
match block.name.matches("minecraft:podzol") setParam("type", "grass") end
|
||||
|
||||
// Grass
|
||||
match block.class.extends(classOf("minecraft:grass_block")) setParam("type", "grass") end
|
||||
|
||||
match isParam("type", "grass")
|
||||
model.extends("minecraft:block/grass_block", "minecraft:block/cube_bottom_top")
|
||||
setParam("texture", model.texture("top"))
|
||||
setParam("tint", model.tint("top"))
|
||||
end
|
||||
|
||||
// Mycelium & Nylium
|
||||
match block.name.matches("minecraft:mycelium", "minecraft:crimson_nylium", "minecraft:warped_nylium") setParam("type", "mycelium") end
|
||||
|
||||
match isParam("type", "mycelium")
|
||||
model.extends("minecraft:block/cube_bottom_top")
|
||||
setParam("texture", model.texture("top"))
|
||||
setParam("tint", model.tint("top"))
|
||||
end
|
||||
|
||||
// Dirt
|
||||
match block.name.matches("minecraft:dirt") setParam("type", "dirt") end
|
||||
|
||||
// Wood Log
|
||||
match block.class.extends(classOf("minecraft:oak_log")) setParam("type", "round-log") end
|
||||
|
||||
match isParam("type", "round-log")
|
||||
model.extends("minecraft:block/cube_column", "minecraft:block/cube_column_horizontal")
|
||||
setParam("texture-side", model.texture("side"))
|
||||
setParam("texture-end", model.texture("end"))
|
||||
end
|
||||
|
||||
match isParam("type", "round-log")
|
||||
model.extends("minecraft:block/cube_all")
|
||||
setParam("texture-side", model.texture("all"))
|
||||
setParam("texture-end", model.texture("all"))
|
||||
end
|
||||
|
||||
// Sand & Dirt
|
||||
match block.name.matches("minecraft:sand", "minecraft:red_sand") setParam("type", "sand") end
|
||||
|
||||
// Cactus, Lilypad, Netherrack
|
||||
match block.name.matches("minecraft:cactus") setParam("type", "cactus") end
|
||||
match block.name.matches("minecraft:lilypad") setParam("type", "lilypad") end
|
||||
match block.name.matches("minecraft:netherrack") setParam("type", "netherrack") end
|
||||
|
||||
// Crops
|
||||
match block.class.extends(classOf("minecraft:wheat")) setParam("type", "crop") end
|
||||
@@ -1,15 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.TallGrassBlock
|
||||
net.minecraft.block.CropsBlock
|
||||
-net.minecraft.block.ReedBlock
|
||||
-net.minecraft.block.DoublePlantBlock
|
||||
-net.minecraft.block.CarrotBlock
|
||||
-net.minecraft.block.PotatoBlock
|
||||
|
||||
// Biomes O'Plenty
|
||||
biomesoplenty.common.block.BlockBOPFlower
|
||||
biomesoplenty.common.block.BlockBOPTurnip
|
||||
biomesoplenty.common.block.BlockBOPPlant
|
||||
|
||||
// Tinkers' Construct
|
||||
tconstruct.blocks.slime.SlimeTallGrass
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.DirtBlock
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.GrassBlock
|
||||
@@ -1,3 +0,0 @@
|
||||
// Vanilla
|
||||
block/grass_block,top
|
||||
block/cube_bottom_top,top
|
||||
177
src/main/resources/assets/betterfoliage/lang/en_us.json
Normal file
177
src/main/resources/assets/betterfoliage/lang/en_us.json
Normal file
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"key.betterfoliage.gui": "Open Settings",
|
||||
|
||||
"betterfoliage.title": "Better Foliage configuration",
|
||||
|
||||
"betterfoliage.global.enabled": "Enable Mod",
|
||||
"betterfoliage.global.enabled.tooltip": "If set to false, BetterFoliage will not render anything",
|
||||
"betterfoliage.nVidia": "nVidia GPU",
|
||||
"betterfoliage.nVidia.tooltip": "Specify whether you have an nVidia GPU",
|
||||
|
||||
"betterfoliage.enabled": "Enable",
|
||||
"betterfoliage.enabled.tooltip": "Is this feature enabled?",
|
||||
"betterfoliage.hOffset": "Horizontal offset",
|
||||
"betterfoliage.hOffset.tooltip": "The distance this element is shifted horizontally, in blocks",
|
||||
"betterfoliage.vOffset": "Vertical offset",
|
||||
"betterfoliage.vOffset.tooltip": "The distance this element is shifted vertically, in blocks",
|
||||
"betterfoliage.size": "Size",
|
||||
"betterfoliage.size.tooltip": "Size of this element",
|
||||
"betterfoliage.heightMin": "Minimum height",
|
||||
"betterfoliage.heightMin.tooltip": "Minimum height of element",
|
||||
"betterfoliage.heightMax": "Maximum height",
|
||||
"betterfoliage.heightMax.tooltip": "Maximum height of element",
|
||||
"betterfoliage.population": "Population",
|
||||
"betterfoliage.population.tooltip": "Chance (N in 64) that an eligible block will have this feature",
|
||||
"betterfoliage.shaderWind": "Shader wind effects",
|
||||
"betterfoliage.shaderWind.tooltip": "Apply wind effects from ShaderMod shaders to this element?",
|
||||
"betterfoliage.distance": "Distance limit",
|
||||
"betterfoliage.distance.tooltip": "Maximum distance from player at which to render this feature",
|
||||
"betterfoliage.saturationThreshold": "Saturation threshold",
|
||||
"betterfoliage.saturationThreshold.tooltip": "Color saturation cutoff between \"colorless\" blocks (using biome color) and \"colorful\" blocks (using their own specific color)",
|
||||
|
||||
"betterfoliage.rendererror": "§a[BetterFoliage]§f Error rendering block %s at position %s",
|
||||
|
||||
"betterfoliage.shaders": "Shader configuration",
|
||||
"betterfoliage.shaders.tooltip": "Configure integration with shaders",
|
||||
"betterfoliage.shaders.leavesId": "Leaves ID",
|
||||
"betterfoliage.shaders.leavesId.tooltip": "Block ID reported to shader programs for all kinds of leaves. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.",
|
||||
"betterfoliage.shaders.grassId": "Grass ID",
|
||||
"betterfoliage.shaders.grassId.tooltip": "Block ID reported to shader programs for all grasses and crops. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.",
|
||||
|
||||
"betterfoliage.leaves": "Extra Leaves",
|
||||
"betterfoliage.leaves.tooltip": "Extra round leaves on leaf blocks",
|
||||
"betterfoliage.leaves.dense": "Dense mode",
|
||||
"betterfoliage.leaves.dense.tooltip": "Dense mode has more round leaves",
|
||||
"betterfoliage.leaves.snowEnabled": "Enable snow",
|
||||
"betterfoliage.leaves.snowEnabled.tooltip": "Enable snow on extra leaves?",
|
||||
"betterfoliage.leaves.hideInternal": "Hide internal leaves",
|
||||
"betterfoliage.leaves.hideInternal.tooltip": "Skip rendering extra leaves if leaf block is completely surrounded by other leaves or solid blocks",
|
||||
|
||||
"betterfoliage.shortGrass": "Short Grass & Mycelium",
|
||||
"betterfoliage.shortGrass.tooltip": "Tufts of grass/mycelium on top of appropriate blocks",
|
||||
"betterfoliage.shortGrass.useGenerated": "Use generated texture for grass",
|
||||
"betterfoliage.shortGrass.useGenerated.tooltip": "Generated texture is made by slicing the tallgrass texture from the active resource pack in half",
|
||||
"betterfoliage.shortGrass.myceliumEnabled": "Enable Mycelium",
|
||||
"betterfoliage.shortGrass.myceliumEnabled.tooltip": "Is this feature enabled for mycelium blocks?",
|
||||
"betterfoliage.shortGrass.grassEnabled": "Enable Grass",
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "Is this feature enabled for grass blocks?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "Enable under snow",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "Enable on snowed grass blocks?",
|
||||
|
||||
"betterfoliage.connectedGrass": "Connected grass textures",
|
||||
"betterfoliage.connectedGrass.tooltip": "Connected grass textures",
|
||||
"betterfoliage.connectedGrass.enabled": "Enable",
|
||||
"betterfoliage.connectedGrass.enabled.tooltip": "If there is a grass block on top of a dirt block: draw grass top texture on all grass block sides,",
|
||||
"betterfoliage.connectedGrass.snowEnabled": "Enable under snow",
|
||||
"betterfoliage.connectedGrass.snowEnabled.tooltip": "Enable on snowed grass blocks?",
|
||||
|
||||
"betterfoliage.hangingGrass": "Hanging Grass",
|
||||
"betterfoliage.hangingGrass.tooltip": "Grass tufts hanging down from the top edges of grass blocks",
|
||||
"betterfoliage.hangingGrass.separation": "Separation",
|
||||
"betterfoliage.hangingGrass.separation.tooltip": "How much the hanging grass stands out from the block",
|
||||
|
||||
"betterfoliage.cactus": "Better Cactus",
|
||||
"betterfoliage.cactus.tooltip": "Enhance cactus with extra bits and smooth shading",
|
||||
"betterfoliage.cactus.sizeVariation": "Size variation",
|
||||
"betterfoliage.cactus.sizeVariation.tooltip": "Amount of random variation on cactus size",
|
||||
|
||||
"betterfoliage.lilypad": "Better Lilypad",
|
||||
"betterfoliage.lilypad.tooltip": "Enhance lilypad with roots and occasional flowers",
|
||||
"betterfoliage.lilypad.flowerChance": "Flower chance",
|
||||
"betterfoliage.lilypad.flowerChance.tooltip": "Chance (N in 64) of a lilypad having a flower on it",
|
||||
|
||||
"betterfoliage.reed": "Reeds",
|
||||
"betterfoliage.reed.tooltip": "Reeds on dirt blocks in shallow water",
|
||||
"betterfoliage.reed.biomes": "Biome List",
|
||||
"betterfoliage.reed.biomes.tooltip": "Configure which biomes reeds are allowed to appear in",
|
||||
"betterfoliage.reed.biomes.tooltip.element": "Should reeds appear in the %s biome?",
|
||||
|
||||
"betterfoliage.algae": "Algae",
|
||||
"betterfoliage.algae.tooltip": "Algae on dirt blocks in deep water",
|
||||
"betterfoliage.algae.biomes": "Biome List",
|
||||
"betterfoliage.algae.biomes.tooltip": "Configure which biomes algae is allowed to appear in",
|
||||
"betterfoliage.algae.biomes.tooltip.element": "Should algae appear in the %s biome?",
|
||||
|
||||
"betterfoliage.coral": "Coral",
|
||||
"betterfoliage.coral.tooltip": "Coral on sand blocks in deep water",
|
||||
"betterfoliage.coral.size": "Coral size",
|
||||
"betterfoliage.coral.size.tooltip": "Size of coral bits sticking out",
|
||||
"betterfoliage.coral.crustSize": "Crust size",
|
||||
"betterfoliage.coral.crustSize.tooltip": "Size of the flat coral part",
|
||||
"betterfoliage.coral.chance": "Coral chance",
|
||||
"betterfoliage.coral.chance.tooltip": "Chance (N in 64) of a specific face of the block to show coral",
|
||||
"betterfoliage.coral.biomes": "Biome List",
|
||||
"betterfoliage.coral.biomes.tooltip": "Configure which biomes coral is allowed to appear in",
|
||||
"betterfoliage.coral.biomes.tooltip.element": "Should coral appear in the %s biome?",
|
||||
"betterfoliage.coral.shallowWater": "Shallow water coral",
|
||||
"betterfoliage.coral.shallowWater.tooltip": "Should coral appear in 1 block deep water?",
|
||||
|
||||
"betterfoliage.netherrack": "Netherrack Vines",
|
||||
"betterfoliage.netherrack.tooltip": "Hanging Vines under netherrack",
|
||||
|
||||
"betterfoliage.fallingLeaves": "Falling leaves",
|
||||
"betterfoliage.fallingLeaves.tooltip": "Falling leaf particle FX emitted from the bottom of leaf blocks",
|
||||
"betterfoliage.fallingLeaves.speed": "Particle speed",
|
||||
"betterfoliage.fallingLeaves.speed.tooltip": "Overall particle speed",
|
||||
"betterfoliage.fallingLeaves.windStrength": "Wind strength",
|
||||
"betterfoliage.fallingLeaves.windStrength.tooltip": "Magnitude of wind effects in good weather (spread of normal distribution centered on 0)",
|
||||
"betterfoliage.fallingLeaves.stormStrength": "Storm strength",
|
||||
"betterfoliage.fallingLeaves.stormStrength.tooltip": "Additional magnitude of wind effects in rainy weather (spread of normal distribution centered on 0)",
|
||||
"betterfoliage.fallingLeaves.size": "Particle size",
|
||||
"betterfoliage.fallingLeaves.chance": "Particle chance",
|
||||
"betterfoliage.fallingLeaves.chance.tooltip": "Chance of each random render tick hitting a leaf block to spawn a particle",
|
||||
"betterfoliage.fallingLeaves.perturb": "Perturbation",
|
||||
"betterfoliage.fallingLeaves.perturb.tooltip": "Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle synchronized to its rotation",
|
||||
"betterfoliage.fallingLeaves.lifetime": "Maximum lifetime",
|
||||
"betterfoliage.fallingLeaves.lifetime.tooltip": "Maximum lifetime of particle in seconds. Minimum lifetime is 60%% of this value",
|
||||
"betterfoliage.fallingLeaves.opacityHack": "Opaque particles",
|
||||
"betterfoliage.fallingLeaves.opacityHack.tooltip": "Stop transparent blocks obscuring particles even when particle is in front. WARNING: may cause glitches.",
|
||||
|
||||
"betterfoliage.risingSoul": "Rising souls",
|
||||
"betterfoliage.risingSoul.tooltip": "Rising soul particle FX emitted from the top of soulsand blocks",
|
||||
"betterfoliage.risingSoul.chance": "Particle chance",
|
||||
"betterfoliage.risingSoul.chance.tooltip": "Chance of each random render tick hitting a soulsand block to spawn a particle",
|
||||
"betterfoliage.risingSoul.speed": "Particle speed",
|
||||
"betterfoliage.risingSoul.speed.tooltip": "Vertical speed of soul particles",
|
||||
"betterfoliage.risingSoul.perturb": "Perturbation",
|
||||
"betterfoliage.risingSoul.perturb.tooltip": "Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle",
|
||||
"betterfoliage.risingSoul.headSize": "Soul size",
|
||||
"betterfoliage.risingSoul.headSize.tooltip": "Size of the soul particle",
|
||||
"betterfoliage.risingSoul.trailSize": "Trail size",
|
||||
"betterfoliage.risingSoul.trailSize.tooltip": "Initial size of the particle trail",
|
||||
"betterfoliage.risingSoul.opacity": "Opacity",
|
||||
"betterfoliage.risingSoul.opacity.tooltip": "Opacity of the particle effect",
|
||||
"betterfoliage.risingSoul.sizeDecay": "Size decay",
|
||||
"betterfoliage.risingSoul.sizeDecay.tooltip": "Trail particle size relative to its size in the previous tick",
|
||||
"betterfoliage.risingSoul.opacityDecay": "Opacity decay",
|
||||
"betterfoliage.risingSoul.opacityDecay.tooltip": "Trail particle opacity relative to its opacity in the previous tick",
|
||||
"betterfoliage.risingSoul.lifetime": "Maximum lifetime",
|
||||
"betterfoliage.risingSoul.lifetime.tooltip": "Maximum lifetime of particle effect in seconds. Minimum lifetime is 60%% of this value",
|
||||
"betterfoliage.risingSoul.trailLength": "Trail length",
|
||||
"betterfoliage.risingSoul.trailLength.tooltip": "Number of previous positions the particle remembers in ticks",
|
||||
"betterfoliage.risingSoul.trailDensity": "Trail density",
|
||||
"betterfoliage.risingSoul.trailDensity.tooltip": "Render every Nth previous position in the particle trail",
|
||||
|
||||
"betterfoliage.roundLogs": "Round Logs",
|
||||
"betterfoliage.roundLogs.tooltip": "Round Logs",
|
||||
"betterfoliage.roundLogs.connectSolids": "Connect to solid",
|
||||
"betterfoliage.roundLogs.connectSolids.tooltip": "Connect round blocks to solid full blocks?",
|
||||
"betterfoliage.roundLogs.connectPerpendicular": "Connect to perpendicular logs",
|
||||
"betterfoliage.roundLogs.connectPerpendicular.tooltip": "Connect round logs to perpendicular logs along its axis?",
|
||||
"betterfoliage.roundLogs.lenientConnect": "Lenient rounding",
|
||||
"betterfoliage.roundLogs.lenientConnect.tooltip": "Connect parallel round logs in an L-shape too, not just 2x2",
|
||||
"betterfoliage.roundLogs.connectGrass": "Connect Grass",
|
||||
"betterfoliage.roundLogs.connectGrass.tooltip": "Render grass block under trees instead of dirt if there is grass nearby",
|
||||
"betterfoliage.roundLogs.radiusSmall": "Chamfer radius",
|
||||
"betterfoliage.roundLogs.radiusSmall.tooltip": "How much to chop off from the log corner",
|
||||
"betterfoliage.roundLogs.radiusLarge": "Connected chamfer radius",
|
||||
"betterfoliage.roundLogs.radiusLarge.tooltip": "How much to chop off from the outer corner of connected logs",
|
||||
"betterfoliage.roundLogs.dimming": "Dimming",
|
||||
"betterfoliage.roundLogs.dimming.tooltip": "Amount to darken obscured log faces",
|
||||
"betterfoliage.roundLogs.zProtection": "Z-Protection",
|
||||
"betterfoliage.roundLogs.zProtection.tooltip": "Amount to scale parallel log connection bits to stop Z-fighting (flickering). Try to set it as high as possible without having glitches.",
|
||||
"betterfoliage.roundLogs.defaultY": "Default to vertical",
|
||||
"betterfoliage.roundLogs.defaultY.tooltip": "If true, log blocks where the orientation cannot be determined will be rendered as vertical. Otherwise, they will be rendered as cube blocks.",
|
||||
"betterfoliage.roundLogs.plantsOnly": "Plants only",
|
||||
"betterfoliage.roundLogs.plantsOnly.tooltip": "If true, only blocks with plant materials (wood, grass) will be rounded. If false, all column blocks will be rounded, including stone columns."
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
key.betterfoliage.gui=Open Settings
|
||||
|
||||
betterfoliage.global.enabled=Enable Mod
|
||||
betterfoliage.global.enabled.tooltip=If set to false, BetterFoliage will not render anything
|
||||
betterfoliage.global.nVidia=nVidia GPU
|
||||
betterfoliage.global.nVidia.tooltip=Specify whether you have an nVidia GPU
|
||||
|
||||
betterfoliage.enabled=Enable
|
||||
betterfoliage.enabled.tooltip=Is this feature enabled?
|
||||
betterfoliage.hOffset=Horizontal offset
|
||||
betterfoliage.hOffset.tooltip=The distance this element is shifted horizontally, in blocks
|
||||
betterfoliage.vOffset=Vertical offset
|
||||
betterfoliage.vOffset.tooltip=The distance this element is shifted vertically, in blocks
|
||||
betterfoliage.size=Size
|
||||
betterfoliage.size.tooltip=Size of this element
|
||||
betterfoliage.heightMin=Minimum height
|
||||
betterfoliage.heightMin.tooltip=Minimum height of element
|
||||
betterfoliage.heightMax=Maximum height
|
||||
betterfoliage.heightMax.tooltip=Maximum height of element
|
||||
betterfoliage.population=Population
|
||||
betterfoliage.population.tooltip=Chance (N in 64) that an eligible block will have this feature
|
||||
betterfoliage.shaderWind=Shader wind effects
|
||||
betterfoliage.shaderWind.tooltip=Apply wind effects from ShaderMod shaders to this element?
|
||||
betterfoliage.distance=Distance limit
|
||||
betterfoliage.distance.tooltip=Maximum distance from player at which to render this feature
|
||||
|
||||
betterfoliage.rendererror=§a[BetterFoliage]§f Error rendering block %s at position %s
|
||||
|
||||
betterfoliage.blocks=Block Types
|
||||
betterfoliage.blocks.tooltip=Configure lists of block classes that will have specific features applied to them
|
||||
|
||||
betterfoliage.blocks.dirtWhitelist=Dirt Whitelist
|
||||
betterfoliage.blocks.dirtBlacklist=Dirt Blacklist
|
||||
betterfoliage.blocks.dirtWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.dirtBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.dirtWhitelist.tooltip=Blocks recognized as Dirt. Has an impact on Reeds, Algae, Connected Grass
|
||||
betterfoliage.blocks.dirtBlacklist.tooltip=Blocks never accepted as Dirt. Has an impact on Reeds, Algae, Connected Grass
|
||||
|
||||
betterfoliage.blocks.grassClassesWhitelist=Grass Whitelist
|
||||
betterfoliage.blocks.grassClassesBlacklist=Grass Blacklist
|
||||
betterfoliage.blocks.grassClassesWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.grassClassesBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.grassModels=Grass Models
|
||||
betterfoliage.blocks.grassModels.arrayEntry=%d entries
|
||||
betterfoliage.blocks.grassWhitelist.tooltip=Blocks recognized as Grass. Has an impact on Short Grass, Connected Grass
|
||||
betterfoliage.blocks.grassBlacklist.tooltip=Blocks never accepted as Grass. Has an impact on Short Grass, Connected Grass
|
||||
betterfoliage.blocks.grassModels.tooltip=Models and textures recognized for grass blocks
|
||||
|
||||
betterfoliage.blocks.leavesClassesWhitelist=Leaves Whitelist
|
||||
betterfoliage.blocks.leavesClassesBlacklist=Leaves Blacklist
|
||||
betterfoliage.blocks.leavesClassesWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.leavesClassesBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.leavesModels=Leaves Models
|
||||
betterfoliage.blocks.leavesModels.arrayEntry=%d entries
|
||||
betterfoliage.blocks.leavesClassesWhitelist.tooltip=Blocks recognized as Leaves. Has an impact on Extra Leaves, Falling Leaves. Leaves will render with leaves block ID in shader programs
|
||||
betterfoliage.blocks.leavesClassesBlacklist.tooltip=Blocks never accepted as Leaves. Has an impact on Extra Leaves, Falling Leaves. Leaves will render with leaves block ID in shader programs
|
||||
betterfoliage.blocks.leavesModels.tooltip=Models and textures recognized for leaves blocks
|
||||
|
||||
betterfoliage.blocks.cropsWhitelist=Crop Whitelist
|
||||
betterfoliage.blocks.cropsBlacklist=Crop Blacklist
|
||||
betterfoliage.blocks.cropsWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cropsBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cropsWhitelist.tooltip=Blocks recognized as crops. Crops will render with tallgrass block ID in shader programs
|
||||
betterfoliage.blocks.cropsBlacklist.tooltip=Blocks never accepted as crops. Crops will render with tallgrass block ID in shader programs
|
||||
|
||||
betterfoliage.blocks.logClassesWhitelist=Wood Log Whitelist
|
||||
betterfoliage.blocks.logClassesBlacklist=Wood Log Blacklist
|
||||
betterfoliage.blocks.logClassesWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.logClassesBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.logModels=Wood Log Models
|
||||
betterfoliage.blocks.logModels.arrayEntry=%d entries
|
||||
betterfoliage.blocks.logClassesWhitelist.tooltip=Blocks recognized as wooden logs. Has an impact on Rounded Logs
|
||||
betterfoliage.blocks.logClassesBlacklist.tooltip=Blocks never accepted as wooden logs. Has an impact on Rounded Logs
|
||||
betterfoliage.blocks.logModels.tooltip=Models and textures recognized for wood log blocks
|
||||
|
||||
betterfoliage.blocks.sandWhitelist=Sand Whitelist
|
||||
betterfoliage.blocks.sandBlacklist=Sand Blacklist
|
||||
betterfoliage.blocks.sandWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.sandBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.sandWhitelist.tooltip=Blocks recognized as Sand. Has an impact on Coral
|
||||
betterfoliage.blocks.sandBlacklist.tooltip=Blocks never accepted Sand. Has an impact on Coral
|
||||
|
||||
betterfoliage.blocks.lilypadWhitelist=Lilypad Whitelist
|
||||
betterfoliage.blocks.lilypadBlacklist=Lilypad Blacklist
|
||||
betterfoliage.blocks.lilypadWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.lilypadBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.lilypadWhitelist.tooltip=Blocks recognized as Lilypad. Has an impact on Better Lilypad
|
||||
betterfoliage.blocks.lilypadBlacklist.tooltip=Blocks never accepted Lilypad. Has an impact on Better Lilypad
|
||||
|
||||
betterfoliage.blocks.cactusWhitelist=Cactus Whitelist
|
||||
betterfoliage.blocks.cactusBlacklist=Cactus Blacklist
|
||||
betterfoliage.blocks.cactusWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cactusBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cactusWhitelist.tooltip=Blocks recognized as Cactus. Has an impact on Better Cactus
|
||||
betterfoliage.blocks.cactusBlacklist.tooltip=Blocks never accepted Cactus. Has an impact on Better Cactus
|
||||
|
||||
betterfoliage.blocks.myceliumWhitelist=Mycelium Whitelist
|
||||
betterfoliage.blocks.myceliumBlacklist=Mycelium Blacklist
|
||||
betterfoliage.blocks.myceliumWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.myceliumBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.myceliumWhitelist.tooltip=Blocks recognized as Mycelium. Has an impact on Better Grass
|
||||
betterfoliage.blocks.myceliumBlacklist.tooltip=Blocks never accepted Mycelium. Has an impact on Better Grass
|
||||
|
||||
betterfoliage.blocks.netherrackWhitelist=Netherrack Whitelist
|
||||
betterfoliage.blocks.netherrackBlacklist=Netherrack Blacklist
|
||||
betterfoliage.blocks.netherrackWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.netherrackBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.netherrackWhitelist.tooltip=Blocks recognized as Netherrack. Has an impact on Netherrack Vines
|
||||
betterfoliage.blocks.netherrackBlacklist.tooltip=Blocks never accepted Netherrack. Has an impact on Netherrack Vines
|
||||
|
||||
betterfoliage.shaders=Shader configuration
|
||||
betterfoliage.shaders.tooltip=Configure integration with shaders
|
||||
betterfoliage.shaders.leavesId=Leaves ID
|
||||
betterfoliage.shaders.leavesId.tooltip=Block ID reported to shader programs for all kinds of leaves. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.
|
||||
betterfoliage.shaders.grassId=Grass ID
|
||||
betterfoliage.shaders.grassId.tooltip=Block ID reported to shader programs for all grasses and crops. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.
|
||||
|
||||
betterfoliage.leaves=Extra Leaves
|
||||
betterfoliage.leaves.tooltip=Extra round leaves on leaf blocks
|
||||
betterfoliage.leaves.dense=Dense mode
|
||||
betterfoliage.leaves.dense.tooltip=Dense mode has more round leaves
|
||||
betterfoliage.leaves.snowEnabled=Enable snow
|
||||
betterfoliage.leaves.snowEnabled.tooltip=Enable snow on extra leaves?
|
||||
betterfoliage.leaves.hideInternal=Hide internal leaves
|
||||
betterfoliage.leaves.hideInternal.tooltip=Skip rendering extra leaves if leaf block is completely surrounded by other leaves or solid blocks
|
||||
|
||||
betterfoliage.shortGrass=Short Grass & Mycelium
|
||||
betterfoliage.shortGrass.tooltip=Tufts of grass/mycelium on top of appropriate blocks
|
||||
betterfoliage.shortGrass.useGenerated=Use generated texture for grass
|
||||
betterfoliage.shortGrass.useGenerated.tooltip=Generated texture is made by slicing the tallgrass texture from the active resource pack in half
|
||||
betterfoliage.shortGrass.myceliumEnabled=Enable Mycelium
|
||||
betterfoliage.shortGrass.myceliumEnabled.tooltip=Is this feature enabled for mycelium blocks?
|
||||
betterfoliage.shortGrass.grassEnabled=Enable Grass
|
||||
betterfoliage.shortGrass.grassEnabled.tooltip=Is this feature enabled for grass blocks?
|
||||
betterfoliage.shortGrass.snowEnabled=Enable under snow
|
||||
betterfoliage.shortGrass.snowEnabled.tooltip=Enable on snowed grass blocks?
|
||||
betterfoliage.shortGrass.saturationThreshold=Saturation threshold
|
||||
betterfoliage.shortGrass.saturationThreshold.tooltip=Color saturation cutoff between "colorless" blocks (using biome color) and "colorful" blocks (using their own specific color)
|
||||
|
||||
betterfoliage.hangingGrass=Hanging Grass
|
||||
betterfoliage.hangingGrass.tooltip=Grass tufts hanging down from the top edges of grass blocks
|
||||
betterfoliage.hangingGrass.separation=Separation
|
||||
betterfoliage.hangingGrass.separation.tooltip=How much the hanging grass stands out from the block
|
||||
|
||||
betterfoliage.cactus=Better Cactus
|
||||
betterfoliage.cactus.tooltip=Enhance cactus with extra bits and smooth shading
|
||||
betterfoliage.cactus.sizeVariation=Size variation
|
||||
betterfoliage.cactus.sizeVariation.tooltip=Amount of random variation on cactus size
|
||||
|
||||
betterfoliage.lilypad=Better Lilypad
|
||||
betterfoliage.lilypad.tooltip=Enhance lilypad with roots and occasional flowers
|
||||
betterfoliage.lilypad.flowerChance=Flower chance
|
||||
betterfoliage.lilypad.flowerChance.tooltip=Chance (N in 64) of a lilypad having a flower on it
|
||||
|
||||
betterfoliage.reed=Reeds
|
||||
betterfoliage.reed.tooltip=Reeds on dirt blocks in shallow water
|
||||
betterfoliage.reed.biomes=Biome List
|
||||
betterfoliage.reed.biomes.tooltip=Configure which biomes reeds are allowed to appear in
|
||||
betterfoliage.reed.biomes.tooltip.element=Should reeds appear in the %s biome?
|
||||
|
||||
betterfoliage.algae=Algae
|
||||
betterfoliage.algae.tooltip=Algae on dirt blocks in deep water
|
||||
betterfoliage.algae.biomes=Biome List
|
||||
betterfoliage.algae.biomes.tooltip=Configure which biomes algae is allowed to appear in
|
||||
betterfoliage.algae.biomes.tooltip.element=Should algae appear in the %s biome?
|
||||
|
||||
betterfoliage.coral=Coral
|
||||
betterfoliage.coral.tooltip=Coral on sand blocks in deep water
|
||||
betterfoliage.coral.size=Coral size
|
||||
betterfoliage.coral.size.tooltip=Size of coral bits sticking out
|
||||
betterfoliage.coral.crustSize=Crust size
|
||||
betterfoliage.coral.crustSize.tooltip=Size of the flat coral part
|
||||
betterfoliage.coral.chance=Coral chance
|
||||
betterfoliage.coral.chance.tooltip=Chance (N in 64) of a specific face of the block to show coral
|
||||
betterfoliage.coral.biomes=Biome List
|
||||
betterfoliage.coral.biomes.tooltip=Configure which biomes coral is allowed to appear in
|
||||
betterfoliage.coral.biomes.tooltip.element=Should coral appear in the %s biome?
|
||||
betterfoliage.coral.shallowWater=Shallow water coral
|
||||
betterfoliage.coral.shallowWater.tooltip=Should coral appear in 1 block deep water?
|
||||
|
||||
betterfoliage.netherrack=Netherrack Vines
|
||||
betterfoliage.netherrack.tooltip=Hanging Vines under netherrack
|
||||
|
||||
betterfoliage.fallingLeaves=Falling leaves
|
||||
betterfoliage.fallingLeaves.tooltip=Falling leaf particle FX emitted from the bottom of leaf blocks
|
||||
betterfoliage.fallingLeaves.speed=Particle speed
|
||||
betterfoliage.fallingLeaves.speed.tooltip=Overall particle speed
|
||||
betterfoliage.fallingLeaves.windStrength=Wind strength
|
||||
betterfoliage.fallingLeaves.windStrength.tooltip=Magnitude of wind effects in good weather (spread of normal distribution centered on 0)
|
||||
betterfoliage.fallingLeaves.stormStrength=Storm strength
|
||||
betterfoliage.fallingLeaves.stormStrength.tooltip=Additional magnitude of wind effects in rainy weather (spread of normal distribution centered on 0)
|
||||
betterfoliage.fallingLeaves.size=Particle size
|
||||
betterfoliage.fallingLeaves.chance=Particle chance
|
||||
betterfoliage.fallingLeaves.chance.tooltip=Chance of each random render tick hitting a leaf block to spawn a particle
|
||||
betterfoliage.fallingLeaves.perturb=Perturbation
|
||||
betterfoliage.fallingLeaves.perturb.tooltip=Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle synchronized to its rotation
|
||||
betterfoliage.fallingLeaves.lifetime=Maximum lifetime
|
||||
betterfoliage.fallingLeaves.lifetime.tooltip=Maximum lifetime of particle in seconds. Minimum lifetime is 60%% of this value
|
||||
betterfoliage.fallingLeaves.opacityHack=Opaque particles
|
||||
betterfoliage.fallingLeaves.opacityHack.tooltip=Stop transparent blocks obscuring particles even when particle is in front. WARNING: may cause glitches.
|
||||
|
||||
betterfoliage.risingSoul=Rising souls
|
||||
betterfoliage.risingSoul.tooltip=Rising soul particle FX emitted from the top of soulsand blocks
|
||||
betterfoliage.risingSoul.chance=Particle chance
|
||||
betterfoliage.risingSoul.chance.tooltip=Chance of each random render tick hitting a soulsand block to spawn a particle
|
||||
betterfoliage.risingSoul.speed=Particle speed
|
||||
betterfoliage.risingSoul.speed.tooltip=Vertical speed of soul particles
|
||||
betterfoliage.risingSoul.perturb=Perturbation
|
||||
betterfoliage.risingSoul.perturb.tooltip=Magnitude of perturbation effect. Adds a corkscrew-like motion to the particle
|
||||
betterfoliage.risingSoul.headSize=Soul size
|
||||
betterfoliage.risingSoul.headSize.tooltip=Size of the soul particle
|
||||
betterfoliage.risingSoul.trailSize=Trail size
|
||||
betterfoliage.risingSoul.trailSize.tooltip=Initial size of the particle trail
|
||||
betterfoliage.risingSoul.opacity=Opacity
|
||||
betterfoliage.risingSoul.opacity.tooltip=Opacity of the particle effect
|
||||
betterfoliage.risingSoul.sizeDecay=Size decay
|
||||
betterfoliage.risingSoul.sizeDecay.tooltip=Trail particle size relative to its size in the previous tick
|
||||
betterfoliage.risingSoul.opacityDecay=Opacity decay
|
||||
betterfoliage.risingSoul.opacityDecay.tooltip=Trail particle opacity relative to its opacity in the previous tick
|
||||
betterfoliage.risingSoul.lifetime=Maximum lifetime
|
||||
betterfoliage.risingSoul.lifetime.tooltip=Maximum lifetime of particle effect in seconds. Minimum lifetime is 60%% of this value
|
||||
betterfoliage.risingSoul.trailLength=Trail length
|
||||
betterfoliage.risingSoul.trailLength.tooltip=Number of previous positions the particle remembers in ticks
|
||||
betterfoliage.risingSoul.trailDensity=Trail density
|
||||
betterfoliage.risingSoul.trailDensity.tooltip=Render every Nth previous position in the particle trail
|
||||
|
||||
betterfoliage.connectedGrass=Connected grass textures
|
||||
betterfoliage.connectedGrass.enabled=Enable
|
||||
betterfoliage.connectedGrass.enabled.tooltip=If there is a grass block on top of a dirt block: draw grass top texture on all grass block sides,
|
||||
|
||||
betterfoliage.roundLogs=Round Logs
|
||||
betterfoliage.roundLogs.tooltip=Connect round blocks to solid full blocks?
|
||||
betterfoliage.roundLogs.connectSolids=Connect to solid
|
||||
betterfoliage.roundLogs.connectSolids.tooltip=Connect round blocks to solid full blocks?
|
||||
betterfoliage.roundLogs.connectPerpendicular=Connect to perpendicular logs
|
||||
betterfoliage.roundLogs.connectPerpendicular.tooltip=Connect round logs to perpendicular logs along its axis?
|
||||
betterfoliage.roundLogs.lenientConnect=Lenient rounding
|
||||
betterfoliage.roundLogs.lenientConnect.tooltip=Connect parallel round logs in an L-shape too, not just 2x2
|
||||
betterfoliage.roundLogs.connectGrass=Connect Grass
|
||||
betterfoliage.roundLogs.connectGrass.tooltip=Render grass block under trees instead of dirt if there is grass nearby
|
||||
betterfoliage.roundLogs.radiusSmall=Chamfer radius
|
||||
betterfoliage.roundLogs.radiusSmall.tooltip=How much to chop off from the log corner
|
||||
betterfoliage.roundLogs.radiusLarge=Connected chamfer radius
|
||||
betterfoliage.roundLogs.radiusLarge.tooltip=How much to chop off from the outer corner of connected logs
|
||||
betterfoliage.roundLogs.dimming=Dimming
|
||||
betterfoliage.roundLogs.dimming.tooltip=Amount to darken obscured log faces
|
||||
betterfoliage.roundLogs.zProtection=Z-Protection
|
||||
betterfoliage.roundLogs.zProtection.tooltip=Amount to scale parallel log connection bits to stop Z-fighting (flickering). Try to set it as high as possible without having glitches.
|
||||
betterfoliage.roundLogs.defaultY=Default to vertical
|
||||
betterfoliage.roundLogs.defaultY.tooltip=If true, log blocks where the orientation cannot be determined will be rendered as vertical. Otherwise, they will be rendered as cube blocks.
|
||||
155
src/main/resources/assets/betterfoliage/lang/ko_kr.json
Normal file
155
src/main/resources/assets/betterfoliage/lang/ko_kr.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"key.betterfoliage.gui": "설정",
|
||||
|
||||
"betterfoliage.global.enabled": "모드 활성화",
|
||||
"betterfoliage.global.enabled.tooltip": "비활성화 할 경우, 환경강화모드 렌더링이 보이지 않습니다.",
|
||||
|
||||
"betterfoliage.enabled": "활성화",
|
||||
"betterfoliage.enabled.tooltip": "이 기능이 활성화 되어 있습니까?",
|
||||
"betterfoliage.hOffset": "수평(가로) 상쇄시키다",
|
||||
"betterfoliage.hOffset.tooltip": "이 성분이 블럭 수평으로 이동된다.",
|
||||
"betterfoliage.vOffset": "수직(세로) 상쇄시키다",
|
||||
"betterfoliage.vOffset.tooltip": "이 성분이 블럭 수직으로 이동된다.",
|
||||
"betterfoliage.size": "사이즈(크기)",
|
||||
"betterfoliage.size.tooltip": "사이즈(크기)의 최소",
|
||||
"betterfoliage.heightMin": "최소한 높이",
|
||||
"betterfoliage.heightMin.tooltip": "높이 최소한의 최소",
|
||||
"betterfoliage.heightMax": "최대한 높이",
|
||||
"betterfoliage.heightMax.tooltip": "높이 최대한의 최소",
|
||||
"betterfoliage.population": "주민",
|
||||
"betterfoliage.population.tooltip": "자격을 갖춘 블럭의 기능 확률(N 분의 64)",
|
||||
"betterfoliage.shaderWind": "쉐이더 바람 효과",
|
||||
"betterfoliage.shaderWind.tooltip": "바람효과를 쉐이더에 적용시키겠습니까?",
|
||||
"betterfoliage.distance": "거리 제한",
|
||||
"betterfoliage.distance.tooltip": "이 기능을 렌더링하는 플레이어의 최대거리",
|
||||
"betterfoliage.saturationThreshold": "채도 임계값",
|
||||
"betterfoliage.saturationThreshold.tooltip": "(특정 색상을 사용하여)\"무채색\"블록과 (바이옴 색을 사용하여)\"화려한\"블록 사이의 채도 차단",
|
||||
|
||||
"betterfoliage.leaves": "잎 추가",
|
||||
"betterfoliage.leaves.tooltip": "둥글게 나뭇잎을 추가시켜줍니다.",
|
||||
"betterfoliage.leaves.dense": "조밀한 모드",
|
||||
"betterfoliage.leaves.dense.tooltip": "조밀한 모드는 둥근 나뭇잎을 더 추가시켜줍니다.",
|
||||
|
||||
"betterfoliage.shortGrass": "이쁜 잔디 & 균사체",
|
||||
"betterfoliage.shortGrass.tooltip": "블록 상단에 잔디 / 균사체",
|
||||
"betterfoliage.shortGrass.useGenerated": "잔디텍스쳐를 활성화 합니다.",
|
||||
"betterfoliage.shortGrass.useGenerated.tooltip": "소스팩의 일부분에 활성화 되어있는 큰잔디 텍스쳐를 생성시킵니다.",
|
||||
"betterfoliage.shortGrass.myceliumEnabled": "균사체 활성화",
|
||||
"betterfoliage.shortGrass.myceliumEnabled.tooltip": "균사체 블록에 있는 기능을 활성화 시키겠습니까?",
|
||||
"betterfoliage.shortGrass.grassEnabled": "잔디 활성화",
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "잔디 블록에 있는 기능을 활성화 시키겠습니까?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "눈 활성화",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "잔디 블록위에 있는 눈을 활성화 시키겠습니까?",
|
||||
|
||||
"betterfoliage.hangingGrass": "매달려있는 잔디",
|
||||
"betterfoliage.hangingGrass.tooltip": "잔디 블록 상단 가장자리에서 아래로 매달려 있는 잔디 다발",
|
||||
"betterfoliage.hangingGrass.separation": "분리",
|
||||
"betterfoliage.hangingGrass.separation.tooltip": "잔디블럭에 매달려있는 잔디의 양 ",
|
||||
|
||||
"betterfoliage.cactus": "선인장",
|
||||
"betterfoliage.cactus.tooltip": "선인장의 비트 수와 부드러운 그림자를 추가",
|
||||
"betterfoliage.cactus.sizeVariation": "사이즈 변화",
|
||||
"betterfoliage.cactus.sizeVariation.tooltip": "선인장의 사이즈 크기를 무작위 변화 시킵니다.",
|
||||
|
||||
"betterfoliage.lilypad": "연꽃잎",
|
||||
"betterfoliage.lilypad.tooltip": "연꽃의 뿌리와 약간의 꽃 추가",
|
||||
"betterfoliage.lilypad.flowerChance": "꽃 확률",
|
||||
"betterfoliage.lilypad.flowerChance.tooltip": "연꽃 위에 있는 꽃 확률(N 분의 64)",
|
||||
|
||||
"betterfoliage.reed": "갈대",
|
||||
"betterfoliage.reed.tooltip": "물 속에서 흙블럭 위에 있는 갈대",
|
||||
"betterfoliage.reed.biomes": "바이옴 리스트",
|
||||
"betterfoliage.reed.biomes.tooltip": "갈대를 바이옴에 따라 표시",
|
||||
"betterfoliage.reed.biomes.tooltip.element": "갈대를 %s 바이옴에 따라 나타내시겠습니까? ",
|
||||
|
||||
"betterfoliage.algae": "해조류",
|
||||
"betterfoliage.algae.tooltip": "깊은 물 속 흙 블럭 위에 있는 해조류",
|
||||
"betterfoliage.algae.biomes": "바이옴 리스트",
|
||||
"betterfoliage.algae.biomes.tooltip": "해조류를 바이옴에 따라 표시",
|
||||
"betterfoliage.algae.biomes.tooltip.element": "해조류를 %s 바이옴에 따라 나타내시겠습니까? ",
|
||||
|
||||
"betterfoliage.coral": "산호",
|
||||
"betterfoliage.coral.tooltip": "깊은 물 속 모래 블럭 위에있는 산호",
|
||||
"betterfoliage.coral.size": "산호 사이즈(크기)",
|
||||
"betterfoliage.coral.size.tooltip": "산호 이미지만큼 적용",
|
||||
"betterfoliage.coral.crustSize": "껍질 크기",
|
||||
"betterfoliage.coral.crustSize.tooltip": "산호 부분의 크기 ",
|
||||
"betterfoliage.coral.chance": "산호 확률",
|
||||
"betterfoliage.coral.chance.tooltip": "산호를 특정블록에 표시하는 확률(N 분의 64)",
|
||||
"betterfoliage.coral.biomes": "바이옴 리스트",
|
||||
"betterfoliage.coral.biomes.tooltip": "산호를 바이옴에 따라 표시",
|
||||
"betterfoliage.coral.biomes.tooltip.element": "산호를 %s 바이옴에 따라 나타내시겠습니까?",
|
||||
"betterfoliage.coral.shallowWater": "얕은 물 산호",
|
||||
"betterfoliage.coral.shallowWater.tooltip": "산호를 깊은 물에 표시하시겠습니까?",
|
||||
|
||||
"betterfoliage.netherrack": "네더랙 덩굴",
|
||||
"betterfoliage.netherrack.tooltip": "네더랙에 매달려있는 덩굴",
|
||||
|
||||
"betterfoliage.fallingLeaves": "떨어지는 나뭇잎",
|
||||
"betterfoliage.fallingLeaves.tooltip": "잎 블록에서 바닥으로 떨어지는 잎 파티클",
|
||||
"betterfoliage.fallingLeaves.speed": "파티클 속도",
|
||||
"betterfoliage.fallingLeaves.speed.tooltip": "전체 파티클 속도",
|
||||
"betterfoliage.fallingLeaves.windStrength": "바람 세기",
|
||||
"betterfoliage.fallingLeaves.windStrength.tooltip": "날씨 바람 추가효과 (0을 중심으로 정규분포의 확산)",
|
||||
"betterfoliage.fallingLeaves.stormStrength": "폭풍 세기",
|
||||
"betterfoliage.fallingLeaves.stormStrength.tooltip": "비 날씨에 바람 추가효과 (0을 중심으로 정규분포의 확산)",
|
||||
"betterfoliage.fallingLeaves.size": "파티클 크기",
|
||||
"betterfoliage.fallingLeaves.chance": "파티클 확률",
|
||||
"betterfoliage.fallingLeaves.chance.tooltip": "잎 파티클 랜덤 확률 설정",
|
||||
"betterfoliage.fallingLeaves.perturb": "움직임",
|
||||
"betterfoliage.fallingLeaves.perturb.tooltip": "움직임 효과의 크기. 코르크 같은 움직임을 추가합니다.",
|
||||
"betterfoliage.fallingLeaves.lifetime": "파티클 지속시간",
|
||||
"betterfoliage.fallingLeaves.lifetime.tooltip": "최대 파티클 지속시간을 설정합니다. 최소 지속시간은 60%입니다.",
|
||||
"betterfoliage.fallingLeaves.opacityHack": "불투명 파티클",
|
||||
"betterfoliage.fallingLeaves.opacityHack.tooltip": "파티클이 앞에 있어도 파티클을 가리는 투명블럭을 없앱니다. 경고: 오류주의",
|
||||
|
||||
"betterfoliage.risingSoul": "소울 상승",
|
||||
"betterfoliage.risingSoul.tooltip": "소울 블럭에서 올라오는 파티클",
|
||||
"betterfoliage.risingSoul.chance": "파티클 수정",
|
||||
"betterfoliage.risingSoul.chance.tooltip": "소울 파티클 랜덤 확률 설정",
|
||||
"betterfoliage.risingSoul.speed": "파티클 속도",
|
||||
"betterfoliage.risingSoul.speed.tooltip": "파티클 속도",
|
||||
"betterfoliage.risingSoul.perturb": "움직임",
|
||||
"betterfoliage.risingSoul.perturb.tooltip": "움직임 효과의 크기. 코르크 같은 움직임을 추가합니다.",
|
||||
"betterfoliage.risingSoul.headSize": "소울 크기",
|
||||
"betterfoliage.risingSoul.headSize.tooltip": "소울 파티클의 크기",
|
||||
"betterfoliage.risingSoul.trailSize": "자취 사이즈",
|
||||
"betterfoliage.risingSoul.trailSize.tooltip": "자취의 크기",
|
||||
"betterfoliage.risingSoul.opacity": "불투명",
|
||||
"betterfoliage.risingSoul.opacity.tooltip": "파티클의 불투명",
|
||||
"betterfoliage.risingSoul.sizeDecay": "크기 감소",
|
||||
"betterfoliage.risingSoul.sizeDecay.tooltip": "상대적인 자취 파티클 크기",
|
||||
"betterfoliage.risingSoul.opacityDecay": "불투명 감소",
|
||||
"betterfoliage.risingSoul.opacityDecay.tooltip": "상대적인 입자의 파티클 불투명도",
|
||||
"betterfoliage.risingSoul.lifetime": "최대 지속시간",
|
||||
"betterfoliage.risingSoul.lifetime.tooltip": "최대 파티클 지속시간을 설정합니다. 최소 지속시간은 60%입니다.",
|
||||
"betterfoliage.risingSoul.trailLength": "자취의 세기",
|
||||
"betterfoliage.risingSoul.trailLength.tooltip": "이전 파티클 틱으로 자취를 생성합니다.",
|
||||
"betterfoliage.risingSoul.trailDensity": "자취의 밀도",
|
||||
"betterfoliage.risingSoul.trailDensity.tooltip": "파티클 자취를 모두 렌더링 합니다.",
|
||||
|
||||
"betterfoliage.connectedGrass": "잔디 연결 텍스쳐",
|
||||
"betterfoliage.connectedGrass.enabled": "활성화",
|
||||
"betterfoliage.connectedGrass.enabled.tooltip": "잔디블록이 흙블록 위에 있을 경우 잔디블록 윗텍스쳐를 옆텍스쳐에 전부 씌웁니다.",
|
||||
|
||||
"betterfoliage.roundLogs": "둥근 나무",
|
||||
"betterfoliage.roundLogs.tooltip": "둥근부분을 블럭과 연결.",
|
||||
"betterfoliage.roundLogs.connectSolids": "블럭과 연결",
|
||||
"betterfoliage.roundLogs.connectSolids.tooltip": "둥근부분을 블럭과 연결.",
|
||||
"betterfoliage.roundLogs.connectPerpendicular": "나무 세로부분과 연결",
|
||||
"betterfoliage.roundLogs.connectPerpendicular.tooltip": "나무 세로부분을 나무부분끼리 연결",
|
||||
"betterfoliage.roundLogs.lenientConnect": "부드럽게 둥글게 연결",
|
||||
"betterfoliage.roundLogs.lenientConnect.tooltip": "2x2사이즈처럼 나무 평형된 부분끼리 서로 연결합니다.",
|
||||
"betterfoliage.roundLogs.connectGrass": "잔디 연결 텍스쳐",
|
||||
"betterfoliage.roundLogs.connectGrass.tooltip": "잔디가 근처에 있을 경우 나무 아래 잔디 블록을 렌더링",
|
||||
"betterfoliage.roundLogs.radiusSmall": "모서리 깎는 반지름",
|
||||
"betterfoliage.roundLogs.radiusSmall.tooltip": "나무 모서리 깎는 정도",
|
||||
"betterfoliage.roundLogs.radiusLarge": "모서리 깎는 부분 연결",
|
||||
"betterfoliage.roundLogs.radiusLarge.tooltip": "나무 모서리 부분을 연결",
|
||||
"betterfoliage.roundLogs.dimming": "조광",
|
||||
"betterfoliage.roundLogs.dimming.tooltip": "나무 표면부분을 어둡게하는 양",
|
||||
"betterfoliage.roundLogs.zProtection": "Z-Protection",
|
||||
"betterfoliage.roundLogs.zProtection.tooltip": "Amount to scale parallel log connection bits to stop Z-fighting (flickering). Try to set it as high as possible without having glitches.",
|
||||
"betterfoliage.roundLogs.defaultY": "수직선에대한 기본값",
|
||||
"betterfoliage.roundLogs.defaultY.tooltip": "true 일경우, 나무 블럭이 수직선에대한 렌더링을 할수가 없습니다. 그렇지 아니하면, 네모난 블럭으로 렌더링 될것임니다."
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
key.betterfoliage.gui=설정
|
||||
|
||||
betterfoliage.global.enabled=모드 활성화
|
||||
betterfoliage.global.enabled.tooltip=비활성화 할 경우, 환경강화모드 렌더링이 보이지 않습니다.
|
||||
|
||||
betterfoliage.enabled=활성화
|
||||
betterfoliage.enabled.tooltip=이 기능이 활성화 되어 있습니까?
|
||||
betterfoliage.hOffset=수평(가로) 상쇄시키다
|
||||
betterfoliage.hOffset.tooltip=이 성분이 블럭 수평으로 이동된다.
|
||||
betterfoliage.vOffset=수직(세로) 상쇄시키다
|
||||
betterfoliage.vOffset.tooltip=이 성분이 블럭 수직으로 이동된다.
|
||||
betterfoliage.size=사이즈(크기)
|
||||
betterfoliage.size.tooltip=사이즈(크기)의 최소
|
||||
betterfoliage.heightMin=최소한 높이
|
||||
betterfoliage.heightMin.tooltip=높이 최소한의 최소
|
||||
betterfoliage.heightMax=최대한 높이
|
||||
betterfoliage.heightMax.tooltip=높이 최대한의 최소
|
||||
betterfoliage.population=주민
|
||||
betterfoliage.population.tooltip=자격을 갖춘 블럭의 기능 확률(N 분의 64)
|
||||
betterfoliage.shaderWind=쉐이더 바람 효과
|
||||
betterfoliage.shaderWind.tooltip=바람효과를 쉐이더에 적용시키겠습니까?
|
||||
betterfoliage.distance=거리 제한
|
||||
betterfoliage.distance.tooltip=이 기능을 렌더링하는 플레이어의 최대거리
|
||||
|
||||
betterfoliage.blocks=블록 타입
|
||||
betterfoliage.blocks.tooltip=세팅 된 것에 따라 블록이 바뀔 것입니다.
|
||||
|
||||
betterfoliage.blocks.dirtWhitelist=흙 허용목록
|
||||
betterfoliage.blocks.dirtBlacklist=흙 차단목록
|
||||
betterfoliage.blocks.dirtWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.dirtBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.grassWhitelist=잔디 허용목록
|
||||
betterfoliage.blocks.grassBlacklist=잔디 차단목록
|
||||
betterfoliage.blocks.grassWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.grassBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.leavesWhitelist=잎 허용목록
|
||||
betterfoliage.blocks.leavesBlacklist=잎 차단목록
|
||||
betterfoliage.blocks.leavesWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.leavesBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.cropsWhitelist=농작물 허용목록
|
||||
betterfoliage.blocks.cropsBlacklist=농작물 차단목록
|
||||
betterfoliage.blocks.cropsWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cropsBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.logsWhitelist=나무 허용목록
|
||||
betterfoliage.blocks.logsBlacklist=나무 차단목록
|
||||
betterfoliage.blocks.logsWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.logsBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.sandWhitelist=모래 허용목록
|
||||
betterfoliage.blocks.sandBlacklist=모래 차단목록
|
||||
betterfoliage.blocks.sandWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.sandBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.lilypadWhitelist=연꽃 허용목록
|
||||
betterfoliage.blocks.lilypadBlacklist=연꽃 차단목록
|
||||
betterfoliage.blocks.lilypadWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.lilypadBlacklist.arrayEntry=%d entries
|
||||
|
||||
betterfoliage.blocks.cactusWhitelist=선인장 허용목록
|
||||
betterfoliage.blocks.cactusBlacklist=선인장 차단목록
|
||||
betterfoliage.blocks.cactusWhitelist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.cactusBlacklist.arrayEntry=%d entries
|
||||
|
||||
|
||||
betterfoliage.blocks.dirtWhitelist.tooltip=흙으로 인식됩니다. 갈대, 조류, 잔디 텍스쳐 연결에 영향을 줍니다.
|
||||
betterfoliage.blocks.dirtBlacklist.tooltip=흙으로 인식 되지 않습니다. 갈대, 조류, 잔디 텍스쳐 연결에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.grassWhitelist.tooltip=잔디로 인식됩니다. 짧은 잔디, 잔디 텍스쳐 연결에 영향을 줍니다.
|
||||
betterfoliage.blocks.grassBlacklist.tooltip=잔디로 인식 되지 않습니다. 짧은 잔디, 잔디 텍스쳐 연결에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.leavesWhitelist.tooltip=잎으로 인식됩니다. 추가 잎, 떨어지는 잎에 영향을 줍니다.
|
||||
betterfoliage.blocks.leavesBlacklist.tooltip=잎으로 인식 되지 않습니다. 추가 잎, 떨어지는 잎에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.cropsWhitelist.tooltip=농작물로 인식됩니다. 농작물은 쉐이더 적용하면 큰잔디로 렌더링 됩니다.
|
||||
betterfoliage.blocks.cropsBlacklist.tooltip=농작물로 인식 되지 않습니다. 농작물은 쉐이더 적용하면 큰잔디로 렌더링 되지 않습니다.
|
||||
betterfoliage.blocks.logsWhitelist.tooltip=나무로 인식됩니다. 둥근 나무에 영향을 줍니다.
|
||||
betterfoliage.blocks.logsBlacklist.tooltip=나무로 인식 되지 않습니다. 둥근 나무에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.sandWhitelist.tooltip=모래로 인식됩니다. 산호에 영향을 줍니다
|
||||
betterfoliage.blocks.sandBlacklist.tooltip=모래로 인식되지 않습니다. 산호에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.lilypadWhitelist.tooltip=연꽃으로 인식됩니다. 보다 나은 연꽃에 영향을 줍니다.
|
||||
betterfoliage.blocks.lilypadBlacklist.tooltip=연꽃으로 인식되지 않습니다. 보다 나은 연꽃에 영향을 주지 않습니다.
|
||||
betterfoliage.blocks.cactusWhitelist.tooltip=선인장으로 인식됩니다. 보다 나은 선인장에 영향을 줍니다.
|
||||
betterfoliage.blocks.cactusBlacklist.tooltip=선인장으로 인식되지 않습니다. 보다 나은 선인장에 영향을 주지 않습니다.
|
||||
|
||||
betterfoliage.leaves=잎 추가
|
||||
betterfoliage.leaves.tooltip=둥글게 나뭇잎을 추가시켜줍니다.
|
||||
betterfoliage.leaves.dense=조밀한 모드
|
||||
betterfoliage.leaves.dense.tooltip=조밀한 모드는 둥근 나뭇잎을 더 추가시켜줍니다.
|
||||
|
||||
betterfoliage.shortGrass=이쁜 잔디 & 균사체
|
||||
betterfoliage.shortGrass.tooltip=블록 상단에 잔디 / 균사체
|
||||
betterfoliage.shortGrass.useGenerated=잔디텍스쳐를 활성화 합니다.
|
||||
betterfoliage.shortGrass.useGenerated.tooltip=소스팩의 일부분에 활성화 되어있는 큰잔디 텍스쳐를 생성시킵니다.
|
||||
betterfoliage.shortGrass.myceliumEnabled=균사체 활성화
|
||||
betterfoliage.shortGrass.myceliumEnabled.tooltip=균사체 블록에 있는 기능을 활성화 시키겠습니까?
|
||||
betterfoliage.shortGrass.grassEnabled=잔디 활성화
|
||||
betterfoliage.shortGrass.grassEnabled.tooltip=잔디 블록에 있는 기능을 활성화 시키겠습니까?
|
||||
betterfoliage.shortGrass.snowEnabled=눈 활성화
|
||||
betterfoliage.shortGrass.snowEnabled.tooltip=잔디 블록위에 있는 눈을 활성화 시키겠습니까?
|
||||
betterfoliage.shortGrass.saturationThreshold=채도 임계값
|
||||
betterfoliage.shortGrass.saturationThreshold.tooltip=(특정 색상을 사용하여)"무채색"블록과 (바이옴 색을 사용하여)"화려한"블록 사이의 채도 차단
|
||||
|
||||
betterfoliage.hangingGrass=매달려있는 잔디
|
||||
betterfoliage.hangingGrass.tooltip=잔디 블록 상단 가장자리에서 아래로 매달려 있는 잔디 다발
|
||||
betterfoliage.hangingGrass.separation=분리
|
||||
betterfoliage.hangingGrass.separation.tooltip=잔디블럭에 매달려있는 잔디의 양
|
||||
|
||||
betterfoliage.cactus=선인장
|
||||
betterfoliage.cactus.tooltip=선인장의 비트 수와 부드러운 그림자를 추가
|
||||
betterfoliage.cactus.sizeVariation=사이즈 변화
|
||||
betterfoliage.cactus.sizeVariation.tooltip=선인장의 사이즈 크기를 무작위 변화 시킵니다.
|
||||
|
||||
betterfoliage.lilypad=연꽃잎
|
||||
betterfoliage.lilypad.tooltip=연꽃의 뿌리와 약간의 꽃 추가
|
||||
betterfoliage.lilypad.flowerChance=꽃 확률
|
||||
betterfoliage.lilypad.flowerChance.tooltip=연꽃 위에 있는 꽃 확률(N 분의 64)
|
||||
|
||||
betterfoliage.reed=갈대
|
||||
betterfoliage.reed.tooltip=물 속에서 흙블럭 위에 있는 갈대
|
||||
betterfoliage.reed.biomes=바이옴 리스트
|
||||
betterfoliage.reed.biomes.tooltip=갈대를 바이옴에 따라 표시
|
||||
betterfoliage.reed.biomes.tooltip.element=갈대를 %s 바이옴에 따라 나타내시겠습니까?
|
||||
|
||||
betterfoliage.algae=해조류
|
||||
betterfoliage.algae.tooltip=깊은 물 속 흙 블럭 위에 있는 해조류
|
||||
betterfoliage.algae.biomes=바이옴 리스트
|
||||
betterfoliage.algae.biomes.tooltip=해조류를 바이옴에 따라 표시
|
||||
betterfoliage.algae.biomes.tooltip.element=해조류를 %s 바이옴에 따라 나타내시겠습니까?
|
||||
|
||||
betterfoliage.coral=산호
|
||||
betterfoliage.coral.tooltip=깊은 물 속 모래 블럭 위에있는 산호
|
||||
betterfoliage.coral.size=산호 사이즈(크기)
|
||||
betterfoliage.coral.size.tooltip=산호 이미지만큼 적용
|
||||
betterfoliage.coral.crustSize=껍질 크기
|
||||
betterfoliage.coral.crustSize.tooltip=산호 부분의 크기
|
||||
betterfoliage.coral.chance=산호 확률
|
||||
betterfoliage.coral.chance.tooltip=산호를 특정블록에 표시하는 확률(N 분의 64)
|
||||
betterfoliage.coral.biomes=바이옴 리스트
|
||||
betterfoliage.coral.biomes.tooltip=산호를 바이옴에 따라 표시
|
||||
betterfoliage.coral.biomes.tooltip.element=산호를 %s 바이옴에 따라 나타내시겠습니까?
|
||||
betterfoliage.coral.shallowWater=얕은 물 산호
|
||||
betterfoliage.coral.shallowWater.tooltip=산호를 깊은 물에 표시하시겠습니까?
|
||||
|
||||
betterfoliage.netherrack=네더랙 덩굴
|
||||
betterfoliage.netherrack.tooltip=네더랙에 매달려있는 덩굴
|
||||
|
||||
betterfoliage.fallingLeaves=떨어지는 나뭇잎
|
||||
betterfoliage.fallingLeaves.tooltip=잎 블록에서 바닥으로 떨어지는 잎 파티클
|
||||
betterfoliage.fallingLeaves.speed=파티클 속도
|
||||
betterfoliage.fallingLeaves.speed.tooltip=전체 파티클 속도
|
||||
betterfoliage.fallingLeaves.windStrength=바람 세기
|
||||
betterfoliage.fallingLeaves.windStrength.tooltip=날씨 바람 추가효과 (0을 중심으로 정규분포의 확산)
|
||||
betterfoliage.fallingLeaves.stormStrength=폭풍 세기
|
||||
betterfoliage.fallingLeaves.stormStrength.tooltip=비 날씨에 바람 추가효과 (0을 중심으로 정규분포의 확산)
|
||||
betterfoliage.fallingLeaves.size=파티클 크기
|
||||
betterfoliage.fallingLeaves.chance=파티클 확률
|
||||
betterfoliage.fallingLeaves.chance.tooltip=잎 파티클 랜덤 확률 설정
|
||||
betterfoliage.fallingLeaves.perturb=움직임
|
||||
betterfoliage.fallingLeaves.perturb.tooltip=움직임 효과의 크기. 코르크 같은 움직임을 추가합니다.
|
||||
betterfoliage.fallingLeaves.lifetime=파티클 지속시간
|
||||
betterfoliage.fallingLeaves.lifetime.tooltip=최대 파티클 지속시간을 설정합니다. 최소 지속시간은 60%입니다.
|
||||
betterfoliage.fallingLeaves.opacityHack=불투명 파티클
|
||||
betterfoliage.fallingLeaves.opacityHack.tooltip=파티클이 앞에 있어도 파티클을 가리는 투명블럭을 없앱니다. 경고: 오류주의
|
||||
|
||||
betterfoliage.risingSoul=소울 상승
|
||||
betterfoliage.risingSoul.tooltip=소울 블럭에서 올라오는 파티클
|
||||
betterfoliage.risingSoul.chance=파티클 수정
|
||||
betterfoliage.risingSoul.chance.tooltip=소울 파티클 랜덤 확률 설정
|
||||
betterfoliage.risingSoul.speed=파티클 속도
|
||||
betterfoliage.risingSoul.speed.tooltip=파티클 속도
|
||||
betterfoliage.risingSoul.perturb=움직임
|
||||
betterfoliage.risingSoul.perturb.tooltip=움직임 효과의 크기. 코르크 같은 움직임을 추가합니다.
|
||||
betterfoliage.risingSoul.headSize=소울 크기
|
||||
betterfoliage.risingSoul.headSize.tooltip=소울 파티클의 크기
|
||||
betterfoliage.risingSoul.trailSize=자취 사이즈
|
||||
betterfoliage.risingSoul.trailSize.tooltip=자취의 크기
|
||||
betterfoliage.risingSoul.opacity=불투명
|
||||
betterfoliage.risingSoul.opacity.tooltip=파티클의 불투명
|
||||
betterfoliage.risingSoul.sizeDecay=크기 감소
|
||||
betterfoliage.risingSoul.sizeDecay.tooltip=상대적인 자취 파티클 크기
|
||||
betterfoliage.risingSoul.opacityDecay=불투명 감소
|
||||
betterfoliage.risingSoul.opacityDecay.tooltip=상대적인 입자의 파티클 불투명도
|
||||
betterfoliage.risingSoul.lifetime=최대 지속시간
|
||||
betterfoliage.risingSoul.lifetime.tooltip=최대 파티클 지속시간을 설정합니다. 최소 지속시간은 60%입니다.
|
||||
betterfoliage.risingSoul.trailLength=자취의 세기
|
||||
betterfoliage.risingSoul.trailLength.tooltip=이전 파티클 틱으로 자취를 생성합니다.
|
||||
betterfoliage.risingSoul.trailDensity=자취의 밀도
|
||||
betterfoliage.risingSoul.trailDensity.tooltip=파티클 자취를 모두 렌더링 합니다.
|
||||
|
||||
betterfoliage.connectedGrass=잔디 연결 텍스쳐
|
||||
betterfoliage.connectedGrass.enabled=활성화
|
||||
betterfoliage.connectedGrass.enabled.tooltip=잔디블록이 흙블록 위에 있을 경우 잔디블록 윗텍스쳐를 옆텍스쳐에 전부 씌웁니다.
|
||||
|
||||
betterfoliage.roundLogs=둥근 나무
|
||||
betterfoliage.roundLogs.tooltip=둥근부분을 블럭과 연결.
|
||||
betterfoliage.roundLogs.connectSolids=블럭과 연결
|
||||
betterfoliage.roundLogs.connectSolids.tooltip=둥근부분을 블럭과 연결.
|
||||
betterfoliage.roundLogs.connectPerpendicular=나무 세로부분과 연결
|
||||
betterfoliage.roundLogs.connectPerpendicular.tooltip=나무 세로부분을 나무부분끼리 연결
|
||||
betterfoliage.roundLogs.lenientConnect=부드럽게 둥글게 연결
|
||||
betterfoliage.roundLogs.lenientConnect.tooltip=2x2사이즈처럼 나무 평형된 부분끼리 서로 연결합니다.
|
||||
betterfoliage.roundLogs.connectGrass=잔디 연결 텍스쳐
|
||||
betterfoliage.roundLogs.connectGrass.tooltip=잔디가 근처에 있을 경우 나무 아래 잔디 블록을 렌더링
|
||||
betterfoliage.roundLogs.radiusSmall=모서리 깎는 반지름
|
||||
betterfoliage.roundLogs.radiusSmall.tooltip=나무 모서리 깎는 정도
|
||||
betterfoliage.roundLogs.radiusLarge=모서리 깎는 부분 연결
|
||||
betterfoliage.roundLogs.radiusLarge.tooltip=나무 모서리 부분을 연결
|
||||
betterfoliage.roundLogs.dimming=조광
|
||||
betterfoliage.roundLogs.dimming.tooltip=나무 표면부분을 어둡게하는 양
|
||||
betterfoliage.roundLogs.zProtection=Z-Protection
|
||||
betterfoliage.roundLogs.zProtection.tooltip=Amount to scale parallel log connection bits to stop Z-fighting (flickering). Try to set it as high as possible without having glitches.
|
||||
betterfoliage.roundLogs.defaultY=수직선에대한 기본값
|
||||
betterfoliage.roundLogs.defaultY.tooltip=true 일경우, 나무 블럭이 수직선에대한 렌더링을 할수가 없습니다. 그렇지 아니하면, 네모난 블럭으로 렌더링 될것임니다.
|
||||
|
||||
# Translate by IS_Jump for feedback mail acarus22@gmail.com
|
||||
154
src/main/resources/assets/betterfoliage/lang/ru_ru.json
Normal file
154
src/main/resources/assets/betterfoliage/lang/ru_ru.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"key.betterfoliage.gui": "Открыть настройки",
|
||||
|
||||
"betterfoliage.global.enabled": "Включить мод",
|
||||
"betterfoliage.global.enabled.tooltip": "Если установлено на false, BetterFoliage не будет ничего рендерить",
|
||||
|
||||
"betterfoliage.enabled": "Включить",
|
||||
"betterfoliage.enabled.tooltip": "Включена ли эта функция?",
|
||||
"betterfoliage.hOffset": "Горизонтальное смещение",
|
||||
"betterfoliage.hOffset.tooltip": "Дистанция горизонтального смещения этого элемента в блоках",
|
||||
"betterfoliage.vOffset": "Вертикальное смещение",
|
||||
"betterfoliage.vOffset.tooltip": "Дистанция вертикального смещения этого элемента в блоках",
|
||||
"betterfoliage.size": "Размер",
|
||||
"betterfoliage.size.tooltip": "Размер этого элемента",
|
||||
"betterfoliage.heightMin": "Минимальная высота",
|
||||
"betterfoliage.heightMin.tooltip": "Минимальная высота элемента",
|
||||
"betterfoliage.heightMax": "Максимальная высота",
|
||||
"betterfoliage.heightMax.tooltip": "Максимальная высота этого элемента",
|
||||
"betterfoliage.population": "Популяция",
|
||||
"betterfoliage.population.tooltip": "Шанс (N к 64), что блок будет иметь эту функцию",
|
||||
"betterfoliage.shaderWind": "Шейдерные эффекты ветра",
|
||||
"betterfoliage.shaderWind.tooltip": "Применить эффекты ветра с ShaderMod для этого элемента?",
|
||||
"betterfoliage.distance": "Лимит дистанции",
|
||||
"betterfoliage.distance.tooltip": "Максимальное расстояние от игрока для рендеринга этой функции",
|
||||
"betterfoliage.saturationThreshold": "Порог насыщения",
|
||||
"betterfoliage.saturationThreshold.tooltip": "Насыщенность цвета разделяется на: \"обесцвеченные\" блоки (используя цвет биома) и \"цветные\" блоки (используя их собственный цвет)",
|
||||
|
||||
"betterfoliage.leaves": "Улучшенная листва",
|
||||
"betterfoliage.leaves.tooltip": "Дополнительное округление листьев на блоках листвы.",
|
||||
"betterfoliage.leaves.dense": "Плотный режим",
|
||||
"betterfoliage.leaves.dense.tooltip": "Плотный режим имеет более округлые листья.",
|
||||
|
||||
"betterfoliage.shortGrass": "Низкая трава и мицелий",
|
||||
"betterfoliage.shortGrass.tooltip": "Пучки травы / мицелия на поверхности соответствующих блоков.",
|
||||
"betterfoliage.shortGrass.useGenerated": "Использовать сгенерированные текстуры для травы.",
|
||||
"betterfoliage.shortGrass.useGenerated.tooltip": "Сгенерированная текстура создается путём разрезания текстуры высокой травы с активного ресурс-пака пополам.",
|
||||
"betterfoliage.shortGrass.myceliumEnabled": "Включить мицелий",
|
||||
"betterfoliage.shortGrass.myceliumEnabled.tooltip": "Включить эту особенность для блоков мицелия?",
|
||||
"betterfoliage.shortGrass.grassEnabled": "Включить траву",
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "Включить эту особенность для блоков травы?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "Включить траву под снегом",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "Включить эту особенность для заснеженных блоков травы?",
|
||||
|
||||
"betterfoliage.hangingGrass": "Висячая трава",
|
||||
"betterfoliage.hangingGrass.tooltip": "Пучки травы свисают вниз с верхних краев блока травы.",
|
||||
"betterfoliage.hangingGrass.separation": "Разделение",
|
||||
"betterfoliage.hangingGrass.separation.tooltip": "Как долго подвесная трава выделяется из блока?",
|
||||
|
||||
"betterfoliage.cactus": "Улучшенные кактусы",
|
||||
"betterfoliage.cactus.tooltip": "Улучшить кактус с дополнительными частицами и плавными тенями.",
|
||||
"betterfoliage.cactus.sizeVariation": "Вариации размера",
|
||||
"betterfoliage.cactus.sizeVariation.tooltip": "Количество случайных изменений в размере кактусов.",
|
||||
|
||||
"betterfoliage.lilypad": "Улучшенные кувшинки",
|
||||
"betterfoliage.lilypad.tooltip": "Добавить кувшинкам корни и цветы.",
|
||||
"betterfoliage.lilypad.flowerChance": "Шанс появления цветов",
|
||||
"betterfoliage.lilypad.flowerChance.tooltip": "Шанс (N к 64) появления цветка на кувшинке.",
|
||||
|
||||
"betterfoliage.reed": "Камыши",
|
||||
"betterfoliage.reed.tooltip": "Мелководные камыши на блоках земли.",
|
||||
"betterfoliage.reed.biomes": "Список биомов",
|
||||
"betterfoliage.reed.biomes.tooltip": "Настройка биомов, в которых камышам разрешено появляться.",
|
||||
"betterfoliage.reed.biomes.tooltip.element": "Должны ли камыши встречаться в %s биоме?",
|
||||
|
||||
"betterfoliage.algae": "Морские водоросли",
|
||||
"betterfoliage.algae.tooltip": "Глубоководные водоросли на блоках земли.",
|
||||
"betterfoliage.algae.biomes": "Список биомов",
|
||||
"betterfoliage.algae.biomes.tooltip": "Настройка биомов, в которых водорослям разрешено появляться.",
|
||||
"betterfoliage.algae.biomes.tooltip.element": "Должны ли водоросли встречаться в %s биоме?",
|
||||
|
||||
"betterfoliage.coral": "Кораллы",
|
||||
"betterfoliage.coral.tooltip": "Кораллы на песчаных блоках в глубокой воде.",
|
||||
"betterfoliage.coral.size": "Размер кораллов",
|
||||
"betterfoliage.coral.size.tooltip": "Размер торчащих частичек кораллов.",
|
||||
"betterfoliage.coral.crustSize": "Размер коры",
|
||||
"betterfoliage.coral.crustSize.tooltip": "Размер плоской части кораллов.",
|
||||
"betterfoliage.coral.chance": "Шанс кораллов",
|
||||
"betterfoliage.coral.chance.tooltip": "Шанс (N in 64) появления кораллов на определенном блоке.",
|
||||
"betterfoliage.coral.biomes": "Список биомов",
|
||||
"betterfoliage.coral.biomes.tooltip": "Настройка биомов, в которых разрешено появляться кораллам.",
|
||||
"betterfoliage.coral.biomes.tooltip.element": "Должны ли кораллы появляться в %s биоме?",
|
||||
"betterfoliage.coral.shallowWater": "Мелководные кораллы",
|
||||
"betterfoliage.coral.shallowWater.tooltip": "Должны ли появляться кораллы в воде, глубиной в 1 блок?",
|
||||
|
||||
"betterfoliage.netherrack": "Адская лоза",
|
||||
"betterfoliage.netherrack.tooltip": "Висячая лоза под адским камнем",
|
||||
|
||||
"betterfoliage.fallingLeaves": "Падающие листья",
|
||||
"betterfoliage.fallingLeaves.tooltip": "Падение FX частиц листвы исходящие из низа блоков листвы",
|
||||
"betterfoliage.fallingLeaves.speed": "Скорость частиц",
|
||||
"betterfoliage.fallingLeaves.speed.tooltip": "Общая скорость частиц",
|
||||
"betterfoliage.fallingLeaves.windStrength": "Сила ветра",
|
||||
"betterfoliage.fallingLeaves.windStrength.tooltip": "Величина воздействия ветра в хорошую погоду (распространение нормального распределения сосредоточено на 0)",
|
||||
"betterfoliage.fallingLeaves.stormStrength": "Сила шторма",
|
||||
"betterfoliage.fallingLeaves.stormStrength.tooltip": "Дополнительная величина воздействия ветра в ненастную погоду (распространение нормального распределения сосредоточено на 0)",
|
||||
"betterfoliage.fallingLeaves.size": "Размер частиц",
|
||||
"betterfoliage.fallingLeaves.chance": "Шанс частиц",
|
||||
"betterfoliage.fallingLeaves.chance.tooltip": "Вероятность каждого случайного рендеринга в такт (1/20 секунды) опадения частицы блока листвы.",
|
||||
"betterfoliage.fallingLeaves.perturb": "Возмущение",
|
||||
"betterfoliage.fallingLeaves.perturb.tooltip": "Величина эффекта возмущений. Добавляет штопорообразное движение к частице синхронизированной с его вращением.",
|
||||
"betterfoliage.fallingLeaves.lifetime": "Максимальное время жизни",
|
||||
"betterfoliage.fallingLeaves.lifetime.tooltip": "Максимальное время жизни частиц. Минимальное время жизни - 60%% от этого значения.",
|
||||
"betterfoliage.fallingLeaves.opacityHack": "Непрозрачные частицы",
|
||||
"betterfoliage.fallingLeaves.opacityHack.tooltip": "Запретить прозрачным блокам затемнять частицы даже тогда, когда частицы впереди. ВНИМАНИЕ: может спровоцировать баги.",
|
||||
|
||||
"betterfoliage.risingSoul": "Адские духи",
|
||||
"betterfoliage.risingSoul.tooltip": "Количество душ-частиц FX, испускаемых из верхней части блоков песка душ.",
|
||||
"betterfoliage.risingSoul.chance": "Шанс частиц",
|
||||
"betterfoliage.risingSoul.chance.tooltip": "Частота генерации частиц на песке душ.",
|
||||
"betterfoliage.risingSoul.speed": "Скорость частиц",
|
||||
"betterfoliage.risingSoul.speed.tooltip": "Вертикальная скорость движения частиц духов.",
|
||||
"betterfoliage.risingSoul.perturb": "Возмущение",
|
||||
"betterfoliage.risingSoul.perturb.tooltip": "Магнитуда эффекта возмущений. Добавляет штопороподобное движение частиц.",
|
||||
"betterfoliage.risingSoul.headSize": "Размер духа",
|
||||
"betterfoliage.risingSoul.headSize.tooltip": "Размер частицы духа",
|
||||
"betterfoliage.risingSoul.trailSize": "Размер следов",
|
||||
"betterfoliage.risingSoul.trailSize.tooltip": "Начальный размер следа частиц",
|
||||
"betterfoliage.risingSoul.opacity": "Прозрачность",
|
||||
"betterfoliage.risingSoul.opacity.tooltip": "Непрозрачность эффекта частиц",
|
||||
"betterfoliage.risingSoul.sizeDecay": "Размер распада",
|
||||
"betterfoliage.risingSoul.sizeDecay.tooltip": "Следующий размер частицы соответствует их же размеру в предыдущем такте (1/20 секунды).",
|
||||
"betterfoliage.risingSoul.opacityDecay": "Непрозрачность распада",
|
||||
"betterfoliage.risingSoul.opacityDecay.tooltip": "Следующий уровень прозрачности частицы соответствует их же уровню прозрачности в предыдущем такте (1/20 секунды).",
|
||||
"betterfoliage.risingSoul.lifetime": "Максимальное время жизни",
|
||||
"betterfoliage.risingSoul.lifetime.tooltip": "Максимальное время жизни эффекта частиц. Минимальное время жизни равно 60%% от этого числа.",
|
||||
"betterfoliage.risingSoul.trailLength": "Длина следов",
|
||||
"betterfoliage.risingSoul.trailLength.tooltip": "Количество предыдущих позиций, которые запомнила частица в тактах (1/20 секунды).",
|
||||
"betterfoliage.risingSoul.trailDensity": "Плотность следов",
|
||||
"betterfoliage.risingSoul.trailDensity.tooltip": "Рендер каждой предыдущий N’ой позиции в следах частиц.",
|
||||
|
||||
|
||||
"betterfoliage.connectedGrass": "Соединенные текстуры травы",
|
||||
"betterfoliage.connectedGrass.enabled": "Включить",
|
||||
"betterfoliage.connectedGrass.enabled.tooltip": "Если блок травы находится над блоком земли: прорисовать верхнюю текстуру травы на всех сторонах блока травы.",
|
||||
|
||||
"betterfoliage.roundLogs": "Цилиндрические брёвна",
|
||||
"betterfoliage.roundLogs.tooltip": "Соединить круглые блоки в сплошные, полные блоки?",
|
||||
"betterfoliage.roundLogs.connectSolids": "Соединение в крупные брёвна",
|
||||
"betterfoliage.roundLogs.connectSolids.tooltip": "Соединить круглые блоки в сплошные, полные блоки?",
|
||||
"betterfoliage.roundLogs.connectPerpendicular": "Соединение в перпендикулярные брёвна",
|
||||
"betterfoliage.roundLogs.connectPerpendicular.tooltip": "Соединить круглые брёвна к перпендикулярным брёвнам относительно их оси?",
|
||||
"betterfoliage.roundLogs.lenientConnect": "Мягкое округление",
|
||||
"betterfoliage.roundLogs.lenientConnect.tooltip": "Соединение в параллельные круглые брёвна L-формы, не только 2х2.",
|
||||
"betterfoliage.roundLogs.connectGrass": "Соединенная трава",
|
||||
"betterfoliage.roundLogs.connectGrass.tooltip": "Заменяет землю под деревьями на траву, если она есть поблизости.",
|
||||
"betterfoliage.roundLogs.radiusSmall": "Радиус фаски",
|
||||
"betterfoliage.roundLogs.radiusSmall.tooltip": "Радиус обрезки углов от бревна.",
|
||||
"betterfoliage.roundLogs.radiusLarge": "Радиус соединенной фаски",
|
||||
"betterfoliage.roundLogs.radiusLarge.tooltip": "Радиус среза внешнего угла соединённых брёвен.",
|
||||
"betterfoliage.roundLogs.dimming": "Затемнение",
|
||||
"betterfoliage.roundLogs.dimming.tooltip": "Затемнить неясные длинные грани.",
|
||||
"betterfoliage.roundLogs.zProtection": "Z-Защита",
|
||||
"betterfoliage.roundLogs.zProtection.tooltip": "Для масштабирования параллельных битов соединения бревен, чтобы остановить Z-бой (мерцание). Попробуйте установить его как можно выше, для устранения мерцания."
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
key.betterfoliage.gui=Открыть настройки
|
||||
|
||||
betterfoliage.global.enabled=Включить мод
|
||||
betterfoliage.global.enabled.tooltip=Если установлено на false, BetterFoliage не будет ничего рендерить
|
||||
|
||||
betterfoliage.enabled=Включить
|
||||
betterfoliage.enabled.tooltip=Включена ли эта функция?
|
||||
betterfoliage.hOffset=Горизонтальное смещение
|
||||
betterfoliage.hOffset.tooltip=Дистанция горизонтального смещения этого элемента в блоках
|
||||
betterfoliage.vOffset=Вертикальное смещение
|
||||
betterfoliage.vOffset.tooltip=Дистанция вертикального смещения этого элемента в блоках
|
||||
betterfoliage.size=Размер
|
||||
betterfoliage.size.tooltip=Размер этого элемента
|
||||
betterfoliage.heightMin=Минимальная высота
|
||||
betterfoliage.heightMin.tooltip=Минимальная высота элемента
|
||||
betterfoliage.heightMax=Максимальная высота
|
||||
betterfoliage.heightMax.tooltip=Максимальная высота этого элемента
|
||||
betterfoliage.population=Популяция
|
||||
betterfoliage.population.tooltip=Шанс (N к 64), что блок будет иметь эту функцию
|
||||
betterfoliage.shaderWind=Шейдерные эффекты ветра
|
||||
betterfoliage.shaderWind.tooltip=Применить эффекты ветра с ShaderMod для этого элемента?
|
||||
betterfoliage.distance=Лимит дистанции
|
||||
betterfoliage.distance.tooltip=Максимальное расстояние от игрока для рендеринга этой функции
|
||||
|
||||
betterfoliage.blocks=Типы блоков
|
||||
betterfoliage.blocks.tooltip=Настройки списка классов блоков, которые будут иметь примененные к ним функции
|
||||
|
||||
betterfoliage.blocks.dirtWhitelist=Белый список земли
|
||||
betterfoliage.blocks.dirtBlacklist=Черный список земли
|
||||
betterfoliage.blocks.dirtWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.dirtBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.grassWhitelist=Белый список травы
|
||||
betterfoliage.blocks.grassBlacklist=Черный список травы
|
||||
betterfoliage.blocks.grassWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.grassBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.leavesWhitelist=Белый список листвы
|
||||
betterfoliage.blocks.leavesBlacklist=Черный список листвы
|
||||
betterfoliage.blocks.leavesWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.leavesBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.cropsWhitelist=Белый список урожая
|
||||
betterfoliage.blocks.cropsBlacklist=Черный список урожая
|
||||
betterfoliage.blocks.cropsWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.cropsBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.logsWhitelist=Белый список древесины
|
||||
betterfoliage.blocks.logsBlacklist=Черный список древесины
|
||||
betterfoliage.blocks.logsWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.logsBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.sandWhitelist=Белый список песка
|
||||
betterfoliage.blocks.sandBlacklist=Черный список песка
|
||||
betterfoliage.blocks.sandWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.sandBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.lilypadWhitelist=Белый список кувшинок
|
||||
betterfoliage.blocks.lilypadBlacklist=Черный список кувшинок
|
||||
betterfoliage.blocks.lilypadWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.lilypadBlacklist.arrayEntry=%d записей
|
||||
|
||||
betterfoliage.blocks.cactusWhitelist=Белый список кактусов
|
||||
betterfoliage.blocks.cactusBlacklist=Черный список кактусов
|
||||
betterfoliage.blocks.cactusWhitelist.arrayEntry=%d записей
|
||||
betterfoliage.blocks.cactusBlacklist.arrayEntry=%d записей
|
||||
|
||||
|
||||
betterfoliage.blocks.dirtWhitelist.tooltip=Блоки, которые будут восприниматься в качестве земли. Влияет на камыши, водоросли, соединенную траву.
|
||||
betterfoliage.blocks.dirtBlacklist.tooltip=Блоки, которые не будут восприниматься в качестве земли. Влияет на камыши, водоросли, соединенную траву.
|
||||
betterfoliage.blocks.grassWhitelist.tooltip=Блоки, которые будут восприниматься в качестве травы. Влияет на короткую и соединенную траву.
|
||||
betterfoliage.blocks.grassBlacklist.tooltip=Блоки, которые не будут восприниматься в качестве травы. Влияет на короткую и соединенную траву.
|
||||
betterfoliage.blocks.leavesWhitelist.tooltip=Блоки, которые будут восприниматься в качестве листвы. Влияет на дополнительную листву, падающую листву. Листва будут рендериться с ID листвы в шейдер-программах.
|
||||
betterfoliage.blocks.leavesBlacklist.tooltip=Блоки, которые никогда не будут восприниматься как листва. Влияет на дополнительную листву, падающую листву. Листва будут рендериться с ID листвы в шейдер-программах.
|
||||
betterfoliage.blocks.cropsWhitelist.tooltip=Блоки, которые будут восприниматься как культуры. Культуры будут рендериться с ID высокой травы в шейдер-программах.
|
||||
betterfoliage.blocks.cropsBlacklist.tooltip= Блоки, которые никогда не будут восприниматься как культуры. Культуры будут рендериться с ID высокой травы в шейдер-программах.
|
||||
betterfoliage.blocks.logsWhitelist.tooltip=Блоки, которые будут восприниматься в качестве деревянных брёвен. Влияет на цилиндрические брёвна.
|
||||
betterfoliage.blocks.logsBlacklist.tooltip=Блоки, которые никогда не будут восприниматься в качестве деревянных брёвен. Влияет на цилиндрические брёвна.
|
||||
betterfoliage.blocks.sandWhitelist.tooltip=Блоки, которые будут восприниматься в качестве песка. Влияет на кораллы.
|
||||
betterfoliage.blocks.sandBlacklist.tooltip=Блоки, которые никогда не будут восприниматься в качестве песка. Влияет на кораллы.
|
||||
betterfoliage.blocks.lilypadWhitelist.tooltip=Блоки, которые будут восприниматься в качестве кувшинок. Влияет на улучшенные кувшинки.
|
||||
betterfoliage.blocks.lilypadBlacklist.tooltip=Блоки, которые никогда не будут восприниматься в качестве кувшинок. Влияет на улучшенные кувшинки.
|
||||
betterfoliage.blocks.cactusWhitelist.tooltip=Блоки, которые будут восприниматься в качестве кактусов. Влияет на улучшенные кактусы.
|
||||
betterfoliage.blocks.cactusBlacklist.tooltip=Блоки, которые никогда не будут восприниматься в качестве кувшинок. Влияет на улучшенные кактусы.
|
||||
|
||||
betterfoliage.leaves=Улучшенная листва
|
||||
betterfoliage.leaves.tooltip=Дополнительное округление листьев на блоках листвы.
|
||||
betterfoliage.leaves.dense=Плотный режим
|
||||
betterfoliage.leaves.dense.tooltip=Плотный режим имеет более округлые листья.
|
||||
|
||||
betterfoliage.shortGrass=Низкая трава и мицелий
|
||||
betterfoliage.shortGrass.tooltip=Пучки травы / мицелия на поверхности соответствующих блоков.
|
||||
betterfoliage.shortGrass.useGenerated=Использовать сгенерированные текстуры для травы.
|
||||
betterfoliage.shortGrass.useGenerated.tooltip=Сгенерированная текстура создается путём разрезания текстуры высокой травы с активного ресурс-пака пополам.
|
||||
betterfoliage.shortGrass.myceliumEnabled=Включить мицелий
|
||||
betterfoliage.shortGrass.myceliumEnabled.tooltip=Включить эту особенность для блоков мицелия?
|
||||
betterfoliage.shortGrass.grassEnabled=Включить траву
|
||||
betterfoliage.shortGrass.grassEnabled.tooltip=Включить эту особенность для блоков травы?
|
||||
betterfoliage.shortGrass.snowEnabled=Включить траву под снегом
|
||||
betterfoliage.shortGrass.snowEnabled.tooltip=Включить эту особенность для заснеженных блоков травы?
|
||||
betterfoliage.shortGrass.saturationThreshold=Порог насыщения
|
||||
betterfoliage.shortGrass.saturationThreshold.tooltip=Насыщенность цвета разделяется на: "обесцвеченные" блоки (используя цвет биома) и "цветные" блоки (используя их собственный цвет)
|
||||
|
||||
betterfoliage.hangingGrass=Висячая трава
|
||||
betterfoliage.hangingGrass.tooltip=Пучки травы свисают вниз с верхних краев блока травы.
|
||||
betterfoliage.hangingGrass.separation=Разделение
|
||||
betterfoliage.hangingGrass.separation.tooltip=Как долго подвесная трава выделяется из блока?
|
||||
|
||||
betterfoliage.cactus=Улучшенные кактусы
|
||||
betterfoliage.cactus.tooltip=Улучшить кактус с дополнительными частицами и плавными тенями.
|
||||
betterfoliage.cactus.sizeVariation=Вариации размера
|
||||
betterfoliage.cactus.sizeVariation.tooltip=Количество случайных изменений в размере кактусов.
|
||||
|
||||
betterfoliage.lilypad=Улучшенные кувшинки
|
||||
betterfoliage.lilypad.tooltip=Добавить кувшинкам корни и цветы.
|
||||
betterfoliage.lilypad.flowerChance=Шанс появления цветов
|
||||
betterfoliage.lilypad.flowerChance.tooltip=Шанс (N к 64) появления цветка на кувшинке.
|
||||
|
||||
betterfoliage.reed=Камыши
|
||||
betterfoliage.reed.tooltip=Мелководные камыши на блоках земли.
|
||||
betterfoliage.reed.biomes=Список биомов
|
||||
betterfoliage.reed.biomes.tooltip=Настройка биомов, в которых камышам разрешено появляться.
|
||||
betterfoliage.reed.biomes.tooltip.element=Должны ли камыши встречаться в %s биоме?
|
||||
|
||||
betterfoliage.algae=Морские водоросли
|
||||
betterfoliage.algae.tooltip=Глубоководные водоросли на блоках земли.
|
||||
betterfoliage.algae.biomes=Список биомов
|
||||
betterfoliage.algae.biomes.tooltip=Настройка биомов, в которых водорослям разрешено появляться.
|
||||
betterfoliage.algae.biomes.tooltip.element=Должны ли водоросли встречаться в %s биоме?
|
||||
|
||||
betterfoliage.coral=Кораллы
|
||||
betterfoliage.coral.tooltip=Кораллы на песчаных блоках в глубокой воде.
|
||||
betterfoliage.coral.size=Размер кораллов
|
||||
betterfoliage.coral.size.tooltip=Размер торчащих частичек кораллов.
|
||||
betterfoliage.coral.crustSize=Размер коры
|
||||
betterfoliage.coral.crustSize.tooltip=Размер плоской части кораллов.
|
||||
betterfoliage.coral.chance=Шанс кораллов
|
||||
betterfoliage.coral.chance.tooltip=Шанс (N in 64) появления кораллов на определенном блоке.
|
||||
betterfoliage.coral.biomes=Список биомов
|
||||
betterfoliage.coral.biomes.tooltip=Настройка биомов, в которых разрешено появляться кораллам.
|
||||
betterfoliage.coral.biomes.tooltip.element=Должны ли кораллы появляться в %s биоме?
|
||||
betterfoliage.coral.shallowWater=Мелководные кораллы
|
||||
betterfoliage.coral.shallowWater.tooltip=Должны ли появляться кораллы в воде, глубиной в 1 блок?
|
||||
|
||||
betterfoliage.netherrack=Адская лоза
|
||||
betterfoliage.netherrack.tooltip=Висячая лоза под адским камнем
|
||||
|
||||
betterfoliage.fallingLeaves=Падающие листья
|
||||
betterfoliage.fallingLeaves.tooltip=Падение FX частиц листвы исходящие из низа блоков листвы
|
||||
betterfoliage.fallingLeaves.speed=Скорость частиц
|
||||
betterfoliage.fallingLeaves.speed.tooltip=Общая скорость частиц
|
||||
betterfoliage.fallingLeaves.windStrength=Сила ветра
|
||||
betterfoliage.fallingLeaves.windStrength.tooltip=Величина воздействия ветра в хорошую погоду (распространение нормального распределения сосредоточено на 0)
|
||||
betterfoliage.fallingLeaves.stormStrength=Сила шторма
|
||||
betterfoliage.fallingLeaves.stormStrength.tooltip=Дополнительная величина воздействия ветра в ненастную погоду (распространение нормального распределения сосредоточено на 0)
|
||||
betterfoliage.fallingLeaves.size=Размер частиц
|
||||
betterfoliage.fallingLeaves.chance=Шанс частиц
|
||||
betterfoliage.fallingLeaves.chance.tooltip=Вероятность каждого случайного рендеринга в такт (1/20 секунды) опадения частицы блока листвы.
|
||||
betterfoliage.fallingLeaves.perturb=Возмущение
|
||||
betterfoliage.fallingLeaves.perturb.tooltip=Величина эффекта возмущений. Добавляет штопорообразное движение к частице синхронизированной с его вращением.
|
||||
betterfoliage.fallingLeaves.lifetime=Максимальное время жизни
|
||||
betterfoliage.fallingLeaves.lifetime.tooltip=Максимальное время жизни частиц. Минимальное время жизни - 60%% от этого значения.
|
||||
betterfoliage.fallingLeaves.opacityHack=Непрозрачные частицы
|
||||
betterfoliage.fallingLeaves.opacityHack.tooltip=Запретить прозрачным блокам затемнять частицы даже тогда, когда частицы впереди. ВНИМАНИЕ: может спровоцировать баги.
|
||||
|
||||
betterfoliage.risingSoul=Адские духи
|
||||
betterfoliage.risingSoul.tooltip=Количество душ-частиц FX, испускаемых из верхней части блоков песка душ.
|
||||
betterfoliage.risingSoul.chance=Шанс частиц
|
||||
betterfoliage.risingSoul.chance.tooltip=Частота генерации частиц на песке душ.
|
||||
betterfoliage.risingSoul.speed=Скорость частиц
|
||||
betterfoliage.risingSoul.speed.tooltip=Вертикальная скорость движения частиц духов.
|
||||
betterfoliage.risingSoul.perturb=Возмущение
|
||||
betterfoliage.risingSoul.perturb.tooltip=Магнитуда эффекта возмущений. Добавляет штопороподобное движение частиц.
|
||||
betterfoliage.risingSoul.headSize=Размер духа
|
||||
betterfoliage.risingSoul.headSize.tooltip=Размер частицы духа
|
||||
betterfoliage.risingSoul.trailSize=Размер следов
|
||||
betterfoliage.risingSoul.trailSize.tooltip=Начальный размер следа частиц
|
||||
betterfoliage.risingSoul.opacity=Прозрачность
|
||||
betterfoliage.risingSoul.opacity.tooltip=Непрозрачность эффекта частиц
|
||||
betterfoliage.risingSoul.sizeDecay=Размер распада
|
||||
betterfoliage.risingSoul.sizeDecay.tooltip=Следующий размер частицы соответствует их же размеру в предыдущем такте (1/20 секунды).
|
||||
betterfoliage.risingSoul.opacityDecay=Непрозрачность распада
|
||||
betterfoliage.risingSoul.opacityDecay.tooltip=Следующий уровень прозрачности частицы соответствует их же уровню прозрачности в предыдущем такте (1/20 секунды).
|
||||
betterfoliage.risingSoul.lifetime=Максимальное время жизни
|
||||
betterfoliage.risingSoul.lifetime.tooltip=Максимальное время жизни эффекта частиц. Минимальное время жизни равно 60%% от этого числа.
|
||||
betterfoliage.risingSoul.trailLength=Длина следов
|
||||
betterfoliage.risingSoul.trailLength.tooltip=Количество предыдущих позиций, которые запомнила частица в тактах (1/20 секунды).
|
||||
betterfoliage.risingSoul.trailDensity=Плотность следов
|
||||
betterfoliage.risingSoul.trailDensity.tooltip=Рендер каждой предыдущий N’ой позиции в следах частиц.
|
||||
|
||||
|
||||
betterfoliage.connectedGrass=Соединенные текстуры травы
|
||||
betterfoliage.connectedGrass.enabled=Включить
|
||||
betterfoliage.connectedGrass.enabled.tooltip=Если блок травы находится над блоком земли: прорисовать верхнюю текстуру травы на всех сторонах блока травы.
|
||||
|
||||
betterfoliage.roundLogs=Цилиндрические брёвна
|
||||
betterfoliage.roundLogs.tooltip=Соединить круглые блоки в сплошные, полные блоки?
|
||||
betterfoliage.roundLogs.connectSolids=Соединение в крупные брёвна
|
||||
betterfoliage.roundLogs.connectSolids.tooltip=Соединить круглые блоки в сплошные, полные блоки?
|
||||
betterfoliage.roundLogs.connectPerpendicular=Соединение в перпендикулярные брёвна
|
||||
betterfoliage.roundLogs.connectPerpendicular.tooltip=Соединить круглые брёвна к перпендикулярным брёвнам относительно их оси?
|
||||
betterfoliage.roundLogs.lenientConnect=Мягкое округление
|
||||
betterfoliage.roundLogs.lenientConnect.tooltip=Соединение в параллельные круглые брёвна L-формы, не только 2х2.
|
||||
betterfoliage.roundLogs.connectGrass=Соединенная трава
|
||||
betterfoliage.roundLogs.connectGrass.tooltip=Заменяет землю под деревьями на траву, если она есть поблизости.
|
||||
betterfoliage.roundLogs.radiusSmall=Радиус фаски
|
||||
betterfoliage.roundLogs.radiusSmall.tooltip=Радиус обрезки углов от бревна.
|
||||
betterfoliage.roundLogs.radiusLarge=Радиус соединенной фаски
|
||||
betterfoliage.roundLogs.radiusLarge.tooltip=Радиус среза внешнего угла соединённых брёвен.
|
||||
betterfoliage.roundLogs.dimming=Затемнение
|
||||
betterfoliage.roundLogs.dimming.tooltip=Затемнить неясные длинные грани.
|
||||
betterfoliage.roundLogs.zProtection=Z-Защита
|
||||
betterfoliage.roundLogs.zProtection.tooltip=Для масштабирования параллельных битов соединения бревен, чтобы остановить Z-бой (мерцание). Попробуйте установить его как можно выше, для устранения мерцания.
|
||||
177
src/main/resources/assets/betterfoliage/lang/zh_cn.json
Normal file
177
src/main/resources/assets/betterfoliage/lang/zh_cn.json
Normal file
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"key.betterfoliage.gui": "打开BF设置",
|
||||
|
||||
"betterfoliage.global.enabled": "模组使用",
|
||||
"betterfoliage.global.enabled.tooltip": "如果关闭,BetterFoliage不会呈现任何东西",
|
||||
"betterfoliage.nVidia": "nVidia GPU",
|
||||
"betterfoliage.nVidia.tooltip": "明确你是否有一个nVidia GPU",
|
||||
|
||||
"betterfoliage.enabled": "启用",
|
||||
"betterfoliage.enabled.tooltip": "要启用这个功能吗?",
|
||||
"betterfoliage.hOffset": "水平偏移",
|
||||
"betterfoliage.hOffset.tooltip": "在方块中这个元素是横向距离移动的",
|
||||
"betterfoliage.vOffset": "垂直偏移",
|
||||
"betterfoliage.vOffset.tooltip": "在方块中这个元素是横向距离移动的",
|
||||
"betterfoliage.size": "尺寸",
|
||||
"betterfoliage.size.tooltip": "该元素的尺寸",
|
||||
"betterfoliage.heightMin": "最小高度",
|
||||
"betterfoliage.heightMin.tooltip": "最小元素的高度",
|
||||
"betterfoliage.heightMax": "最大高度",
|
||||
"betterfoliage.heightMax.tooltip": "最大元素的高度",
|
||||
"betterfoliage.population": "物体密度",
|
||||
"betterfoliage.population.tooltip": "生成的概率为64分之1",
|
||||
"betterfoliage.shaderWind": "光影水反中的 风的影响",
|
||||
"betterfoliage.shaderWind.tooltip": "能适用于光影水反中的\"风的影响\" 这一元素?",
|
||||
"betterfoliage.distance": "距离限制",
|
||||
"betterfoliage.distance.tooltip": "离游戏者的最大距离,在这个范围内呈现这个特征",
|
||||
"betterfoliage.saturationThreshold": "饱和度阈值",
|
||||
"betterfoliage.saturationThreshold.tooltip": "色彩饱和度将介于\"无色\"方块(使用生物群系的颜色)和\"有色\"方块(使用材质特定颜色)之间",
|
||||
|
||||
"betterfoliage.rendererror": "§a[BF更好的叶子]§f 错误:渲染方块 %s 此位置 %s",
|
||||
|
||||
"betterfoliage.shaders": "着色器配置",
|
||||
"betterfoliage.shaders.tooltip": "Configure integration with shaders",
|
||||
"betterfoliage.shaders.leavesId": "叶子 ID",
|
||||
"betterfoliage.shaders.leavesId.tooltip": "方块 ID 将作为所有叶片都使用的相同ID提供给光影水反 如果你的光影水反文件使用了一个 §6block.properties§e 文件, 你可能需要修改这个以匹配光影水反的映射",
|
||||
"betterfoliage.shaders.grassId": "草 ID",
|
||||
"betterfoliage.shaders.grassId.tooltip": "方块 ID 将作为所有的草和作物都使用的相同ID提供给光影水反 如果你的光影水反文件使用了一个 §6block.properties§e 文件, 你可能需要修改这个以匹配光影水反的映射",
|
||||
|
||||
"betterfoliage.leaves": "额外的叶片",
|
||||
"betterfoliage.leaves.tooltip": "叶方块上额外的密叶",
|
||||
"betterfoliage.leaves.dense": "浓密模式",
|
||||
"betterfoliage.leaves.dense.tooltip": "浓密的叶子会有更多额外的叶片",
|
||||
"betterfoliage.leaves.snowEnabled": "启用雪覆盖叶片",
|
||||
"betterfoliage.leaves.snowEnabled.tooltip": "是否启用被雪覆盖的额外叶片?",
|
||||
"betterfoliage.leaves.hideInternal": "隐藏内部的叶片",
|
||||
"betterfoliage.leaves.hideInternal.tooltip": "如果该叶方块完全被其他叶方块或者固体方块包围,将不渲染额外的叶片",
|
||||
|
||||
"betterfoliage.shortGrass": "短草和菌丝",
|
||||
"betterfoliage.shortGrass.tooltip": "一簇小草或者一丛菌丝长在合适的方块的顶部",
|
||||
"betterfoliage.shortGrass.useGenerated": "为草使用mod创建的材质",
|
||||
"betterfoliage.shortGrass.useGenerated.tooltip": "mod创建的材质指的是将已选用的资源包中高草的材质切一半来用",
|
||||
"betterfoliage.shortGrass.myceliumEnabled": "使用在菌丝上",
|
||||
"betterfoliage.shortGrass.myceliumEnabled.tooltip": "将这个特性利用到菌丝中吗?",
|
||||
"betterfoliage.shortGrass.grassEnabled": "使用在草方块上",
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "将这个特性利用到草方块中吗?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "使用在雪地里",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "使用到被雪覆盖的草方块中去?",
|
||||
"betterfoliage.shortGrass.saturationThreshold": "饱和度阈值",
|
||||
"betterfoliage.shortGrass.saturationThreshold.tooltip": "色彩饱和度将介于\"无色\"方块(使用生物群系的颜色)和\"有色\"方块(使用材质特定颜色)之间",
|
||||
|
||||
"betterfoliage.connectedGrass": "草的纹理的连接",
|
||||
"betterfoliage.connectedGrass.tooltip": "使得草方块侧面拥有像顶面的材质",
|
||||
"betterfoliage.connectedGrass.enabled": "启用",
|
||||
"betterfoliage.connectedGrass.enabled.tooltip": "如果泥土上有草方块:所有的草方块的侧面换成草方块顶部的材质,",
|
||||
"betterfoliage.connectedGrass.snowEnabled": "使用在被雪覆盖的草方块上",
|
||||
"betterfoliage.connectedGrass.snowEnabled.tooltip": "是否应用于被雪覆盖的草方块上?",
|
||||
|
||||
"betterfoliage.hangingGrass": "悬挂的草",
|
||||
"betterfoliage.hangingGrass.tooltip": "草会在边缘的草方块上垂下来",
|
||||
"betterfoliage.hangingGrass.separation": "垂下来的距离",
|
||||
"betterfoliage.hangingGrass.separation.tooltip": "有多长的挂草会从草方块上垂下来",
|
||||
|
||||
"betterfoliage.cactus": "更好的仙人掌",
|
||||
"betterfoliage.cactus.tooltip": "用额外的仙人掌刺和圆润的形状来提高仙人掌的真实性",
|
||||
"betterfoliage.cactus.sizeVariation": "尺寸变化",
|
||||
"betterfoliage.cactus.sizeVariation.tooltip": "仙人掌的尺寸随机变化的范围",
|
||||
|
||||
"betterfoliage.lilypad": "更好的莲叶与荷花",
|
||||
"betterfoliage.lilypad.tooltip": "用莲叶的根和随机的莲花以提高莲叶的真实性",
|
||||
"betterfoliage.lilypad.flowerChance": "花的生成概率",
|
||||
"betterfoliage.lilypad.flowerChance.tooltip": "(64个莲叶中 N 个)莲叶具有花了",
|
||||
|
||||
"betterfoliage.reed": "芦苇",
|
||||
"betterfoliage.reed.tooltip": "芦苇会生成在浅水中的泥土块上",
|
||||
"betterfoliage.reed.biomes": "生物群系的列表",
|
||||
"betterfoliage.reed.biomes.tooltip": "配置允许出现芦苇的生物群系",
|
||||
"betterfoliage.reed.biomes.tooltip.element": "芦苇应该出现在%s生物群落?",
|
||||
|
||||
"betterfoliage.algae": "藻类",
|
||||
"betterfoliage.algae.tooltip": "藻类会生成在深水中的泥土块上",
|
||||
"betterfoliage.algae.biomes": "生物群系的列表",
|
||||
"betterfoliage.algae.biomes.tooltip": "配置允许出现藻类的生物群系",
|
||||
"betterfoliage.algae.biomes.tooltip.element": "藻类应该出现在%s生物群落?",
|
||||
|
||||
"betterfoliage.coral": "珊瑚",
|
||||
"betterfoliage.coral.tooltip": "珊瑚会生成在深水中的沙块上",
|
||||
"betterfoliage.coral.size": "珊瑚的大小",
|
||||
"betterfoliage.coral.size.tooltip": "珊瑚伸出的部分的大小",
|
||||
"betterfoliage.coral.crustSize": "外壳尺寸",
|
||||
"betterfoliage.coral.crustSize.tooltip": "珊瑚的平的部分的尺寸",
|
||||
"betterfoliage.coral.chance": "珊瑚的生成概率",
|
||||
"betterfoliage.coral.chance.tooltip": "64个特殊表面的方块中有N个有机会出现珊瑚",
|
||||
"betterfoliage.coral.biomes": "生物群落的列表",
|
||||
"betterfoliage.coral.biomes.tooltip": "配置允许出现珊瑚的生物群系",
|
||||
"betterfoliage.coral.biomes.tooltip.element": "珊瑚应该出现在%s生物群落?",
|
||||
"betterfoliage.coral.shallowWater": "浅海珊瑚",
|
||||
"betterfoliage.coral.shallowWater.tooltip": "珊瑚应该生成在深水1格?",
|
||||
|
||||
"betterfoliage.netherrack": "下界岩下面的藤蔓",
|
||||
"betterfoliage.netherrack.tooltip": "下界岩下面将挂上藤蔓",
|
||||
|
||||
"betterfoliage.fallingLeaves": "落叶",
|
||||
"betterfoliage.fallingLeaves.tooltip": "落叶粒子效果 FX 会从叶方块底部下落",
|
||||
"betterfoliage.fallingLeaves.speed": "粒子速度",
|
||||
"betterfoliage.fallingLeaves.speed.tooltip": "总体的粒子速度",
|
||||
"betterfoliage.fallingLeaves.windStrength": "柔风的强度",
|
||||
"betterfoliage.fallingLeaves.windStrength.tooltip": "在晴天中体现的风的效果(默认以0为中心散播)",
|
||||
"betterfoliage.fallingLeaves.stormStrength": "风暴的强度",
|
||||
"betterfoliage.fallingLeaves.stormStrength.tooltip": "附加在阴天里体现的风的效果(默认以0为中心散播)",
|
||||
"betterfoliage.fallingLeaves.size": "粒子大小",
|
||||
"betterfoliage.fallingLeaves.chance": "粒子生成机率",
|
||||
"betterfoliage.fallingLeaves.chance.tooltip": "随机渲染刻激发叶方块生成粒子的概率",
|
||||
"betterfoliage.fallingLeaves.perturb": "扰动",
|
||||
"betterfoliage.fallingLeaves.perturb.tooltip": "扰动的大小.增加了一个与之旋转同步的螺旋状动量",
|
||||
"betterfoliage.fallingLeaves.lifetime": "最长存在时间",
|
||||
"betterfoliage.fallingLeaves.lifetime.tooltip": "粒子的最长存在时间,仅在几秒内,最短时间为这个值60%%",
|
||||
"betterfoliage.fallingLeaves.opacityHack": "粒子不透明",
|
||||
"betterfoliage.fallingLeaves.opacityHack.tooltip": "阻止透明方块隐藏粒子,即使粒子已经在前面了.警告:可能会导致错误",
|
||||
|
||||
"betterfoliage.risingSoul": "灵魂沙上飘起的魂灵",
|
||||
"betterfoliage.risingSoul.tooltip": "灵魂沙的顶部飘出灵魂粒子 FX",
|
||||
"betterfoliage.risingSoul.chance": "粒子的生成机率",
|
||||
"betterfoliage.risingSoul.chance.tooltip": "随机渲染刻激发灵魂沙生成一个粒子的概率",
|
||||
"betterfoliage.risingSoul.speed": "粒子速度",
|
||||
"betterfoliage.risingSoul.speed.tooltip": "灵颗粒的垂直速度",
|
||||
"betterfoliage.risingSoul.perturb": "扰动",
|
||||
"betterfoliage.risingSoul.perturb.tooltip": "动的大小.增加了一个与之旋转同步的螺旋状动量",
|
||||
"betterfoliage.risingSoul.headSize": "魂灵的大小",
|
||||
"betterfoliage.risingSoul.headSize.tooltip": "灵魂粒子的大小",
|
||||
"betterfoliage.risingSoul.trailSize": "魂灵轨迹的大小",
|
||||
"betterfoliage.risingSoul.trailSize.tooltip": "灵魂粒子的轨迹的初始大小",
|
||||
"betterfoliage.risingSoul.opacity": "不透明度",
|
||||
"betterfoliage.risingSoul.opacity.tooltip": "粒子效果的不透明度",
|
||||
"betterfoliage.risingSoul.sizeDecay": "大小的衰减",
|
||||
"betterfoliage.risingSoul.sizeDecay.tooltip": "灵魂轨迹粒子大小与前一刻他的大小相关",
|
||||
"betterfoliage.risingSoul.opacityDecay": "不透明度的衰减",
|
||||
"betterfoliage.risingSoul.opacityDecay.tooltip": "灵魂轨迹粒子不透明度与前一刻他的不透明度相关",
|
||||
"betterfoliage.risingSoul.lifetime": "最长存在时间",
|
||||
"betterfoliage.risingSoul.lifetime.tooltip": "粒子的最长存在时间,仅在几秒内,最短时间为这个值60%%",
|
||||
"betterfoliage.risingSoul.trailLength": "魂灵轨迹长度",
|
||||
"betterfoliage.risingSoul.trailLength.tooltip": "在游戏刻内粒子记录的轨迹位置的数量",
|
||||
"betterfoliage.risingSoul.trailDensity": "魂灵轨迹密集程度",
|
||||
"betterfoliage.risingSoul.trailDensity.tooltip": "整条轨迹中渲染每第N个轨迹位置",
|
||||
|
||||
"betterfoliage.roundLogs": "圆木",
|
||||
"betterfoliage.roundLogs.tooltip": "使木方块拥有八角形的横截面",
|
||||
"betterfoliage.roundLogs.plantsOnly": "仅仅应用于植物性材料",
|
||||
"betterfoliage.roundLogs.plantsOnly.tooltip": "是否仅仅应用于木质材料和干草堆,而不是所有圆柱方块?",
|
||||
"betterfoliage.roundLogs.connectSolids": "连接到固体方块上",
|
||||
"betterfoliage.roundLogs.connectSolids.tooltip": "要使圆木连接到完整的固体方块上吗?",
|
||||
"betterfoliage.roundLogs.connectPerpendicular": "连接到垂直的圆木上",
|
||||
"betterfoliage.roundLogs.connectPerpendicular.tooltip": "要使圆木根据它的角度连接到垂直圆木上吗?",
|
||||
"betterfoliage.roundLogs.lenientConnect": "巨大的圆木",
|
||||
"betterfoliage.roundLogs.lenientConnect.tooltip": "在L形中也连接成巨大的圆木, 不只是2x2形",
|
||||
"betterfoliage.roundLogs.connectGrass": "连接到草方块上",
|
||||
"betterfoliage.roundLogs.connectGrass.tooltip": "如果树木下的泥土附近有草方块,那么它也会被渲染成草方块",
|
||||
"betterfoliage.roundLogs.radiusSmall": "圆木斜面的半径",
|
||||
"betterfoliage.roundLogs.radiusSmall.tooltip": "从圆木的外角砍掉多大的部分",
|
||||
"betterfoliage.roundLogs.radiusLarge": "连接圆木的斜面半径",
|
||||
"betterfoliage.roundLogs.radiusLarge.tooltip": "从连接着的巨大圆木的外角砍掉多大的部分",
|
||||
"betterfoliage.roundLogs.dimming": "调光变暗",
|
||||
"betterfoliage.roundLogs.dimming.tooltip": "将圆木阴暗面变黑的程度",
|
||||
"betterfoliage.roundLogs.zProtection": "Z-Protection [Z保护]",
|
||||
"betterfoliage.roundLogs.zProtection.tooltip": "用多少倍放大平行圆木连接处的面 去停止 Z-fighting (闪烁)[斑驳,两个多边形共面所出现的效果].在没有发生故障的情况下,试着将这个值设置得尽可能的高[这是Z-fighting出现的原因:多个多面体的面重叠在一起,会一直闪烁]",
|
||||
"betterfoliage.roundLogs.defaultY": "默认垂直",
|
||||
"betterfoliage.roundLogs.defaultY.tooltip": "如果开启, 方向不确定的圆木将会渲染成垂直的.否则, 它们仅仅渲染为cube型方块."
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.LeavesBlock
|
||||
@@ -1,4 +0,0 @@
|
||||
minecraft:block/leaves,all
|
||||
minecraft:block/cube_all,all
|
||||
|
||||
biomesoplenty:block/leaves_overlay,leaves
|
||||
@@ -1,8 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.BlockLilyPad
|
||||
|
||||
// Biomes O'Plenty
|
||||
biomesoplenty.common.block.BlockBOPLilypad
|
||||
|
||||
// TerraFirmaCraft
|
||||
com.bioxx.tfc.Blocks.Vanilla.BlockCustomLilyPad
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.LogBlock
|
||||
@@ -1,4 +0,0 @@
|
||||
// Vanilla
|
||||
block/column_side,side,end
|
||||
block/cube_column,side,end
|
||||
block/cube_all,all,all
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.MyceliumBlock
|
||||
@@ -7,7 +7,8 @@
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
"MixinOptifineBlockUtils"
|
||||
"MixinOptifineBlockUtils",
|
||||
"MixinOptifineChunkRendererDispatcher"
|
||||
],
|
||||
"server": [
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user