Compare commits
16 Commits
1.14.4-Fab
...
1.16.5-Fab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7065c9dd8c | ||
|
|
368b50a578 | ||
|
|
cd2d46f422 | ||
|
|
03367443b4 | ||
|
|
81d3a2eba4 | ||
|
|
79ef6cfaa9 | ||
|
|
40fd46b278 | ||
|
|
9dacdde761 | ||
|
|
6a31adcdd8 | ||
|
|
b46fdeaeef | ||
|
|
f1fa629c5c | ||
|
|
ad914ac03a | ||
|
|
d8ce8ecb06 | ||
|
|
8d9214c190 | ||
|
|
594db19bfb | ||
|
|
a89edd53a4 |
@@ -2,8 +2,8 @@ import net.fabricmc.loom.task.RemapJarTask
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
|
||||
plugins {
|
||||
kotlin("jvm").version("1.3.60")
|
||||
id("fabric-loom").version("0.2.6-SNAPSHOT")
|
||||
id("fabric-loom").version("0.6-SNAPSHOT")
|
||||
kotlin("jvm").version("1.4.31")
|
||||
id("org.ajoberstar.grgit").version("3.1.1")
|
||||
}
|
||||
apply(plugin = "org.ajoberstar.grgit")
|
||||
@@ -13,9 +13,10 @@ val semVer = "${project.version}+$gitHash"
|
||||
val jarName = "BetterFoliage-$semVer-Fabric-${properties["mcVersion"]}"
|
||||
|
||||
repositories {
|
||||
maven("http://maven.fabricmc.net/")
|
||||
maven("https://maven.fabricmc.net/")
|
||||
maven("https://minecraft.curseforge.com/api/maven")
|
||||
maven("http://maven.modmuss50.me/")
|
||||
maven("https://maven.modmuss50.me/")
|
||||
maven("https://maven.shedaniel.me/")
|
||||
maven("https://grondag-repo.appspot.com").credentials { username = "guest"; password = "" }
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
@@ -32,7 +33,7 @@ dependencies {
|
||||
// configuration handling
|
||||
"modImplementation"("io.github.prospector:modmenu:${properties["modMenuVersion"]}")
|
||||
listOf("modImplementation", "include").forEach { configuration ->
|
||||
configuration("me.shedaniel.cloth:config-2:${properties["clothConfigVersion"]}")
|
||||
configuration("me.shedaniel.cloth:cloth-config-fabric:${properties["clothConfigVersion"]}")
|
||||
configuration("me.zeroeightsix:fiber:${properties["fiberVersion"]}")
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ dependencies {
|
||||
// "modImplementation"("grondag:canvas:0.7.+")
|
||||
|
||||
// Optifabric
|
||||
"modImplementation"("com.github.modmuss50:OptiFabric:df03dc2c22")
|
||||
// "modImplementation"("com.github.modmuss50:OptiFabric:1.0.0")
|
||||
"implementation"("org.zeroturnaround:zt-zip:1.13")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,17 +5,16 @@ group = com.github.octarine-noise
|
||||
name = betterfoliage
|
||||
jarName = BetterFoliage-Forge
|
||||
|
||||
version = 2.5.0
|
||||
version = 2.6.5
|
||||
|
||||
mcVersion = 1.14.4
|
||||
yarnMappings=1.14.4+build.15
|
||||
loaderVersion=0.7.3+build.176
|
||||
fabricVersion=0.4.2+build.246-1.14
|
||||
loomVersion=0.2.6-SNAPSHOT
|
||||
mcVersion = 1.16.5
|
||||
yarnMappings=1.16.5+build.6
|
||||
loaderVersion=0.11.3
|
||||
fabricVersion=0.32.5+1.16
|
||||
|
||||
kotlinVersion=1.3.60
|
||||
fabricKotlinVersion=1.3.60+build.1
|
||||
fabricKotlinVersion=1.5.0+kotlin.1.4.31
|
||||
|
||||
clothConfigVersion=1.8
|
||||
modMenuVersion=1.7.6+build.115
|
||||
clothConfigVersion=4.11.24
|
||||
modMenuVersion=1.16.9
|
||||
fiberVersion=0.8.0-2
|
||||
49
src/main/java/mods/betterfoliage/MixinConfigPlugin.java
Normal file
49
src/main/java/mods/betterfoliage/MixinConfigPlugin.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package mods.betterfoliage;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MixinConfigPlugin implements IMixinConfigPlugin {
|
||||
|
||||
Logger logger = LogManager.getLogger(this);
|
||||
|
||||
Boolean hasOptifine = null;
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
if (hasOptifine == null) {
|
||||
hasOptifine = FabricLoader.getInstance().isModLoaded("optifabric");
|
||||
if (hasOptifine) logger.log(Level.INFO, "[BetterFoliage] Optifabric detected, applying Optifine mixins");
|
||||
else logger.log(Level.INFO, "[BetterFoliage] Optifabric not detected, applying Vanilla mixins");
|
||||
}
|
||||
if (mixinClassName.endsWith("Vanilla") && hasOptifine) return false;
|
||||
if (mixinClassName.endsWith("Optifine") && !hasOptifine) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) { }
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() { return null; }
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { }
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() { return null; }
|
||||
|
||||
@Override
|
||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
|
||||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
|
||||
}
|
||||
@@ -19,8 +19,8 @@ import java.util.Random;
|
||||
|
||||
@Mixin(Block.class)
|
||||
public class MixinBlock {
|
||||
private static final String shouldSideBeRendered = "shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z";
|
||||
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;getCullShape(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Lnet/minecraft/util/shape/VoxelShape;";
|
||||
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z";
|
||||
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;getCullingFace(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Lnet/minecraft/util/shape/VoxelShape;";
|
||||
private static final String randomDisplayTick = "randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -14,10 +15,11 @@ 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)
|
||||
@SuppressWarnings({"UnnecessaryQualifiedMemberReference", "deprecation"})
|
||||
@Mixin(AbstractBlock.AbstractBlockState.class)
|
||||
@SuppressWarnings({"deprecation"})
|
||||
public class MixinBlockState {
|
||||
private static final String callFrom = "Lnet/minecraft/block/BlockState;getAmbientOcclusionLightLevel(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
private static final String callFrom = "Lnet/minecraft/block/AbstractBlock$AbstractBlockState;getAmbientOcclusionLightLevel(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
// why is the INVOKEVIRTUAL target class Block in the bytecode, not AbstractBlock?
|
||||
private static final String callTo = "Lnet/minecraft/block/Block;getAmbientOcclusionLightLevel(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
|
||||
|
||||
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
|
||||
|
||||
@@ -3,9 +3,10 @@ package mods.betterfoliage.mixin;
|
||||
import mods.betterfoliage.ClientChunkLoadCallback;
|
||||
import net.minecraft.client.world.ClientChunkManager;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.world.biome.source.BiomeArray;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -14,10 +15,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
@Mixin(ClientChunkManager.class)
|
||||
public class MixinClientChunkManager {
|
||||
|
||||
private static final String onLoadChunkFromPacket = "loadChunkFromPacket(Lnet/minecraft/world/World;IILnet/minecraft/util/PacketByteBuf;Lnet/minecraft/nbt/CompoundTag;IZ)Lnet/minecraft/world/chunk/WorldChunk;";
|
||||
private static final String onLoadChunkFromPacket = "Lnet/minecraft/client/world/ClientChunkManager;loadChunkFromPacket(IILnet/minecraft/world/biome/source/BiomeArray;Lnet/minecraft/network/PacketByteBuf;Lnet/minecraft/nbt/CompoundTag;IZ)Lnet/minecraft/world/chunk/WorldChunk;";
|
||||
|
||||
@Inject(method = onLoadChunkFromPacket, at = @At(value = "RETURN", ordinal = 2))
|
||||
void onLoadChunkFromPacket(World world, int chunkX, int chunkZ, PacketByteBuf data, CompoundTag nbt, int updatedSectionsBits, boolean clearOld, CallbackInfoReturnable<WorldChunk> ci) {
|
||||
void onLoadChunkFromPacket(int x, int z, @Nullable BiomeArray biomes, PacketByteBuf buf, CompoundTag tag, int verticalStripBitmask, boolean complete, CallbackInfoReturnable<WorldChunk> ci) {
|
||||
ClientChunkLoadCallback.EVENT.invoker().loadChunk(ci.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
@Mixin(targets = {"net.minecraft.client.world.ClientChunkManager$ClientChunkMap"})
|
||||
public class MixinClientChunkManagerChunkMap {
|
||||
|
||||
private static final String onSetAndCompare = "method_20183(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;";
|
||||
private static final String onCompareAndSet = "Lnet/minecraft/client/world/ClientChunkManager$ClientChunkMap;compareAndSet(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;";
|
||||
|
||||
@Inject(method = onSetAndCompare, at = @At("HEAD"))
|
||||
@Inject(method = onCompareAndSet, at = @At("HEAD"))
|
||||
void onSetAndCompare(int i, WorldChunk oldChunk, WorldChunk newChunk, CallbackInfoReturnable<WorldChunk> ci) {
|
||||
ClientChunkLoadCallback.EVENT.invoker().unloadChunk(oldChunk);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.level.LevelInfo;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -16,12 +18,13 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ClientWorld.class)
|
||||
public class MixinClientWorld {
|
||||
|
||||
private static final String ctor = "<init>(Lnet/minecraft/client/network/ClientPlayNetworkHandler;Lnet/minecraft/world/level/LevelInfo;Lnet/minecraft/world/dimension/DimensionType;ILnet/minecraft/util/profiler/Profiler;Lnet/minecraft/client/render/WorldRenderer;)V";
|
||||
private static final String scheduleBlockRender = "scheduleBlockRender(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
|
||||
private static final String ctor = "Lnet/minecraft/client/world/ClientWorld;<init>(Lnet/minecraft/client/network/ClientPlayNetworkHandler;Lnet/minecraft/client/world/ClientWorld$Properties;Lnet/minecraft/util/registry/RegistryKey;Lnet/minecraft/world/dimension/DimensionType;ILjava/util/function/Supplier;Lnet/minecraft/client/render/WorldRenderer;ZJ)V";
|
||||
private static final String scheduleBlockRerenderIfNeeded = "Lnet/minecraft/client/world/ClientWorld;scheduleBlockRerenderIfNeeded(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
|
||||
private static final String rendererNotify = "Lnet/minecraft/client/render/WorldRenderer;method_21596(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
|
||||
private static final String worldDisplayTick = "randomBlockDisplayTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)V";
|
||||
private static final String blockDisplayTick = "Lnet/minecraft/block/Block;randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
||||
@@ -30,13 +33,13 @@ public class MixinClientWorld {
|
||||
* Inject callback to get notified of client-side blockstate changes.
|
||||
* Used to invalidate caches in the {@link mods.betterfoliage.chunk.ChunkOverlayManager}
|
||||
*/
|
||||
@Inject(method = scheduleBlockRender, at = @At(value = "HEAD"))
|
||||
@Inject(method = scheduleBlockRerenderIfNeeded, at = @At(value = "HEAD"))
|
||||
void onClientBlockChanged(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo ci) {
|
||||
Hooks.onClientBlockChanged((ClientWorld) (Object) this, pos, oldState, newState);
|
||||
}
|
||||
|
||||
@Inject(method = ctor, at = @At("RETURN"))
|
||||
void onClientWorldCreated(ClientPlayNetworkHandler netHandler, LevelInfo levelInfo, DimensionType dimensionType, int i, Profiler profiler, WorldRenderer worldRenderer, CallbackInfo ci) {
|
||||
void onClientWorldCreated(ClientPlayNetworkHandler networkHandler, ClientWorld.Properties properties, RegistryKey<World> registryRef, DimensionType dimensionType, int loadDistance, Supplier<Profiler> profiler, WorldRenderer worldRenderer, boolean debugWorld, long seed, CallbackInfo ci) {
|
||||
ClientWorldLoadCallback.EVENT.invoker().loadWorld((ClientWorld) (Object) this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.ModelLoadingCallback;
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static net.minecraft.client.render.model.ModelLoader.MISSING;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(ModelLoader.class)
|
||||
public class MixinModelLoader {
|
||||
|
||||
@Shadow @Final private ResourceManager resourceManager;
|
||||
|
||||
// use the same trick fabric-api does to get around the no-mixins-in-constructors policy
|
||||
@Inject(at = @At("HEAD"), method = "addModel")
|
||||
private void addModelHook(ModelIdentifier id, CallbackInfo info) {
|
||||
// use the same trick fabric-api does to get around the no-mixins-in-constructors policy
|
||||
if (id == MISSING) {
|
||||
if (id.getPath().equals("trident_in_hand")) {
|
||||
// last step before stitching
|
||||
ModelLoadingCallback.EVENT.invoker().beginLoadModels((ModelLoader) (Object) this, resourceManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(ModelLoader.class)
|
||||
public class MixinModelLoaderOptifine {
|
||||
|
||||
private static final String loaderBake = "Lnet/minecraft/class_1088;getBakedModel(Lnet/minecraft/class_2960;Lnet/minecraft/class_3665;Ljava/util/function/Function;)Lnet/minecraft/class_1087;";
|
||||
private static final String modelBake = "Lnet/minecraft/class_1100;method_4753(Lnet/minecraft/class_1088;Ljava/util/function/Function;Lnet/minecraft/class_3665;Lnet/minecraft/class_2960;)Lnet/minecraft/class_1087;";
|
||||
|
||||
@SuppressWarnings("UnresolvedMixinReference")
|
||||
@Redirect(method = loaderBake, at = @At(value = "INVOKE", target = modelBake), remap = false)
|
||||
BakedModel onBakeModel(
|
||||
UnbakedModel unbaked,
|
||||
ModelLoader loader,
|
||||
Function<SpriteIdentifier, Sprite> textureGetter,
|
||||
ModelBakeSettings rotationContainer,
|
||||
Identifier modelId
|
||||
) {
|
||||
return BakeWrapperManager.INSTANCE.onBake(unbaked, loader, textureGetter, rotationContainer, modelId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(ModelLoader.class)
|
||||
public class MixinModelLoaderVanilla {
|
||||
|
||||
private static final String loaderBake = "Lnet/minecraft/client/render/model/ModelLoader;bake(Lnet/minecraft/util/Identifier;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;";
|
||||
private static final String modelBake = "Lnet/minecraft/client/render/model/UnbakedModel;bake(Lnet/minecraft/client/render/model/ModelLoader;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/BakedModel;";
|
||||
|
||||
@Redirect(method = loaderBake, at = @At(value = "INVOKE", target = modelBake))
|
||||
BakedModel onBakeModel(
|
||||
UnbakedModel unbaked,
|
||||
ModelLoader loader,
|
||||
Function<SpriteIdentifier, Sprite> textureGetter,
|
||||
ModelBakeSettings rotationContainer,
|
||||
Identifier modelId
|
||||
) {
|
||||
return BakeWrapperManager.INSTANCE.onBake(unbaked, loader, textureGetter, rotationContainer, modelId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,18 @@ import me.zeroeightsix.fiber.JanksonSettings
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.MainConfig
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.render.block.vanilla.*
|
||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||
import mods.betterfoliage.resource.discovery.BakedModelReplacer
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
||||
import mods.betterfoliage.resource.generated.GeneratedBlockTexturePack
|
||||
import net.fabricmc.api.ClientModInitializer
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper
|
||||
import net.fabricmc.fabric.mixin.resource.loader.ResourcePackManagerAccessor
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -27,18 +31,14 @@ import java.util.*
|
||||
object BetterFoliage : ClientModInitializer {
|
||||
const val MOD_ID = "betterfoliage"
|
||||
|
||||
var logger = LogManager.getLogger()
|
||||
var logDetail = SimpleLogger(
|
||||
"BetterFoliage",
|
||||
Level.DEBUG,
|
||||
false, false, true, false,
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
null,
|
||||
PropertiesUtil(Properties()),
|
||||
PrintStream(File(FabricLoader.getInstance().gameDirectory, "logs/betterfoliage.log").apply {
|
||||
parentFile.mkdirs()
|
||||
if (!exists()) createNewFile()
|
||||
})
|
||||
val detailLogStream = PrintStream(File("logs/betterfoliage.log").apply {
|
||||
parentFile.mkdirs()
|
||||
if (!exists()) createNewFile()
|
||||
})
|
||||
|
||||
fun logger(obj: Any) = LogManager.getLogger(obj)
|
||||
fun detailLogger(obj: Any) = SimpleLogger(
|
||||
obj::class.java.simpleName, Level.DEBUG, false, true, true, false, "yyyy-MM-dd HH:mm:ss", null, PropertiesUtil(Properties()), detailLogStream
|
||||
)
|
||||
|
||||
val configFile get() = File(FabricLoader.getInstance().configDirectory, "BetterFoliage.json")
|
||||
@@ -49,40 +49,46 @@ object BetterFoliage : ClientModInitializer {
|
||||
}
|
||||
|
||||
val blockConfig = BlockConfig()
|
||||
val generatedPack = GeneratedBlockTexturePack(Identifier(MOD_ID, "generated"), "betterfoliage-generated", "Better Foliage", "Generated leaf textures", logDetail)
|
||||
val modelReplacer = BakedModelReplacer()
|
||||
val generatedPack = GeneratedBlockTexturePack(Identifier(MOD_ID, "generated"), "betterfoliage-generated", "Better Foliage", "Generated leaf textures")
|
||||
|
||||
/** List of recognized [BlockState]s */
|
||||
var blockTypes = BlockTypeCache()
|
||||
|
||||
override fun onInitializeClient() {
|
||||
// Register generated resource pack
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(generatedPack)
|
||||
MinecraftClient.getInstance().resourcePackContainerManager.addCreator(generatedPack.finder)
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(generatedPack.reloader)
|
||||
(MinecraftClient.getInstance().resourcePackManager as ResourcePackManagerAccessor)
|
||||
.providers.add(generatedPack.finder)
|
||||
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(blockConfig)
|
||||
|
||||
// Add standard block support
|
||||
modelReplacer.discoverers.add(StandardLeafDiscovery)
|
||||
modelReplacer.discoverers.add(StandardGrassDiscovery)
|
||||
modelReplacer.discoverers.add(StandardLogDiscovery)
|
||||
modelReplacer.discoverers.add(StandardCactusDiscovery)
|
||||
modelReplacer.discoverers.add(LilyPadDiscovery)
|
||||
modelReplacer.discoverers.add(DirtDiscovery)
|
||||
modelReplacer.discoverers.add(SandDiscovery)
|
||||
modelReplacer.discoverers.add(MyceliumDiscovery)
|
||||
modelReplacer.discoverers.add(NetherrackDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardCactusDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardDirtDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardGrassDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardLeafDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardLilypadDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardMyceliumDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardNetherrackDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardRoundLogDiscovery)
|
||||
BakeWrapperManager.discoverers.add(StandardSandDiscovery)
|
||||
|
||||
// Init overlay layers
|
||||
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
|
||||
|
||||
// Init singletons
|
||||
LeafParticleRegistry
|
||||
NormalLeavesModel.Companion
|
||||
GrassBlockModel.Companion
|
||||
RoundLogModel.Companion
|
||||
CactusModel.Companion
|
||||
LilypadModel.Companion
|
||||
StandardLeafModel.Companion
|
||||
StandardGrassModel.Companion
|
||||
StandardRoundLogModel.Companion
|
||||
StandardCactusModel.Companion
|
||||
StandardLilypadModel.Companion
|
||||
DirtModel.Companion
|
||||
SandModel.Companion
|
||||
MyceliumModel.Companion
|
||||
NetherrackModel.Companion
|
||||
StandardSandModel.Companion
|
||||
StandardMyceliumModel.Companion
|
||||
StandardNetherrackModel.Companion
|
||||
RisingSoulParticle.Companion
|
||||
ShadersModIntegration
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +1,11 @@
|
||||
package mods.betterfoliage
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.world.World
|
||||
|
||||
// Optifine
|
||||
//val OptifineClassTransformer = ClassRefOld<Any>("optifine.OptiFineClassTransformer")
|
||||
//val BlockPosM = ClassRefOld<Any>("net.optifine.BlockPosM")
|
||||
//object ChunkCacheOF : ClassRefOld<Any>("net.optifine.override.ChunkCacheOF") {
|
||||
// val chunkCache = FieldRefOld(this, "chunkCache", ChunkRendererRegion)
|
||||
//}
|
||||
|
||||
//object RenderEnv : ClassRefOld<Any>("net.optifine.render.RenderEnv") {
|
||||
// val reset = MethodRefOld(this, "reset", void, BlockState, BlockPos)
|
||||
//}
|
||||
|
||||
// Optifine custom colors
|
||||
//val IColorizer = ClassRefOld<Any>("net.optifine.CustomColors\$IColorizer")
|
||||
//object CustomColors : ClassRefOld<Any>("net.optifine.CustomColors") {
|
||||
// val getColorMultiplier = MethodRefOld(this, "getColorMultiplier", int, BakedQuad, BlockState, ExtendedBlockView, BlockPos, RenderEnv)
|
||||
//}
|
||||
|
||||
// Optifine shaders
|
||||
//object SVertexBuilder : ClassRefOld<Any>("net.optifine.shaders.SVertexBuilder") {
|
||||
// val pushState = MethodRefOld(this, "pushEntity", void, BlockState, BlockPos, ExtendedBlockView, BufferBuilder)
|
||||
// val pushNum = MethodRefOld(this, "pushEntity", void, long)
|
||||
// val pop = MethodRefOld(this, "popEntity", void)
|
||||
//}
|
||||
|
||||
|
||||
|
||||
val VertexFormat_offsets = YarnHelper.requiredField<IntList>("net.minecraft.class_293", "field_1597", "Lit/unimi/dsi/fastutil/ints/IntList;")
|
||||
val BakedQuad_sprite = YarnHelper.requiredField<Sprite>("net.minecraft.class_777", "field_4176", "Lnet/minecraft/class_1058;")
|
||||
val WorldChunk_world = YarnHelper.requiredField<World>("net.minecraft.class_2818", "field_12858", "Lnet/minecraft/class_1937;")
|
||||
val ChunkRendererRegion_world = YarnHelper.requiredField<World>("net.minecraft.class_853", "field_4490", "Lnet/minecraft/class_1937;")
|
||||
@@ -2,16 +2,14 @@
|
||||
package mods.betterfoliage
|
||||
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.render.block.vanilla.LeafParticleKey
|
||||
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
||||
import mods.betterfoliage.render.particle.FallingLeafParticle
|
||||
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||
import mods.betterfoliage.render.block.vanilla.LeafKey
|
||||
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.random
|
||||
import mods.betterfoliage.util.randomD
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockRenderLayer.CUTOUT
|
||||
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.MinecraftClient
|
||||
@@ -25,7 +23,7 @@ import net.minecraft.world.BlockView
|
||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
||||
if (BetterFoliage.config.enabled &&
|
||||
BetterFoliage.config.roundLogs.enabled &&
|
||||
BetterFoliage.modelReplacer.getTyped<RoundLogKey>(state) != null
|
||||
BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state)
|
||||
) return BetterFoliage.config.roundLogs.dimming.toFloat()
|
||||
return original
|
||||
}
|
||||
@@ -53,15 +51,17 @@ fun onRandomDisplayTick(world: ClientWorld, pos: BlockPos) {
|
||||
BetterFoliage.config.fallingLeaves.enabled &&
|
||||
world.isAir(pos + Direction.DOWN.offset) &&
|
||||
randomD() < BetterFoliage.config.fallingLeaves.chance) {
|
||||
BetterFoliage.modelReplacer.getTyped<LeafKey>(state)?.let { key ->
|
||||
FallingLeafParticle(world, pos, key).addIfValid()
|
||||
BetterFoliage.blockTypes.getTyped<LeafParticleKey>(state)?.let { key ->
|
||||
val blockColor = MinecraftClient.getInstance().blockColors.getColor(state, world, pos, 0)
|
||||
FallingLeafParticle(world, pos, key, blockColor, random).addIfValid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getVoxelShapeOverride(state: BlockState, reader: BlockView, pos: BlockPos, dir: Direction): VoxelShape {
|
||||
if (BetterFoliage.modelReplacer[state] is RoundLogKey) {
|
||||
if (BetterFoliage.blockTypes.hasTyped<RoundLogKey>(state)) {
|
||||
return VoxelShapes.empty()
|
||||
}
|
||||
return state.getCullShape(reader, pos, dir)
|
||||
// TODO ?
|
||||
return state.getCullingFace(reader, pos, dir)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mods.betterfoliage.chunk
|
||||
|
||||
import mods.betterfoliage.ChunkRendererRegion_world
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.offset
|
||||
@@ -7,9 +8,11 @@ import mods.betterfoliage.util.plus
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.chunk.ChunkRendererRegion
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import net.minecraft.world.WorldView
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
@@ -17,7 +20,7 @@ import net.minecraft.world.biome.Biome
|
||||
* block-relative coordinates.
|
||||
*/
|
||||
interface BlockCtx {
|
||||
val world: ExtendedBlockView
|
||||
val world: BlockRenderView
|
||||
val pos: BlockPos
|
||||
|
||||
fun offset(dir: Direction) = offset(dir.offset)
|
||||
@@ -27,9 +30,11 @@ interface BlockCtx {
|
||||
fun state(dir: Direction) = world.getBlockState(pos + dir.offset)
|
||||
fun state(offset: Int3) = world.getBlockState(pos + offset)
|
||||
|
||||
val biome: Biome get() = world.getBiome(pos)
|
||||
val biome: Biome? get() =
|
||||
(world as? WorldView)?.getBiome(pos) ?:
|
||||
(world as? ChunkRendererRegion)?.let { ChunkRendererRegion_world[it]?.getBiome(pos) }
|
||||
|
||||
val isNormalCube: Boolean get() = state.isSimpleFullBlock(world, pos)
|
||||
val isNormalCube: Boolean get() = state.isOpaqueFullCube(world, pos)
|
||||
|
||||
fun shouldSideBeRendered(side: Direction) = Block.shouldDrawSide(state, world, pos, side)
|
||||
|
||||
@@ -40,7 +45,7 @@ interface BlockCtx {
|
||||
}
|
||||
|
||||
open class BasicBlockCtx(
|
||||
override val world: ExtendedBlockView,
|
||||
override val world: BlockRenderView,
|
||||
override val pos: BlockPos
|
||||
) : BlockCtx {
|
||||
override val state = world.getBlockState(pos)
|
||||
@@ -48,8 +53,8 @@ open class BasicBlockCtx(
|
||||
fun cache() = CachedBlockCtx(world, pos)
|
||||
}
|
||||
|
||||
open class CachedBlockCtx(world: ExtendedBlockView, pos: BlockPos) : BasicBlockCtx(world, pos) {
|
||||
open class CachedBlockCtx(world: BlockRenderView, pos: BlockPos) : BasicBlockCtx(world, pos) {
|
||||
var neighbors = Array<BlockState>(6) { world.getBlockState(pos + allDirections[it].offset) }
|
||||
override var biome: Biome = world.getBiome(pos)
|
||||
override var biome: Biome? = super.biome
|
||||
override fun state(dir: Direction) = neighbors[dir.ordinal]
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package mods.betterfoliage.chunk
|
||||
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.LightType
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
/**
|
||||
* Delegating [IBlockAccess] that fakes a _modified_ location to return values from a _target_ location.
|
||||
@@ -21,7 +21,7 @@ open class OffsetBlockView(open val original: BlockView, val modded: BlockPos, v
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
||||
class OffsetExtBlockView(val original: ExtendedBlockView, val modded: BlockPos, val target: BlockPos) : ExtendedBlockView by original {
|
||||
class OffsetExtBlockView(val original: WorldView, val modded: BlockPos, val target: BlockPos) : WorldView by original {
|
||||
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
||||
|
||||
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
|
||||
@@ -29,7 +29,7 @@ class OffsetExtBlockView(val original: ExtendedBlockView, val modded: BlockPos,
|
||||
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
|
||||
|
||||
override fun getLightLevel(type: LightType, pos: BlockPos) = original.getLightLevel(type, actualPos(pos))
|
||||
override fun getLightmapIndex(pos: BlockPos, light: Int) = original.getLightmapIndex(actualPos(pos), light)
|
||||
override fun getBaseLightLevel(pos: BlockPos, light: Int) = original.getBaseLightLevel(actualPos(pos), light)
|
||||
override fun getBiome(pos: BlockPos) = original.getBiome(actualPos(pos))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import net.minecraft.client.render.chunk.ChunkRendererRegion
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.ViewableWorld
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldView
|
||||
import net.minecraft.world.chunk.WorldChunk
|
||||
import net.minecraft.world.dimension.DimensionType
|
||||
import java.util.*
|
||||
@@ -21,14 +21,9 @@ import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
|
||||
// net.minecraft.world.chunk.WorldChunk.world
|
||||
val WorldChunk_world = YarnHelper.requiredField<World>("net.minecraft.class_2818", "field_12858", "Lnet/minecraft/class_1937;")
|
||||
// net.minecraft.client.render.chunk.ChunkRendererRegion.world
|
||||
val ChunkRendererRegion_world = YarnHelper.requiredField<World>("net.minecraft.class_853", "field_4490", "Lnet/minecraft/class_1937;")
|
||||
|
||||
val ExtendedBlockView.dimType: DimensionType get() = when {
|
||||
this is ViewableWorld -> dimension.type
|
||||
this is ChunkRendererRegion -> this[ChunkRendererRegion_world]!!.dimension.type
|
||||
val BlockRenderView.dimType: DimensionType get() = when {
|
||||
this is WorldView -> dimension
|
||||
this is ChunkRendererRegion -> this[ChunkRendererRegion_world]!!.dimension
|
||||
// this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache]!!.dimType
|
||||
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
|
||||
}
|
||||
@@ -38,7 +33,7 @@ val ExtendedBlockView.dimType: DimensionType get() = when {
|
||||
*/
|
||||
interface ChunkOverlayLayer<T> {
|
||||
fun calculate(ctx: BlockCtx): T
|
||||
fun onBlockUpdate(world: ExtendedBlockView, pos: BlockPos)
|
||||
fun onBlockUpdate(world: WorldView, pos: BlockPos)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import mods.betterfoliage.resource.VeryEarlyReloadListener
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureListConfiguration
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class BlockConfig {
|
||||
class BlockConfig : VeryEarlyReloadListener {
|
||||
private val list = mutableListOf<Any>()
|
||||
|
||||
val leafBlocks = blocks("leaves_blocks_default.cfg")
|
||||
val leafModels = models("leaves_models_default.cfg")
|
||||
val grassBlocks = blocks("grass_blocks_default.cfg")
|
||||
val grassModels = models("grass_models_default.cfg")
|
||||
val mycelium = blocks("mycelium_blocks_default.cfg")
|
||||
// val dirt = blocks("dirt_default.cfg")
|
||||
val crops = blocks("crop_default.cfg")
|
||||
// val mycelium = blocks("mycelium_blocks_default.cfg")
|
||||
// val dirt = blocks("dirt_default.cfg")
|
||||
// val crops = blocks("crop_default.cfg")
|
||||
val logBlocks = blocks("log_blocks_default.cfg")
|
||||
val logModels = models("log_models_default.cfg")
|
||||
val sand = blocks("sand_default.cfg")
|
||||
val lilypad = blocks("lilypad_default.cfg")
|
||||
val cactus = blocks("cactus_default.cfg")
|
||||
val netherrack = blocks("netherrack_blocks_default.cfg")
|
||||
// val sand = blocks("sand_default.cfg")
|
||||
// val lilypad = blocks("lilypad_default.cfg")
|
||||
// val cactus = blocks("cactus_default.cfg")
|
||||
// val netherrack = blocks("netherrack_blocks_default.cfg")
|
||||
|
||||
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
private fun models(cfgName: String) = ModelTextureListConfiguration(Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
|
||||
fun reloadConfig(manager: ResourceManager) {
|
||||
|
||||
override fun getFabricId() = Identifier(BetterFoliage.MOD_ID, "block-config")
|
||||
|
||||
override fun onReloadStarted(manager: ResourceManager) {
|
||||
list.forEach { when(it) {
|
||||
is ConfigurableBlockMatcher -> it.readDefaults(manager)
|
||||
is ModelTextureListConfiguration -> it.readDefaults(manager)
|
||||
|
||||
@@ -8,12 +8,16 @@ import me.zeroeightsix.fiber.tree.ConfigLeaf
|
||||
import me.zeroeightsix.fiber.tree.ConfigNode
|
||||
import me.zeroeightsix.fiber.tree.ConfigValue
|
||||
import net.minecraft.client.resource.language.I18n
|
||||
import net.minecraft.text.LiteralText
|
||||
import java.util.*
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
const val MAX_LINE_LEN = 30
|
||||
|
||||
fun textify(string: String) = LiteralText(string)
|
||||
fun textify(strings: Array<String>) = strings.map(::LiteralText).toTypedArray()
|
||||
|
||||
sealed class DelegatingConfigNode<N: ConfigLeaf>(val fiberNode: N) {
|
||||
abstract fun createClothNode(names: List<String>): AbstractConfigListEntry<*>
|
||||
}
|
||||
@@ -24,9 +28,9 @@ open class DelegatingConfigGroup(fiberNode: ConfigNode) : DelegatingConfigNode<C
|
||||
val children = mutableListOf<DelegatingConfigNode<*>>()
|
||||
override fun createClothNode(names: List<String>): SubCategoryListEntry {
|
||||
val builder = ConfigEntryBuilder.create()
|
||||
.startSubCategory(names.joinToString(".").translate())
|
||||
.setTooltip(*names.joinToString(".").translateTooltip())
|
||||
.setExpended(false)
|
||||
.startSubCategory(textify(names.joinToString(".").translate()))
|
||||
.setTooltip(*textify(names.joinToString(".").translateTooltip()))
|
||||
.setExpanded(false)
|
||||
children.forEach { builder.add(it.createClothNode(names + it.fiberNode.name!!)) }
|
||||
return builder.build()
|
||||
}
|
||||
@@ -85,8 +89,8 @@ fun boolean(
|
||||
.build()
|
||||
|
||||
override fun createClothNode(node: ConfigValue<Boolean>, names: List<String>) = ConfigEntryBuilder.create()
|
||||
.startBooleanToggle(langKey(names).translate(), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.startBooleanToggle(textify(langKey(names).translate()), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
||||
.setSaveConsumer { node.value = valueOverride(it) }
|
||||
.build()
|
||||
}
|
||||
@@ -104,8 +108,8 @@ fun integer(
|
||||
.build()
|
||||
|
||||
override fun createClothNode(node: ConfigValue<Int>, names: List<String>) = ConfigEntryBuilder.create()
|
||||
.startIntField(langKey(names).translate(), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.startIntField(textify(langKey(names).translate()), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
||||
.setMin(min).setMax(max)
|
||||
.setSaveConsumer { node.value = valueOverride(it) }
|
||||
.build()
|
||||
@@ -124,8 +128,8 @@ fun double(
|
||||
.build()
|
||||
|
||||
override fun createClothNode(node: ConfigValue<Double>, names: List<String>) = ConfigEntryBuilder.create()
|
||||
.startDoubleField(langKey(names).translate(), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
|
||||
.startDoubleField(textify(langKey(names).translate()), node.value!!)
|
||||
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(textify(it.translateTooltip())) else Optional.empty() })
|
||||
.setMin(min).setMax(max)
|
||||
.setSaveConsumer { node.value = valueOverride(it) }
|
||||
.build()
|
||||
|
||||
@@ -39,6 +39,8 @@ class LeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
|
||||
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
|
||||
val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
val saturationThreshold by double(0.1, min = 0.0, max = 1.0, langKey = recurring)
|
||||
}
|
||||
|
||||
class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
||||
@@ -52,7 +54,7 @@ class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), Populati
|
||||
override val population by population(64)
|
||||
val useGenerated by boolean(false)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
val saturationThreshold by double(0.1, min = 0.0, max = 1.0)
|
||||
val saturationThreshold by double(0.1, min = 0.0, max = 1.0, langKey = recurring)
|
||||
}
|
||||
|
||||
class ConnectedGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
@@ -70,14 +72,14 @@ class RoundLogConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
val connectGrass by boolean(true)
|
||||
|
||||
val radiusSmall by double(0.25, min = 0.0, max = 0.5)
|
||||
val radiusLarge by double(0.25, min = 0.0, max = 0.5) { it.coerceAtLeast(radiusSmall) }
|
||||
val radiusLarge by double(0.44, min = 0.0, max = 0.5) { it.coerceAtLeast(radiusSmall) }
|
||||
val dimming by double(0.7, min = 0.0, max = 1.0)
|
||||
val zProtection by double(0.99, min = 0.9, max = 1.0)
|
||||
}
|
||||
|
||||
class CactusConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
val enabled by boolean(true, langKey = recurring)
|
||||
val size by double(1.3, min = 0.5, max = 1.5, langKey = recurring)
|
||||
val size by double(1.3, min = 1.0, max = 2.0, langKey = recurring)
|
||||
val sizeVariation by double(0.1, min = 0.0, max = 0.5)
|
||||
val hOffset by double(0.1, min = 0.0, max = 0.5, langKey = recurring)
|
||||
}
|
||||
@@ -86,6 +88,7 @@ class LilypadConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationC
|
||||
override val enabled by boolean(true, langKey = recurring)
|
||||
val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring)
|
||||
override val population by population(16)
|
||||
val shaderWind by boolean(true, langKey = recurring)
|
||||
}
|
||||
|
||||
class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
|
||||
@@ -130,6 +133,7 @@ class NetherrackConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
|
||||
class FallingLeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
|
||||
val enabled by boolean(true, langKey = recurring)
|
||||
val opacityHack by boolean(false)
|
||||
val speed by double(0.05, min = 0.01, max = 0.15)
|
||||
val windStrength by double(0.5, min = 0.1, max = 2.0)
|
||||
val stormStrength by double(0.8, min = 0.1, max = 2.0) { it.coerceAtLeast(windStrength) }
|
||||
|
||||
13
src/main/kotlin/mods/betterfoliage/config/MiscDefaults.kt
Normal file
13
src/main/kotlin/mods/betterfoliage/config/MiscDefaults.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
val CACTUS_BLOCKS = listOf(Blocks.CACTUS)
|
||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL)
|
||||
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
|
||||
val NETHERRACK_BLOCKS = listOf(Blocks.NETHERRACK)
|
||||
|
||||
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
|
||||
val SNOW_MATERIALS = listOf(Material.SNOW_BLOCK)
|
||||
@@ -1,110 +0,0 @@
|
||||
package mods.betterfoliage.integration
|
||||
|
||||
/*
|
||||
val TextureLeaves = ClassRefOld<Any>("forestry.arboriculture.models.TextureLeaves")
|
||||
val TextureLeaves_leafTextures = FieldRefOld(TextureLeaves, "leafTextures", Map)
|
||||
val TextureLeaves_plain = FieldRefOld(TextureLeaves, "plain", Identifier)
|
||||
val TextureLeaves_fancy = FieldRefOld(TextureLeaves, "fancy", Identifier)
|
||||
val TextureLeaves_pollinatedPlain = FieldRefOld(TextureLeaves, "pollinatedPlain", Identifier)
|
||||
val TextureLeaves_pollinatedFancy = FieldRefOld(TextureLeaves, "pollinatedFancy", Identifier)
|
||||
|
||||
|
||||
val TileLeaves = ClassRefOld<Any>("forestry.arboriculture.tiles.TileLeaves")
|
||||
val TileLeaves_getLeaveSprite = MethodRefOld(TileLeaves, "getLeaveSprite", Identifier, boolean)
|
||||
val PropertyWoodType = ClassRefOld<Any>("forestry.arboriculture.blocks.PropertyWoodType")
|
||||
val IWoodType = ClassRefOld<Any>("forestry.api.arboriculture.IWoodType")
|
||||
val IWoodType_barkTex = MethodRefOld(IWoodType, "getBarkTexture", String)
|
||||
val IWoodType_heartTex = MethodRefOld(IWoodType, "getHeartTexture", String)
|
||||
|
||||
val PropertyTreeType = ClassRefOld<Any>("forestry.arboriculture.blocks.PropertyTreeType")
|
||||
val IAlleleTreeSpecies = ClassRefOld<Any>("forestry.api.arboriculture.IAlleleTreeSpecies")
|
||||
val ILeafSpriteProvider = ClassRefOld<Any>("forestry.api.arboriculture.ILeafSpriteProvider")
|
||||
val TreeDefinition = ClassRefOld<Any>("forestry.arboriculture.genetics.TreeDefinition")
|
||||
|
||||
val IAlleleTreeSpecies_getLeafSpriteProvider = MethodRefOld(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider)
|
||||
val TreeDefinition_species = FieldRefOld(TreeDefinition, "species", IAlleleTreeSpecies)
|
||||
val ILeafSpriteProvider_getSprite = MethodRefOld(ILeafSpriteProvider, "getSprite", Identifier, boolean, boolean)
|
||||
|
||||
object ForestryIntegration {
|
||||
init {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider<ModelLoader>, ModelRenderRegistry<LeafInfo> {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
var idToValue = emptyMap<Identifier, LeafInfo>()
|
||||
|
||||
override fun get(state: BlockState, world: BlockView, pos: BlockPos): LeafInfo? {
|
||||
// check variant property (used in decorative leaves)
|
||||
state.entries.entries.find {
|
||||
PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value)
|
||||
} ?.let {
|
||||
val species = it.value[TreeDefinition_species]!!
|
||||
val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]()
|
||||
val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, MinecraftClient.isFancyGraphicsEnabled())
|
||||
return idToValue[textureLoc]
|
||||
}
|
||||
|
||||
// extract leaf texture information from TileEntity
|
||||
val tile = world.getBlockEntity(pos) ?: return null
|
||||
if (!TileLeaves.isInstance(tile)) return null
|
||||
val textureLoc = tile[TileLeaves_getLeaveSprite](MinecraftClient.isFancyGraphicsEnabled())
|
||||
return idToValue[textureLoc]
|
||||
}
|
||||
|
||||
override fun setup(manager: ResourceManager, bakeryF: CompletableFuture<ModelLoader>, atlasFuture: AtlasFuture): StitchPhases {
|
||||
val futures = mutableMapOf<Identifier, CompletableFuture<LeafInfo>>()
|
||||
|
||||
return StitchPhases(
|
||||
discovery = bakeryF.thenRunAsync {
|
||||
val allLeaves = TextureLeaves_leafTextures.getStatic()
|
||||
allLeaves!!.entries.forEach { (type, leaves) ->
|
||||
log("base leaf type $type")
|
||||
leaves!!
|
||||
listOf(
|
||||
leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain],
|
||||
leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy]
|
||||
).forEach { textureLocation ->
|
||||
futures[textureLocation!!] = defaultRegisterLeaf(textureLocation, atlasFuture)
|
||||
}
|
||||
}
|
||||
},
|
||||
cleanup = atlasFuture.runAfter {
|
||||
idToValue = futures.mapValues { it.value.get() }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
|
||||
// respect class list to avoid triggering on fences, stairs, etc.
|
||||
if (!BetterFoliageMod.blockConfig.logBlocks.matchesClass(ctx.state.block)) return null
|
||||
|
||||
// find wood type property
|
||||
val woodType = ctx.state.entries.entries.find {
|
||||
PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value)
|
||||
}
|
||||
if (woodType != null) {
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}")
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
|
||||
|
||||
// get texture names for wood type
|
||||
val bark = woodType.value[IWoodType_barkTex]()
|
||||
val heart = woodType.value[IWoodType_heartTex]()
|
||||
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
|
||||
|
||||
val heartSprite = atlas.sprite(heart)
|
||||
val barkSprite = atlas.sprite(bark)
|
||||
return atlas.mapAfter {
|
||||
SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get()))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -4,9 +4,11 @@ import io.github.prospector.modmenu.api.ModMenuApi
|
||||
import me.shedaniel.clothconfig2.api.ConfigBuilder
|
||||
import me.zeroeightsix.fiber.JanksonSettings
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.resource.language.I18n
|
||||
import net.minecraft.text.LiteralText
|
||||
import java.util.function.Function
|
||||
|
||||
object ModMenu : ModMenuApi {
|
||||
@@ -15,13 +17,13 @@ object ModMenu : ModMenuApi {
|
||||
override fun getConfigScreenFactory() = Function { screen: Screen ->
|
||||
val builder = ConfigBuilder.create()
|
||||
.setParentScreen(screen)
|
||||
.setTitle(I18n.translate("betterfoliage.title"))
|
||||
.setTitle(LiteralText(I18n.translate("betterfoliage.title")))
|
||||
BetterFoliage.config.createClothNode(listOf("betterfoliage")).value.forEach { rootOption ->
|
||||
builder.getOrCreateCategory("main").addEntry(rootOption)
|
||||
builder.getOrCreateCategory(LiteralText("main")).addEntry(rootOption)
|
||||
}
|
||||
builder.savingRunnable = Runnable {
|
||||
JanksonSettings().serialize(BetterFoliage.config.fiberNode, BetterFoliage.configFile.outputStream(), false)
|
||||
BetterFoliage.modelReplacer.invalidate()
|
||||
BakeWrapperManager.invalidate()
|
||||
MinecraftClient.getInstance().worldRenderer.reload()
|
||||
}
|
||||
builder.build()
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package mods.betterfoliage.resource.model
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.BakedQuad_sprite
|
||||
import mods.betterfoliage.VertexFormat_offsets
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.findFirst
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import mods.betterfoliage.util.get
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormatElement
|
||||
@@ -14,8 +17,8 @@ import net.minecraft.client.render.VertexFormatElement.Type.UV
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BakedQuad
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.util.math.Direction
|
||||
import java.lang.Float
|
||||
import java.util.*
|
||||
|
||||
interface BakedModelConverter {
|
||||
@@ -24,12 +27,12 @@ interface BakedModelConverter {
|
||||
* @param model Input model
|
||||
* @param converter Converter to use for converting nested models.
|
||||
*/
|
||||
fun convert(model: BakedModel, converter: BakedModelConverter): BakedModel?
|
||||
fun convert(model: BakedModel): BakedModel?
|
||||
companion object {
|
||||
fun of(func: (BakedModel, BakedModelConverter)->BakedModel?) = object : BakedModelConverter {
|
||||
override fun convert(model: BakedModel, converter: BakedModelConverter) = func(model, converter)
|
||||
fun of(func: (BakedModel)->BakedModel?) = object : BakedModelConverter {
|
||||
override fun convert(model: BakedModel) = func(model)
|
||||
}
|
||||
val identity = of { model, _ -> model }
|
||||
val identity = of { model -> model }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,40 +42,40 @@ interface BakedModelConverter {
|
||||
*/
|
||||
fun List<BakedModelConverter>.convert(model: BakedModel) = object : BakedModelConverter {
|
||||
val converters = this@convert + BakedModelConverter.identity
|
||||
override fun convert(model: BakedModel, converter: BakedModelConverter) = converters.findFirst { it.convert(model, converter) }
|
||||
override fun convert(model: BakedModel) = converters.findFirst { it.convert(model) }
|
||||
}.let { converterStack ->
|
||||
// we are guaranteed a result here because of the identity converter
|
||||
converterStack.convert(model, converterStack)!!
|
||||
converterStack.convert(model)!!
|
||||
}
|
||||
|
||||
/** List of converters without meaningful configuration that should always be used */
|
||||
val COMMON_MESH_CONVERTERS = listOf(WrappedWeightedModel.converter)
|
||||
|
||||
/**
|
||||
* Convert [BakedModel] into one using fabric-rendering-api [Mesh] instead of the vanilla pipeline.
|
||||
* @param renderLayerOverride Use the given [BlockRenderLayer] for the [Mesh]
|
||||
* Convert [BasicBakedModel] into one using fabric-rendering-api [Mesh] instead of the vanilla pipeline.
|
||||
* @param blendMode Use the given [BlockRenderLayer] for the [Mesh]
|
||||
* instead of the one declared by the corresponding [Block]
|
||||
*/
|
||||
fun meshifyStandard(model: BakedModel, state: BlockState, renderLayerOverride: BlockRenderLayer? = null) =
|
||||
(COMMON_MESH_CONVERTERS + WrappedMeshModel.converter(state, renderLayerOverride = renderLayerOverride)).convert(model)
|
||||
fun meshifyStandard(model: BasicBakedModel, state: BlockState? = null, blendMode: BlendMode? = null) =
|
||||
WrappedMeshModel.converter(state, blendModeOverride = blendMode).convert(model)!!
|
||||
|
||||
fun meshifySolid(model: BasicBakedModel) = meshifyStandard(model, null, BlendMode.SOLID)
|
||||
fun meshifyCutoutMipped(model: BasicBakedModel) = meshifyStandard(model, null, BlendMode.CUTOUT_MIPPED)
|
||||
|
||||
/**
|
||||
* Convert a vanilla [BakedModel] into intermediate [Quad]s
|
||||
* Vertex normals not supported (yet)
|
||||
* Vertex data elements not aligned to 32 bit boundaries not supported
|
||||
*/
|
||||
fun unbakeQuads(model: BakedModel, state: BlockState, random: Random, unshade: Boolean): List<Quad> {
|
||||
fun unbakeQuads(model: BakedModel, state: BlockState?, random: Random, unshade: Boolean): List<Quad> {
|
||||
return (allDirections.toList() + null as Direction?).flatMap { face ->
|
||||
model.getQuads(state, face, random).mapIndexed { qIdx, bakedQuad ->
|
||||
var quad = Quad(Vertex(), Vertex(), Vertex(), Vertex(), face = face, colorIndex = bakedQuad.colorIndex, sprite = bakedQuad.sprite)
|
||||
var quad = Quad(Vertex(), Vertex(), Vertex(), Vertex(), face = face, colorIndex = bakedQuad.colorIndex, sprite = bakedQuad[BakedQuad_sprite])
|
||||
|
||||
val format = quadVertexFormat(bakedQuad)
|
||||
val stride = format.vertexSizeInteger
|
||||
format.getIntOffset(POSITION, FLOAT, 3)?.let { posOffset ->
|
||||
quad = quad.transformVI { vertex, vIdx -> vertex.copy(xyz = Double3(
|
||||
x = Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 0]).toDouble(),
|
||||
y = Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 1]).toDouble(),
|
||||
z = Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 2]).toDouble()
|
||||
x = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 0]).toDouble(),
|
||||
y = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 1]).toDouble(),
|
||||
z = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + posOffset + 2]).toDouble()
|
||||
)) }
|
||||
}
|
||||
format.getIntOffset(COLOR, UBYTE, 4)?.let { colorOffset ->
|
||||
@@ -82,8 +85,8 @@ fun unbakeQuads(model: BakedModel, state: BlockState, random: Random, unshade: B
|
||||
}
|
||||
format.getIntOffset(UV, FLOAT, 2, 0)?.let { uvOffset ->
|
||||
quad = quad.transformVI { vertex, vIdx -> vertex.copy(uv = UV(
|
||||
u = Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 0]).toDouble(),
|
||||
v = Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 1]).toDouble()
|
||||
u = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 0]).toDouble(),
|
||||
v = java.lang.Float.intBitsToFloat(bakedQuad.vertexData[vIdx * stride + uvOffset + 1]).toDouble()
|
||||
)) }
|
||||
}
|
||||
|
||||
@@ -97,8 +100,8 @@ fun unbakeQuads(model: BakedModel, state: BlockState, random: Random, unshade: B
|
||||
/** Get the byte offset of the [VertexFormatElement] matching the given criteria */
|
||||
fun VertexFormat.getByteOffset(type: VertexFormatElement.Type, format: VertexFormatElement.Format, count: Int, index: Int = 0): Int? {
|
||||
elements.forEachIndexed { idx, element ->
|
||||
if (element.type == type && element.format == format && element.count == count && element.index == index)
|
||||
return getElementOffset(idx)
|
||||
if (element == VertexFormatElement(index, format, type, count))
|
||||
return VertexFormat_offsets[this]!!.getInt(idx)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -111,4 +114,4 @@ fun VertexFormat.getIntOffset(type: VertexFormatElement.Type, format: VertexForm
|
||||
getByteOffset(type, format, count, index)?.let { if (it % 4 == 0) it / 4 else null }
|
||||
|
||||
/** Function to determine [VertexFormat] used by [BakedQuad] */
|
||||
var quadVertexFormat: (BakedQuad)->VertexFormat = { VertexFormats.POSITION_COLOR_UV_LMAP }
|
||||
var quadVertexFormat: (BakedQuad)->VertexFormat = { VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL }
|
||||
@@ -1,20 +1,20 @@
|
||||
package mods.betterfoliage.resource.model
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.minmax
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.client.texture.MissingSprite
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.Direction.*
|
||||
import java.lang.Math.max
|
||||
import java.lang.Math.min
|
||||
import java.util.Random
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Vertex UV coordinates
|
||||
@@ -166,13 +166,13 @@ fun Array<List<Quad>>.withOpposites() = map { it.withOpposites() }.toTypedArray(
|
||||
/**
|
||||
* Pour quad data into a fabric-renderer-api Mesh
|
||||
*/
|
||||
fun List<Quad>.build(layer: BlockRenderLayer, noDiffuse: Boolean = false, flatLighting: Boolean = false): Mesh {
|
||||
val renderer = RendererAccess.INSTANCE.renderer
|
||||
val material = renderer.materialFinder().blendMode(0, layer).disableAo(0, flatLighting).disableDiffuse(0, noDiffuse).find()
|
||||
fun List<Quad>.build(blendMode: BlendMode, noDiffuse: Boolean = false, flatLighting: Boolean = false): Mesh {
|
||||
val renderer = RendererAccess.INSTANCE.renderer!!
|
||||
val material = renderer.materialFinder().blendMode(0, blendMode).disableAo(0, flatLighting).disableDiffuse(0, noDiffuse).find()
|
||||
val builder = renderer.meshBuilder()
|
||||
builder.emitter.apply {
|
||||
forEach { quad ->
|
||||
val sprite = quad.sprite ?: Atlas.BLOCKS.atlas[MissingSprite.getMissingSpriteId()]!!
|
||||
val sprite = quad.sprite ?: Atlas.BLOCKS[MissingSprite.getMissingSpriteId()]!!
|
||||
quad.verts.forEachIndexed { idx, vertex ->
|
||||
pos(idx, (vertex.xyz + Double3(0.5, 0.5, 0.5)).asVec3f)
|
||||
sprite(idx, 0,
|
||||
@@ -190,7 +190,7 @@ fun List<Quad>.build(layer: BlockRenderLayer, noDiffuse: Boolean = false, flatLi
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun Array<List<Quad>>.build(layer: BlockRenderLayer, noDiffuse: Boolean = false, flatLighting: Boolean = false) = map { it.build(layer, noDiffuse, flatLighting) }.toTypedArray()
|
||||
fun Array<List<Quad>>.build(blendMode: BlendMode, noDiffuse: Boolean = false, flatLighting: Boolean = false) = map { it.build(blendMode, noDiffuse, flatLighting) }.toTypedArray()
|
||||
|
||||
/**
|
||||
* The model should be positioned so that (0,0,0) is the block center.
|
||||
@@ -1,4 +1,4 @@
|
||||
package mods.betterfoliage.resource.model
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.get
|
||||
@@ -21,8 +21,8 @@ class FixedSpriteSet(val sprites: List<Sprite>) : SpriteSet {
|
||||
override fun get(idx: Int) = sprites[idx % num]
|
||||
|
||||
constructor(atlas: Atlas, ids: List<Identifier>) : this(
|
||||
ids.mapNotNull { atlas.atlas[it] }.let { sprites ->
|
||||
if (sprites.isNotEmpty()) sprites else listOf(atlas.atlas[MissingSprite.getMissingSpriteId()]!!)
|
||||
ids.mapNotNull { atlas[it] }.let { sprites ->
|
||||
if (sprites.isNotEmpty()) sprites else listOf(atlas[MissingSprite.getMissingSpriteId()]!!)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -42,12 +42,17 @@ class SpriteDelegate(val atlas: Atlas, val idFunc: ()->Identifier) : ReadOnlyPro
|
||||
value?.let { return it }
|
||||
synchronized(this) {
|
||||
value?.let { return it }
|
||||
atlas.atlas[id!!]!!.let { value = it; return it }
|
||||
atlas[id!!]!!.let { value = it; return it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpriteSetDelegate(val atlas: Atlas, val idRegister: (Identifier)->Identifier = { it }, val idFunc: (Int)->Identifier) : ReadOnlyProperty<Any, SpriteSet>, ClientSpriteRegistryCallback {
|
||||
|
||||
class SpriteSetDelegate(
|
||||
val atlas: Atlas,
|
||||
val idRegister: (Identifier)->Identifier = { it },
|
||||
val idFunc: (Int)->Identifier
|
||||
) : ReadOnlyProperty<Any, SpriteSet>, ClientSpriteRegistryCallback {
|
||||
private var idList: List<Identifier> = emptyList()
|
||||
private var spriteSet: SpriteSet? = null
|
||||
init { ClientSpriteRegistryCallback.event(atlas.resourceId).register(this) }
|
||||
@@ -55,7 +60,7 @@ class SpriteSetDelegate(val atlas: Atlas, val idRegister: (Identifier)->Identifi
|
||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
||||
spriteSet = null
|
||||
val manager = MinecraftClient.getInstance().resourceManager
|
||||
idList = (0 until 16).map(idFunc).filter { manager.containsResource(atlas.wrap(it)) }.map(idRegister)
|
||||
idList = (0 until 16).map(idFunc).filter { manager.containsResource(atlas.file(it)) }.map(idRegister)
|
||||
idList.forEach { registry.register(it) }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package mods.betterfoliage.resource.model
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.*
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
@@ -34,22 +31,22 @@ fun tuftQuadSingle(size: Double, height: Double, flipU: Boolean) =
|
||||
verticalRectangle(x1 = -0.5 * size, z1 = 0.5 * size, x2 = 0.5 * size, z2 = -0.5 * size, yBottom = 0.5, yTop = 0.5 + height)
|
||||
.mirrorUV(flipU, false)
|
||||
|
||||
fun tuftModelSet(shapes: Array<TuftShapeKey>, overrideColor: Int?, spriteGetter: (Int)->Sprite) = shapes.mapIndexed { idx, shape ->
|
||||
fun tuftModelSet(shapes: Array<TuftShapeKey>, tintIndex: Int, spriteGetter: (Int)->Sprite) = shapes.mapIndexed { idx, shape ->
|
||||
listOf(
|
||||
tuftQuadSingle(shape.size, shape.height, shape.flipU1),
|
||||
tuftQuadSingle(shape.size, shape.height, shape.flipU2).rotate(rot(UP))
|
||||
).map { it.move(shape.offset) }
|
||||
.map { it.colorAndIndex(overrideColor) }
|
||||
.map { it.colorIndex(tintIndex) }
|
||||
.map { it.sprite(spriteGetter(idx)) }
|
||||
}.toTypedArray()
|
||||
|
||||
fun fullCubeTextured(spriteId: Identifier, overrideColor: Int?, scrambleUV: Boolean = true): Mesh {
|
||||
val sprite = Atlas.BLOCKS.atlas[spriteId]!!
|
||||
fun fullCubeTextured(spriteId: Identifier, tintIndex: Int, scrambleUV: Boolean = true): Mesh {
|
||||
val sprite = Atlas.BLOCKS[spriteId]!!
|
||||
return allDirections.map { faceQuad(it) }
|
||||
.map { if (!scrambleUV) it else it.rotateUV(randomI(max = 4)) }
|
||||
.map { it.sprite(sprite) }
|
||||
.map { it.colorAndIndex(overrideColor) }
|
||||
.build(BlockRenderLayer.SOLID)
|
||||
.map { it.colorIndex(tintIndex) }
|
||||
.build(BlendMode.SOLID, noDiffuse = true)
|
||||
}
|
||||
|
||||
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Array<List<Quad>> {
|
||||
@@ -64,11 +61,20 @@ fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Ar
|
||||
}
|
||||
}
|
||||
|
||||
fun crossModelsTextured(leafBase: Array<List<Quad>>, overrideColor: Int?, scrambleUV: Boolean, spriteGetter: (Int)->Sprite) = leafBase.map { leaf ->
|
||||
leaf.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
||||
.map { it.colorAndIndex(overrideColor) }
|
||||
.mapIndexed { idx, quad -> quad.sprite(spriteGetter(idx)) }
|
||||
.withOpposites().build(CUTOUT_MIPPED)
|
||||
fun crossModelSingle(base: List<Quad>, sprite: Sprite, tintIndex: Int,scrambleUV: Boolean) =
|
||||
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
||||
.map { it.colorIndex(tintIndex) }
|
||||
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
||||
.withOpposites()
|
||||
.build(BlendMode.CUTOUT_MIPPED)
|
||||
|
||||
fun crossModelsTextured(
|
||||
leafBase: Array<List<Quad>>,
|
||||
tintIndex: Int,
|
||||
scrambleUV: Boolean,
|
||||
spriteGetter: (Int) -> Identifier
|
||||
) = leafBase.mapIndexed { idx, leaf ->
|
||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
||||
}.toTypedArray()
|
||||
|
||||
fun Array<List<Quad>>.buildTufts() = withOpposites().build(CUTOUT_MIPPED)
|
||||
fun Array<List<Quad>>.buildTufts() = withOpposites().build(BlendMode.CUTOUT_MIPPED)
|
||||
87
src/main/kotlin/mods/betterfoliage/model/VanillaWrappers.kt
Normal file
87
src/main/kotlin/mods/betterfoliage/model/VanillaWrappers.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
package mods.betterfoliage.model
|
||||
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.collection.WeightedPicker
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
abstract class ModelWrapKey : ModelBakingKey, HasLogger() {
|
||||
override fun bake(ctx: ModelBakingContext): BakedModel? {
|
||||
val baseModel = super.bake(ctx)
|
||||
if (baseModel is BasicBakedModel)
|
||||
return bake(ctx, baseModel)
|
||||
else
|
||||
return baseModel
|
||||
}
|
||||
|
||||
abstract fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel
|
||||
}
|
||||
|
||||
abstract class WrappedBakedModel(val wrapped: BakedModel) : BakedModel by wrapped, FabricBakedModel {
|
||||
override fun isVanillaAdapter() = false
|
||||
|
||||
override fun emitItemQuads(stack: ItemStack, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(wrapped as FabricBakedModel).emitItemQuads(stack, randomSupplier, context)
|
||||
}
|
||||
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedMeshModel(wrapped: BasicBakedModel, val mesh: Mesh) : WrappedBakedModel(wrapped) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
context.meshConsumer().accept(mesh)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Converter for [BasicBakedModel] instances.
|
||||
* @param state [BlockState] to use when querying [BakedModel]
|
||||
* @param unshade undo vanilla diffuse lighting when unbaking the [BakedModel]
|
||||
* @param noDiffuse disable diffuse lighting when baking the [Mesh]
|
||||
* @param blendModeOverride [BlockRenderLayer] to use instead of the one declared by the corresponding [Block]
|
||||
*/
|
||||
fun converter(state: BlockState?, unshade: Boolean = false, noDiffuse: Boolean = true, blendModeOverride: BlendMode? = null) = BakedModelConverter.of { model ->
|
||||
if (model is BasicBakedModel) {
|
||||
val mesh = unbakeQuads(model, state, Random(42L), unshade).build(
|
||||
blendMode = blendModeOverride ?: BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)),
|
||||
noDiffuse = noDiffuse,
|
||||
flatLighting = !model.useAmbientOcclusion()
|
||||
)
|
||||
WrappedMeshModel(model, mesh)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WeightedModelWrapper(
|
||||
val models: List<WeightedModel>, baseModel: BakedModel
|
||||
): WrappedBakedModel(baseModel), FabricBakedModel {
|
||||
|
||||
class WeightedModel(val model: BakedModel, val weight: Int) : WeightedPicker.Entry(weight)
|
||||
fun getModel(random: Random) = WeightedPicker.getRandom(random, models).model
|
||||
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(getModel(randomSupplier.get()) as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
}
|
||||
|
||||
fun getUnderlyingModel(model: BakedModel, random: Random): BakedModel = when(model) {
|
||||
is WeightedModelWrapper -> getUnderlyingModel(model.getModel(random), random)
|
||||
is WrappedBakedModel -> model.wrapped
|
||||
else -> model
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package mods.betterfoliage.render
|
||||
|
||||
import mods.betterfoliage.util.Double3
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.particle.ParticleTextureSheet
|
||||
import net.minecraft.client.particle.SpriteBillboardParticle
|
||||
import net.minecraft.client.render.BufferBuilder
|
||||
import net.minecraft.client.render.Camera
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.world.World
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
|
||||
abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) : SpriteBillboardParticle(world, x, y, z) {
|
||||
|
||||
companion object {
|
||||
// @JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
||||
// @JvmStatic val cos = Array(64) { idx -> Math.cos(PI2 / 64.0 * idx) }
|
||||
}
|
||||
|
||||
val billboardRot = Pair(Double3.zero, Double3.zero)
|
||||
val currentPos = Double3.zero
|
||||
val prevPos = Double3.zero
|
||||
val velocity = Double3.zero
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
currentPos.setTo(x, y, z)
|
||||
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
||||
velocity.setTo(velocityX, velocityY, velocityZ)
|
||||
update()
|
||||
x = currentPos.x; y = currentPos.y; z = currentPos.z;
|
||||
velocityX = velocity.x; velocityY = velocity.y; velocityZ = velocity.z;
|
||||
}
|
||||
|
||||
/** Render the particle. */
|
||||
abstract fun render(worldRenderer: BufferBuilder, partialTickTime: Float)
|
||||
|
||||
/** Update particle on world tick. */
|
||||
abstract fun update()
|
||||
|
||||
/** True if the particle is renderable. */
|
||||
abstract val isValid: Boolean
|
||||
|
||||
/** Add the particle to the effect renderer if it is valid. */
|
||||
fun addIfValid() { if (isValid) MinecraftClient.getInstance().particleManager.addParticle(this) }
|
||||
|
||||
override fun buildGeometry(buffer: BufferBuilder, camera: Camera, tickDelta: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
|
||||
billboardRot.first.setTo(rotX + rotXY, rotZ, rotYZ + rotXZ)
|
||||
billboardRot.second.setTo(rotX - rotXY, -rotZ, rotYZ - rotXZ)
|
||||
render(buffer, tickDelta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a particle quad.
|
||||
*
|
||||
* @param[tessellator] the [Tessellator] instance to use
|
||||
* @param[partialTickTime] partial tick time
|
||||
* @param[currentPos] render position
|
||||
* @param[prevPos] previous tick position for interpolation
|
||||
* @param[size] particle size
|
||||
* @param[rotation] viewpoint-dependent particle rotation (64 steps)
|
||||
* @param[sprite] particle texture
|
||||
* @param[isMirrored] mirror particle texture along V-axis
|
||||
* @param[alpha] aplha blending
|
||||
*/
|
||||
fun renderParticleQuad(worldRenderer: BufferBuilder,
|
||||
partialTickTime: Float,
|
||||
currentPos: Double3 = this.currentPos,
|
||||
prevPos: Double3 = this.prevPos,
|
||||
size: Double = scale.toDouble(),
|
||||
rotation: Double = 0.0,
|
||||
sprite: Sprite = this.sprite,
|
||||
isMirrored: Boolean = false,
|
||||
alpha: Float = this.colorAlpha) {
|
||||
|
||||
val minU = (if (isMirrored) sprite.minU else sprite.maxU).toDouble()
|
||||
val maxU = (if (isMirrored) sprite.maxU else sprite.minU).toDouble()
|
||||
val minV = sprite.minV.toDouble()
|
||||
val maxV = sprite.maxV.toDouble()
|
||||
|
||||
val center = currentPos.copy().sub(prevPos).mul(partialTickTime.toDouble()).add(prevPos).sub(cameraX, cameraY, cameraZ)
|
||||
|
||||
val cosRotation = cos(rotation); val sinRotation = sin(rotation)
|
||||
val v1 = Double3.weight(billboardRot.first, cosRotation * size, billboardRot.second, sinRotation * size)
|
||||
val v2 = Double3.weight(billboardRot.first, -sinRotation * size, billboardRot.second, cosRotation * size)
|
||||
|
||||
val renderBrightness = this.getColorMultiplier(partialTickTime)
|
||||
val brHigh = renderBrightness shr 16 and 65535
|
||||
val brLow = renderBrightness and 65535
|
||||
|
||||
worldRenderer
|
||||
.vertex(center.x - v1.x, center.y - v1.y, center.z - v1.z)
|
||||
.texture(maxU, maxV)
|
||||
.color(colorRed, colorGreen, colorBlue, alpha)
|
||||
.texture(brHigh, brLow)
|
||||
.next()
|
||||
|
||||
worldRenderer
|
||||
.vertex(center.x - v2.x, center.y - v2.y, center.z - v2.z)
|
||||
.texture(maxU, minV)
|
||||
.color(colorRed, colorGreen, colorBlue, alpha)
|
||||
.texture(brHigh, brLow)
|
||||
.next()
|
||||
|
||||
worldRenderer
|
||||
.vertex(center.x + v1.x, center.y + v1.y, center.z + v1.z)
|
||||
.texture(minU, minV)
|
||||
.color(colorRed, colorGreen, colorBlue, alpha)
|
||||
.texture(brHigh, brLow)
|
||||
.next()
|
||||
|
||||
worldRenderer
|
||||
.vertex(center.x + v2.x, center.y + v2.y, center.z + v2.z)
|
||||
.texture(minU, maxV)
|
||||
.color(colorRed, colorGreen, colorBlue, alpha)
|
||||
.texture(brHigh, brLow)
|
||||
.next()
|
||||
}
|
||||
|
||||
override fun getType() = ParticleTextureSheet.PARTICLE_SHEET_OPAQUE
|
||||
|
||||
fun setColor(color: Int) {
|
||||
colorBlue = (color and 255) / 256.0f
|
||||
colorGreen = ((color shr 8) and 255) / 256.0f
|
||||
colorRed = ((color shr 16) and 255) / 256.0f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package mods.betterfoliage.render
|
||||
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT)
|
||||
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
|
||||
|
||||
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
|
||||
|
||||
val SNOW_MATERIALS = listOf(Material.SNOW, Material.SNOW_BLOCK)
|
||||
@@ -1,48 +0,0 @@
|
||||
package mods.betterfoliage.render
|
||||
|
||||
import mods.betterfoliage.*
|
||||
import mods.betterfoliage.util.ThreadLocalDelegate
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.model.BakedQuad
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
/**
|
||||
* Integration for OptiFine custom block colors.
|
||||
*/
|
||||
/*
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
object OptifineCustomColors {
|
||||
|
||||
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
|
||||
|
||||
init {
|
||||
BetterFoliage.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }")
|
||||
}
|
||||
|
||||
val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }
|
||||
val fakeQuad = BakedQuad(IntArray(0), 1, UP, null)
|
||||
|
||||
fun getBlockColor(ctx: CombinedContext): Int {
|
||||
val ofColor = if (isColorAvailable && MinecraftClient.getInstance().options.reflectDeclaredField<Boolean>("ofCustomColors") == true) {
|
||||
renderEnv.reset(ctx.state, ctx.pos)
|
||||
CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
|
||||
} else null
|
||||
return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor
|
||||
}
|
||||
}
|
||||
|
||||
class OptifineRenderEnv {
|
||||
val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let {
|
||||
it.isAccessible = true
|
||||
it.newInstance(null, null)
|
||||
}
|
||||
|
||||
fun reset(state: BlockState, pos: BlockPos) {
|
||||
RenderEnv.reset.invoke(wrapped, state, pos)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -1,64 +1,54 @@
|
||||
package mods.betterfoliage.render
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.util.get
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockRenderType.MODEL
|
||||
import mods.betterfoliage.render.lighting.getBufferBuilder
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.getAllMethods
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import net.minecraft.client.render.BufferBuilder
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
|
||||
/**
|
||||
* Integration for ShadersMod.
|
||||
*/
|
||||
/*
|
||||
object ShadersModIntegration {
|
||||
object ShadersModIntegration : HasLogger() {
|
||||
|
||||
@JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop)
|
||||
val BufferBuilder_SVertexBuilder = BufferBuilder::class.java.fields.find { it.name == "sVertexBuilder" }
|
||||
val SVertexBuilder_pushState = getAllMethods("net.optifine.shaders.SVertexBuilder", "pushEntity").find { it.parameterCount == 1 }
|
||||
val SVertexBuilder_popState = getAllMethods("net.optifine.shaders.SVertexBuilder", "popEntity").find { it.parameterCount == 0 }
|
||||
val BlockAliases_getAliasBlockId = getAllMethods("net.optifine.shaders.BlockAliases", "getAliasBlockId").firstOrNull()
|
||||
|
||||
@JvmStatic val isAvailable =
|
||||
listOf(BufferBuilder_SVertexBuilder).all { it != null } &&
|
||||
listOf(SVertexBuilder_pushState, SVertexBuilder_popState, BlockAliases_getAliasBlockId).all { it != null }
|
||||
|
||||
val defaultLeaves = Blocks.OAK_LEAVES.defaultState
|
||||
val defaultGrass = Blocks.TALL_GRASS.defaultState
|
||||
|
||||
/**
|
||||
* Called from transformed ShadersMod code.
|
||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
||||
*/
|
||||
@JvmStatic fun getBlockStateOverride(state: BlockState, world: ExtendedBlockView, pos: BlockPos): BlockState {
|
||||
// if (LeafRegistry[state, world, pos] != null) return defaultLeaves
|
||||
if (BetterFoliage.blockConfig.crops.matchesClass(state.block)) return defaultGrass
|
||||
return state
|
||||
}
|
||||
|
||||
init {
|
||||
BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
|
||||
logger.info("[BetterFoliage] ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
|
||||
}
|
||||
|
||||
inline fun renderAs(ctx: CombinedContext, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(ctx, ctx.state, renderType, enabled, func)
|
||||
|
||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
||||
inline fun renderAs(ctx: CombinedContext, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) {
|
||||
inline fun renderAs(ctx: RenderContext, state: BlockState, layer: RenderLayer, enabled: Boolean = true, func: ()->Unit) {
|
||||
if (isAvailable && enabled) {
|
||||
val buffer = ctx.renderCtx.renderBuffer
|
||||
val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder]
|
||||
SVertexBuilder.pushState.invoke(sVertexBuilder!!, ctx.state, ctx.pos, ctx.world, buffer)
|
||||
val sVertexBuilder = BufferBuilder_SVertexBuilder!!.get(ctx.getBufferBuilder(layer))
|
||||
val aliasBlockId = BlockAliases_getAliasBlockId!!.invoke(null, state)
|
||||
SVertexBuilder_pushState!!.invoke(sVertexBuilder, aliasBlockId)
|
||||
func()
|
||||
SVertexBuilder.pop.invoke(sVertexBuilder)
|
||||
SVertexBuilder_popState!!.invoke(sVertexBuilder)
|
||||
} else {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
|
||||
inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(ctx, defaultGrass, MODEL, enabled, func)
|
||||
inline fun grass(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(ctx, defaultGrass, RenderLayer.getCutoutMipped(), enabled, func)
|
||||
|
||||
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
|
||||
inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(ctx, defaultLeaves, MODEL, enabled, func)
|
||||
inline fun leaves(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(ctx, defaultLeaves, RenderLayer.getCutoutMipped(), enabled, func)
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -1,52 +1,71 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.CACTUS_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteDelegate
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
import mods.betterfoliage.model.crossModelsRaw
|
||||
import mods.betterfoliage.model.crossModelsTextured
|
||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.render.lighting.roundLeafLighting
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.*
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.horizontalDirections
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.CactusBlock
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.DOWN
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
|
||||
interface CactusKey : BlockRenderKey {
|
||||
val cactusTop: Identifier
|
||||
val cactusBottom: Identifier
|
||||
val cactusSide: Identifier
|
||||
}
|
||||
|
||||
object StandardCactusDiscovery : ConfigurableModelDiscovery() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
|
||||
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
|
||||
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
|
||||
val sprites = textures.map { Identifier(it) }
|
||||
return CactusModel.Key(sprites[0], sprites[1], sprites[2])
|
||||
object StandardCactusDiscovery : AbstractModelDiscovery() {
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
if (model is JsonUnbakedModel && ctx.blockState.block in CACTUS_BLOCKS) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardCactusKey)
|
||||
ctx.sprites.add(StandardCactusModel.cactusCrossSprite)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
class CactusModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
|
||||
object StandardCactusKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardCactusModel(meshifyCutoutMipped(wrapped))
|
||||
}
|
||||
|
||||
class StandardCactusModel(wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
|
||||
|
||||
val crossModels by cactusCrossModels.delegate(key)
|
||||
val armModels by cactusArmModels.delegate(key)
|
||||
val armLighting = horizontalDirections.map { grassTuftLighting(it) }
|
||||
val crossLighting = roundLeafLighting()
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.cactus.enabled) return
|
||||
|
||||
@@ -54,36 +73,26 @@ class CactusModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped
|
||||
val armSide = random.nextInt() and 3
|
||||
|
||||
context.withLighting(armLighting[armSide]) {
|
||||
it.accept(armModels[armSide][random])
|
||||
it.accept(cactusArmModels[armSide][random])
|
||||
}
|
||||
context.withLighting(crossLighting) {
|
||||
it.accept(crossModels[random])
|
||||
it.accept(cactusCrossModels[random])
|
||||
}
|
||||
}
|
||||
|
||||
data class Key(
|
||||
override val cactusTop: Identifier,
|
||||
override val cactusBottom: Identifier,
|
||||
override val cactusSide: Identifier
|
||||
) : CactusKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = CactusModel(this, meshifyStandard(model, state))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val cactusCrossSprite by SpriteDelegate(Atlas.BLOCKS) {
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus")
|
||||
}
|
||||
val cactusCrossSprite = Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus")
|
||||
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx")
|
||||
}
|
||||
val cactusArmModels = LazyMap(BetterFoliage.modelReplacer) { key: CactusKey ->
|
||||
val cactusArmModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = BetterFoliage.config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
|
||||
val models = tuftModelSet(shapes, Color.white.asInt) { cactusArmSprites[randomI()] }
|
||||
horizontalDirections.map { side ->
|
||||
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
|
||||
}.toTypedArray()
|
||||
}
|
||||
val cactusCrossModels = LazyMap(BetterFoliage.modelReplacer) { key: CactusKey ->
|
||||
val cactusCrossModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val models = BetterFoliage.config.cactus.let { config ->
|
||||
crossModelsRaw(64, config.size, 0.0, 0.0)
|
||||
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
|
||||
|
||||
@@ -2,42 +2,72 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.render.DIRT_BLOCKS
|
||||
import mods.betterfoliage.render.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.DIRT_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.build
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.model.withOpposites
|
||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.model.getUnderlyingModel
|
||||
import mods.betterfoliage.model.meshifySolid
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.render.lighting.reedLighting
|
||||
import mods.betterfoliage.render.lighting.renderMasquerade
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.BlockRenderKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.generated.CenteredSprite
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
object DirtKey : BlockRenderKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = DirtModel(meshifyStandard(model, state))
|
||||
object StandardDirtDiscovery : AbstractModelDiscovery() {
|
||||
fun canRenderInLayer(layer: RenderLayer) = when {
|
||||
!BetterFoliage.config.enabled -> layer == RenderLayer.getSolid()
|
||||
(!BetterFoliage.config.connectedGrass.enabled &&
|
||||
!BetterFoliage.config.algae.enabled &&
|
||||
!BetterFoliage.config.reed.enabled
|
||||
) -> layer == RenderLayer.getSolid()
|
||||
else -> layer == RenderLayer.getCutoutMipped()
|
||||
}
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in DIRT_BLOCKS) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardDirtKey)
|
||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
object DirtDiscovery : ModelDiscoveryBase() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
|
||||
if (ctx.state.block in DIRT_BLOCKS) DirtKey else null
|
||||
object StandardDirtKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = DirtModel(meshifySolid(wrapped))
|
||||
}
|
||||
|
||||
class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
@@ -45,33 +75,43 @@ class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
val algaeLighting = grassTuftLighting(UP)
|
||||
val reedLighting = reedLighting()
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(
|
||||
blockView: BlockRenderView,
|
||||
state: BlockState,
|
||||
pos: BlockPos,
|
||||
randomSupplier: Supplier<Random>,
|
||||
context: RenderContext
|
||||
) {
|
||||
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
|
||||
val ctx = BasicBlockCtx(blockView, pos)
|
||||
val stateUp = ctx.offset(UP).state
|
||||
val keyUp = BetterFoliage.modelReplacer[stateUp]
|
||||
val isGrassUp = stateUp in BetterFoliage.blockTypes.grass
|
||||
|
||||
val isWater = stateUp.material == Material.WATER
|
||||
val isDeepWater = isWater && ctx.offset(Int3(2 to UP)).state.material == Material.WATER
|
||||
val isShallowWater = isWater && ctx.offset(Int3(2 to UP)).state.isAir
|
||||
val isSaltWater = isWater && ctx.biome.category in SALTWATER_BIOMES
|
||||
val isSaltWater = isWater && ctx.biome?.category in SALTWATER_BIOMES
|
||||
|
||||
if (BetterFoliage.config.connectedGrass.enabled && keyUp is GrassKey) {
|
||||
val grassBaseModel = (ctx.model(UP) as WrappedBakedModel).wrapped
|
||||
val random = randomSupplier.get()
|
||||
if (BetterFoliage.config.connectedGrass.enabled && isGrassUp) {
|
||||
val grassBaseModel = getUnderlyingModel(ctx.model(UP), random)
|
||||
context.renderMasquerade(grassBaseModel, blockView, stateUp, pos, randomSupplier, context)
|
||||
} else {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
|
||||
val random = randomSupplier.get()
|
||||
if (BetterFoliage.config.algae.enabled(random) && isDeepWater) {
|
||||
context.withLighting(algaeLighting) {
|
||||
it.accept(algaeModels[random])
|
||||
ShadersModIntegration.grass(context, BetterFoliage.config.algae.shaderWind) {
|
||||
context.withLighting(algaeLighting) {
|
||||
it.accept(algaeModels[random])
|
||||
}
|
||||
}
|
||||
} else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) {
|
||||
context.withLighting(reedLighting) {
|
||||
it.accept(reedModels[random])
|
||||
ShadersModIntegration.grass(context, BetterFoliage.config.reed.shaderWind) {
|
||||
context.withLighting(reedLighting) {
|
||||
it.accept(reedModels[random])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,18 +124,19 @@ class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
idFunc = { idx -> Identifier(BetterFoliage.MOD_ID, "blocks/better_reed_$idx") },
|
||||
idRegister = { id -> CenteredSprite(id, aspectHeight = 2).register(BetterFoliage.generatedPack) }
|
||||
)
|
||||
val algaeModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val shapes = BetterFoliage.config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
val algaeModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes =
|
||||
BetterFoliage.config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, Color.white.asInt) { algaeSprites[randomI()] }
|
||||
.withOpposites()
|
||||
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
|
||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
||||
|
||||
}
|
||||
val reedModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val reedModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = BetterFoliage.config.reed.let { tuftShapeSet(2.0, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, Color.white.asInt) { reedSprites[randomI()] }
|
||||
.withOpposites()
|
||||
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
|
||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,53 +2,54 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.render.SNOW_MATERIALS
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.SNOW_MATERIALS
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.*
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.tag.BlockTags
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.*
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
|
||||
interface GrassKey : BlockRenderKey {
|
||||
val grassTopTexture: Identifier
|
||||
|
||||
/**
|
||||
* Color to use for Short Grass rendering instead of the biome color.
|
||||
*
|
||||
* Value is null if the texture is mostly grey (the saturation of its average color is under a configurable limit),
|
||||
* the average color of the texture otherwise.
|
||||
*/
|
||||
val overrideColor: Int?
|
||||
}
|
||||
|
||||
object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.grassBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.grassModels.modelList
|
||||
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
|
||||
val grassId = Identifier(textures[0])
|
||||
log(" block state $state")
|
||||
log(" texture $grassId")
|
||||
return GrassBlockModel.Key(grassId, getAndLogColorOverride(grassId, Atlas.BLOCKS, BetterFoliage.config.shortGrass.saturationThreshold))
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
||||
ctx.addReplacement(StandardGrassKey(textureMatch[0], null))
|
||||
BetterFoliage.blockTypes.grass.add(ctx.blockState)
|
||||
}
|
||||
}
|
||||
|
||||
class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
|
||||
data class StandardGrassKey(
|
||||
val grassLocation: Identifier,
|
||||
val overrideColor: Color?
|
||||
) : ModelWrapKey() {
|
||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel {
|
||||
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
||||
logColorOverride(detailLogger, BetterFoliage.config.shortGrass.saturationThreshold, hsb)
|
||||
hsb.colorOverride(BetterFoliage.config.shortGrass.saturationThreshold)
|
||||
}
|
||||
return StandardGrassModel(meshifyCutoutMipped(wrapped), this.copy(overrideColor = grassSpriteColor))
|
||||
}
|
||||
}
|
||||
|
||||
class StandardGrassModel(wrapped: BakedModel, val key: StandardGrassKey) : WrappedBakedModel(wrapped) {
|
||||
|
||||
val tuftNormal by grassTuftMeshesNormal.delegate(key)
|
||||
val tuftSnowed by grassTuftMeshesSnowed.delegate(key)
|
||||
@@ -56,7 +57,7 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra
|
||||
|
||||
val tuftLighting = grassTuftLighting(UP)
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
|
||||
val ctx = BasicBlockCtx(blockView, pos)
|
||||
@@ -65,10 +66,8 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra
|
||||
|
||||
val isSnowed = stateAbove.material in SNOW_MATERIALS
|
||||
val connected = BetterFoliage.config.connectedGrass.enabled &&
|
||||
(!isSnowed || BetterFoliage.config.connectedGrass.snowEnabled) && (
|
||||
BlockTags.DIRT_LIKE.contains(stateBelow.block) ||
|
||||
BetterFoliage.modelReplacer.getTyped<GrassKey>(stateBelow) != null
|
||||
)
|
||||
(!isSnowed || BetterFoliage.config.connectedGrass.snowEnabled) &&
|
||||
(stateBelow in BetterFoliage.blockTypes.dirt || stateBelow in BetterFoliage.blockTypes.grass)
|
||||
|
||||
val random = randomSupplier.get()
|
||||
if (connected) {
|
||||
@@ -78,19 +77,14 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra
|
||||
}
|
||||
|
||||
if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) {
|
||||
context.withLighting(tuftLighting) {
|
||||
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random])
|
||||
ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
|
||||
context.withLighting(tuftLighting) {
|
||||
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Key(
|
||||
override val grassTopTexture: Identifier,
|
||||
override val overrideColor: Int?
|
||||
) : GrassKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = GrassBlockModel(this, meshifyStandard(model, state))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val grassTuftSpritesNormal by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
|
||||
@@ -98,24 +92,24 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra
|
||||
val grassTuftSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
|
||||
}
|
||||
val grassTuftShapes = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
|
||||
val grassTuftShapes = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
}
|
||||
val grassTuftMeshesNormal = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
|
||||
tuftModelSet(grassTuftShapes[key], key.overrideColor) { idx -> grassTuftSpritesNormal[randomI()] }
|
||||
val grassTuftMeshesNormal = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes[key], key.tintIndex) { idx -> grassTuftSpritesNormal[randomI()] }
|
||||
.withOpposites()
|
||||
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
|
||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
||||
}
|
||||
val grassTuftMeshesSnowed = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
|
||||
val grassTuftMeshesSnowed = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
tuftModelSet(grassTuftShapes[key], Color.white.asInt) { idx -> grassTuftSpritesSnowed[randomI()] }
|
||||
.withOpposites()
|
||||
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
|
||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
||||
}
|
||||
val grassFullBlockMeshes = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
|
||||
Array(64) { fullCubeTextured(key.grassTopTexture, key.overrideColor) }
|
||||
val grassFullBlockMeshes = LazyMap(BakeWrapperManager) { key: StandardGrassKey ->
|
||||
Array(64) { fullCubeTextured(key.grassLocation, key.tintIndex) }
|
||||
}
|
||||
val snowFullBlockMeshes by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
Array(64) { fullCubeTextured(Identifier("block/snow"), Color.white.asInt) }
|
||||
val snowFullBlockMeshes by LazyInvalidatable(BakeWrapperManager) {
|
||||
Array(64) { fullCubeTextured(Identifier("block/snow"), -1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,115 +2,121 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.BasicBlockCtx
|
||||
import mods.betterfoliage.render.SNOW_MATERIALS
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.SNOW_MATERIALS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.crossModelsRaw
|
||||
import mods.betterfoliage.model.crossModelsTextured
|
||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.render.lighting.roundLeafLighting
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.*
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||
import mods.betterfoliage.resource.generated.GeneratedLeafSprite
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyMap
|
||||
import mods.betterfoliage.util.averageColor
|
||||
import mods.betterfoliage.util.colorOverride
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.logColorOverride
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.*
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import org.apache.logging.log4j.Level
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
interface LeafKey : BlockRenderKey {
|
||||
val roundLeafTexture: Identifier
|
||||
interface LeafBlockModel {
|
||||
val key: LeafParticleKey
|
||||
}
|
||||
|
||||
/** Type of the leaf block (configurable by user). */
|
||||
interface LeafParticleKey {
|
||||
val leafType: String
|
||||
|
||||
/** Average color of the round leaf texture. */
|
||||
val overrideColor: Int?
|
||||
val overrideColor: Color?
|
||||
}
|
||||
|
||||
object StandardLeafDiscovery : ConfigurableModelDiscovery() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.leafBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.leafModels.modelList
|
||||
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>) =
|
||||
defaultRegisterLeaf(Identifier(textures[0]), atlas)
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
||||
val leafType = LeafParticleRegistry.typeMappings.getType(textureMatch[0]) ?: "default"
|
||||
val generated = GeneratedLeafSprite(textureMatch[0], leafType)
|
||||
.register(BetterFoliage.generatedPack)
|
||||
.apply { ctx.sprites.add(this) }
|
||||
|
||||
}
|
||||
|
||||
fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: Consumer<Identifier>): BlockRenderKey? {
|
||||
val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default"
|
||||
val leafId = GeneratedLeafSprite(sprite, leafType).register(BetterFoliage.generatedPack)
|
||||
atlas.accept(leafId)
|
||||
|
||||
log(" leaf texture $sprite")
|
||||
log(" particle $leafType")
|
||||
|
||||
return NormalLeavesModel.Key(
|
||||
leafId, leafType,
|
||||
getAndLogColorOverride(leafId, Atlas.BLOCKS, BetterFoliage.config.shortGrass.saturationThreshold)
|
||||
)
|
||||
}
|
||||
|
||||
fun HasLogger.getAndLogColorOverride(sprite: Identifier, atlas: Atlas, threshold: Double): Int? {
|
||||
val hsb = resourceManager.averageImageColorHSB(sprite, atlas)
|
||||
return if (hsb.saturation >= threshold) {
|
||||
log(" brightness ${hsb.brightness}")
|
||||
log(" saturation ${hsb.saturation} >= ${threshold}, using texture color")
|
||||
hsb.copy(brightness = 0.9f.coerceAtMost(hsb.brightness * 2.0f)).asColor
|
||||
} else {
|
||||
log(" saturation ${hsb.saturation} < ${threshold}, using block color")
|
||||
null
|
||||
detailLogger.log(Level.INFO, " particle $leafType")
|
||||
ctx.addReplacement(StandardLeafKey(generated, leafType, null))
|
||||
}
|
||||
}
|
||||
|
||||
class NormalLeavesModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
|
||||
data class StandardLeafKey(
|
||||
val roundLeafTexture: Identifier,
|
||||
override val leafType: String,
|
||||
override val overrideColor: Color?
|
||||
) : ModelWrapKey(), LeafParticleKey {
|
||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel): BakedModel {
|
||||
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
||||
logColorOverride(detailLogger, BetterFoliage.config.leaves.saturationThreshold, hsb)
|
||||
hsb.colorOverride(BetterFoliage.config.leaves.saturationThreshold)
|
||||
}
|
||||
return StandardLeafModel(meshifyCutoutMipped(wrapped), this.copy(overrideColor = leafSpriteColor))
|
||||
}
|
||||
}
|
||||
|
||||
class StandardLeafModel(
|
||||
wrapped: BakedModel,
|
||||
override val key: StandardLeafKey
|
||||
) : WrappedBakedModel(wrapped), LeafBlockModel {
|
||||
|
||||
val leafNormal by leafModelsNormal.delegate(key)
|
||||
val leafSnowed by leafModelsSnowed.delegate(key)
|
||||
val leafLighting = roundLeafLighting()
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
ShadersModIntegration.leaves(context, BetterFoliage.config.leaves.shaderWind) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return
|
||||
|
||||
val ctx = BasicBlockCtx(blockView, pos)
|
||||
val stateAbove = ctx.state(UP)
|
||||
val isSnowed = stateAbove.material in SNOW_MATERIALS
|
||||
val ctx = BasicBlockCtx(blockView, pos)
|
||||
val stateAbove = ctx.state(UP)
|
||||
val isSnowed = stateAbove.material in SNOW_MATERIALS
|
||||
|
||||
val random = randomSupplier.get()
|
||||
context.withLighting(leafLighting) {
|
||||
it.accept(leafNormal[random])
|
||||
if (isSnowed) it.accept(leafSnowed[random])
|
||||
val random = randomSupplier.get()
|
||||
context.withLighting(leafLighting) {
|
||||
it.accept(leafNormal[random])
|
||||
if (isSnowed) it.accept(leafSnowed[random])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Key(
|
||||
override val roundLeafTexture: Identifier,
|
||||
override val leafType: String,
|
||||
override val overrideColor: Int?
|
||||
) : LeafKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = NormalLeavesModel(this, meshifyStandard(model, state, renderLayerOverride = CUTOUT_MIPPED))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val leafSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_leaves_snowed_$idx")
|
||||
}
|
||||
val leafModelsBase = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
|
||||
val leafModelsBase = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
BetterFoliage.config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
||||
}
|
||||
val leafModelsNormal = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], key.overrideColor, true) { Atlas.BLOCKS.atlas[key.roundLeafTexture]!! }
|
||||
val leafModelsNormal = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], key.tintIndex, true) { key.roundLeafTexture }
|
||||
}
|
||||
val leafModelsSnowed = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], Color.white.asInt, false) { leafSpritesSnowed[it] }
|
||||
val leafModelsSnowed = LazyMap(BakeWrapperManager) { key: StandardLeafKey ->
|
||||
crossModelsTextured(leafModelsBase[key], Color.white.asInt, false) { leafSpritesSnowed[it].id }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,61 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.BlockRenderKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.RenderKeyFactory
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.semiRandom
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.DOWN
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
object LilypadKey : BlockRenderKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = LilypadModel(meshifyStandard(model, state))
|
||||
object StandardLilypadDiscovery : AbstractModelDiscovery() {
|
||||
val LILYPAD_BLOCKS = listOf(Blocks.LILY_PAD)
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in LILYPAD_BLOCKS) {
|
||||
ctx.addReplacement(StandardLilypadKey)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
object LilyPadDiscovery : ModelDiscoveryBase() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
|
||||
if (ctx.state.block == Blocks.LILY_PAD) LilypadKey else null
|
||||
object StandardLilypadKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardLilypadModel(meshifyCutoutMipped(wrapped))
|
||||
}
|
||||
|
||||
class LilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
class StandardLilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return
|
||||
|
||||
val random = randomSupplier.get()
|
||||
context.meshConsumer().accept(lilypadRootModels[random])
|
||||
ShadersModIntegration.grass(context, BetterFoliage.config.lilypad.shaderWind) {
|
||||
context.meshConsumer().accept(lilypadRootModels[random])
|
||||
}
|
||||
if (random.nextInt(64) < BetterFoliage.config.lilypad.population) {
|
||||
context.meshConsumer().accept(lilypadFlowerModels[random])
|
||||
}
|
||||
@@ -52,13 +68,13 @@ class LilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
val lilypadFlowerSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx")
|
||||
}
|
||||
val lilypadRootModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val lilypadRootModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = tuftShapeSet(1.0, 1.0, 1.0, BetterFoliage.config.lilypad.hOffset)
|
||||
tuftModelSet(shapes, Color.white.asInt) { lilypadRootSprites[it] }
|
||||
.transform { move(2.0 to DOWN) }
|
||||
.buildTufts()
|
||||
}
|
||||
val lilypadFlowerModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val lilypadFlowerModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = tuftShapeSet(0.5, 0.5, 0.5, BetterFoliage.config.lilypad.hOffset)
|
||||
tuftModelSet(shapes, Color.white.asInt) { lilypadFlowerSprites[it] }
|
||||
.transform { move(1.0 to DOWN) }
|
||||
|
||||
@@ -1,43 +1,62 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.buildTufts
|
||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.render.ShadersModIntegration
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.BlockRenderKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.RenderKeyFactory
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
object MyceliumKey : BlockRenderKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = MyceliumModel(meshifyStandard(model, state))
|
||||
object StandardMyceliumDiscovery : AbstractModelDiscovery() {
|
||||
val MYCELIUM_BLOCKS = listOf(Blocks.MYCELIUM)
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in MYCELIUM_BLOCKS) {
|
||||
ctx.addReplacement(StandardMyceliumKey)
|
||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutout())
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
object MyceliumDiscovery : ModelDiscoveryBase() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
val myceliumBlocks = listOf(Blocks.MYCELIUM)
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
|
||||
if (ctx.state.block in myceliumBlocks) MyceliumKey else null
|
||||
object StandardMyceliumKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardMyceliumModel(meshifyCutoutMipped(wrapped))
|
||||
}
|
||||
|
||||
class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
class StandardMyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
|
||||
val tuftLighting = grassTuftLighting(UP)
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
|
||||
val random = randomSupplier.get()
|
||||
@@ -45,8 +64,10 @@ class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } &&
|
||||
blockView.getBlockState(pos + UP.offset).isAir
|
||||
) {
|
||||
context.withLighting(tuftLighting) {
|
||||
it.accept(myceliumTuftModels[random])
|
||||
ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
|
||||
context.withLighting(tuftLighting) {
|
||||
it.accept(myceliumTuftModels[random])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +76,7 @@ class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
val myceliumTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_mycel_$idx")
|
||||
}
|
||||
val myceliumTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val myceliumTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, Color.white.asInt) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
|
||||
}
|
||||
|
||||
@@ -1,44 +1,72 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.NETHERRACK_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.build
|
||||
import mods.betterfoliage.model.meshifyCutoutMipped
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.model.withOpposites
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.BlockRenderKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.RenderKeyFactory
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.DOWN
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
object NetherrackKey : BlockRenderKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = NetherrackModel(meshifyStandard(model, state))
|
||||
object StandardNetherrackDiscovery : AbstractModelDiscovery() {
|
||||
|
||||
fun canRenderInLayer(layer: RenderLayer) = when {
|
||||
!BetterFoliage.config.enabled -> layer == RenderLayer.getSolid()
|
||||
!BetterFoliage.config.netherrack.enabled -> layer == RenderLayer.getSolid()
|
||||
else -> layer == RenderLayer.getCutoutMipped()
|
||||
}
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in NETHERRACK_BLOCKS) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardNetherrackKey)
|
||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
object NetherrackDiscovery : ModelDiscoveryBase() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
val netherrackBlocks = listOf(Blocks.NETHERRACK)
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
|
||||
if (ctx.state.block in netherrackBlocks) NetherrackKey else null
|
||||
object StandardNetherrackKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardNetherrackModel(meshifyCutoutMipped(wrapped))
|
||||
}
|
||||
|
||||
class NetherrackModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
class StandardNetherrackModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
|
||||
val tuftLighting = grassTuftLighting(DOWN)
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
if (BetterFoliage.config.enabled &&
|
||||
BetterFoliage.config.netherrack.enabled &&
|
||||
@@ -55,12 +83,12 @@ class NetherrackModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_netherrack_$idx")
|
||||
}
|
||||
val netherrackTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val netherrackTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = BetterFoliage.config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||
tuftModelSet(shapes, Color.white.asInt) { netherrackTuftSprites[randomI()] }
|
||||
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
|
||||
.withOpposites()
|
||||
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
|
||||
.build(BlendMode.CUTOUT_MIPPED, flatLighting = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,54 @@
|
||||
package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.column.*
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.meshifySolid
|
||||
import mods.betterfoliage.render.column.ColumnBlockKey
|
||||
import mods.betterfoliage.render.column.ColumnMeshSet
|
||||
import mods.betterfoliage.render.column.ColumnModelBase
|
||||
import mods.betterfoliage.render.column.ColumnRenderLayer
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.*
|
||||
import mods.betterfoliage.resource.model.meshifyStandard
|
||||
import mods.betterfoliage.util.LazyMap
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.LogBlock
|
||||
import net.minecraft.block.PillarBlock
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import java.util.function.Consumer
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
interface RoundLogKey : ColumnBlockKey, ModelBakingKey {
|
||||
val barkSprite: Identifier
|
||||
val endSprite: Identifier
|
||||
}
|
||||
|
||||
object RoundLogOverlayLayer : ColumnRenderLayer() {
|
||||
override fun getColumnKey(state: BlockState) = BetterFoliage.modelReplacer.getTyped<ColumnBlockKey>(state)
|
||||
override fun getColumnKey(state: BlockState) = BetterFoliage.blockTypes.getTyped<ColumnBlockKey>(state)
|
||||
override val connectSolids: Boolean get() = BetterFoliage.config.roundLogs.connectSolids
|
||||
override val lenientConnect: Boolean get() = BetterFoliage.config.roundLogs.lenientConnect
|
||||
override val defaultToY: Boolean get() = BetterFoliage.config.roundLogs.defaultY
|
||||
}
|
||||
|
||||
object StandardLogDiscovery : ConfigurableModelDiscovery() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
object StandardRoundLogDiscovery : ConfigurableModelDiscovery() {
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.logBlocks
|
||||
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.logModels.modelList
|
||||
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
|
||||
val axis = getAxis(state)
|
||||
log(" axis $axis")
|
||||
return RoundLogModel.Key(axis, Identifier(textures[0]), Identifier(textures[1]))
|
||||
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<Identifier>) {
|
||||
val axis = getAxis(ctx.blockState)
|
||||
detailLogger.log(Level.INFO, " axis $axis")
|
||||
ctx.addReplacement(StandardRoundLogKey(axis, textureMatch[0], textureMatch[1]))
|
||||
}
|
||||
|
||||
fun getAxis(state: BlockState): Axis? {
|
||||
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
|
||||
val axis = tryDefault(null) { state.get(PillarBlock.AXIS).toString() } ?:
|
||||
state.entries.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
|
||||
return when (axis) {
|
||||
"x" -> Axis.X
|
||||
@@ -46,12 +59,15 @@ object StandardLogDiscovery : ConfigurableModelDiscovery() {
|
||||
}
|
||||
}
|
||||
|
||||
interface RoundLogKey : ColumnBlockKey, BlockRenderKey {
|
||||
val barkSprite: Identifier
|
||||
val endSprite: Identifier
|
||||
data class StandardRoundLogKey(
|
||||
override val axis: Axis?,
|
||||
override val barkSprite: Identifier,
|
||||
override val endSprite: Identifier
|
||||
) : RoundLogKey, ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardRoundLogModel(meshifySolid(wrapped), this)
|
||||
}
|
||||
|
||||
class RoundLogModel(val key: Key, wrapped: BakedModel) : ColumnModelBase(wrapped) {
|
||||
class StandardRoundLogModel(wrapped: BakedModel, val key: StandardRoundLogKey) : ColumnModelBase(wrapped) {
|
||||
override val enabled: Boolean get() = BetterFoliage.config.enabled && BetterFoliage.config.roundLogs.enabled
|
||||
override val overlayLayer: ColumnRenderLayer get() = RoundLogOverlayLayer
|
||||
override val connectPerpendicular: Boolean get() = BetterFoliage.config.roundLogs.connectPerpendicular
|
||||
@@ -59,18 +75,10 @@ class RoundLogModel(val key: Key, wrapped: BakedModel) : ColumnModelBase(wrapped
|
||||
val modelSet by modelSets.delegate(key)
|
||||
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
|
||||
|
||||
data class Key(
|
||||
override val axis: Axis?,
|
||||
override val barkSprite: Identifier,
|
||||
override val endSprite: Identifier
|
||||
) : RoundLogKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = RoundLogModel(this, meshifyStandard(model, state))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val modelSets = LazyMap(BetterFoliage.modelReplacer) { key: Key ->
|
||||
val barkSprite = Atlas.BLOCKS.atlas[key.barkSprite]!!
|
||||
val endSprite = Atlas.BLOCKS.atlas[key.endSprite]!!
|
||||
val modelSets = LazyMap(BakeWrapperManager) { key: StandardRoundLogKey ->
|
||||
val barkSprite = Atlas.BLOCKS[key.barkSprite]!!
|
||||
val endSprite = Atlas.BLOCKS[key.endSprite]!!
|
||||
BetterFoliage.config.roundLogs.let { config ->
|
||||
ColumnMeshSet(
|
||||
config.radiusSmall, config.radiusLarge, config.zProtection,
|
||||
|
||||
@@ -2,52 +2,78 @@ package mods.betterfoliage.render.block.vanilla
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.CachedBlockCtx
|
||||
import mods.betterfoliage.render.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.render.SAND_BLOCKS
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.config.SALTWATER_BIOMES
|
||||
import mods.betterfoliage.config.SAND_BLOCKS
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.ModelWrapKey
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.build
|
||||
import mods.betterfoliage.model.horizontalRectangle
|
||||
import mods.betterfoliage.model.meshifySolid
|
||||
import mods.betterfoliage.model.meshifyStandard
|
||||
import mods.betterfoliage.model.transform
|
||||
import mods.betterfoliage.model.tuftModelSet
|
||||
import mods.betterfoliage.model.tuftShapeSet
|
||||
import mods.betterfoliage.model.withOpposites
|
||||
import mods.betterfoliage.render.lighting.grassTuftLighting
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.resource.discovery.BlockRenderKey
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
|
||||
import mods.betterfoliage.render.lighting.withLighting
|
||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.LazyInvalidatable
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.randomB
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.Random
|
||||
import java.util.function.Supplier
|
||||
|
||||
object SandKey : BlockRenderKey {
|
||||
override fun replace(model: BakedModel, state: BlockState) = SandModel(meshifyStandard(model, state))
|
||||
object StandardSandDiscovery : AbstractModelDiscovery() {
|
||||
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
if (ctx.getUnbaked() is JsonUnbakedModel && ctx.blockState.block in SAND_BLOCKS) {
|
||||
BetterFoliage.blockTypes.dirt.add(ctx.blockState)
|
||||
ctx.addReplacement(StandardSandKey)
|
||||
// RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutoutMipped())
|
||||
}
|
||||
super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
object SandDiscovery : ModelDiscoveryBase() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
|
||||
if (ctx.state.block in SAND_BLOCKS) SandKey else null
|
||||
object StandardSandKey : ModelWrapKey() {
|
||||
override fun bake(ctx: ModelBakingContext, wrapped: BasicBakedModel) = StandardSandModel(meshifySolid(wrapped))
|
||||
}
|
||||
|
||||
class SandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
class StandardSandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
|
||||
val coralLighting = allDirections.map { grassTuftLighting(it) }.toTypedArray()
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
|
||||
val ctx = CachedBlockCtx(blockView, pos)
|
||||
|
||||
val random = randomSupplier.get()
|
||||
if (!BetterFoliage.config.enabled || !BetterFoliage.config.coral.enabled(random)) return
|
||||
if (ctx.biome.category !in SALTWATER_BIOMES) return
|
||||
if (ctx.biome?.category !in SALTWATER_BIOMES) return
|
||||
|
||||
allDirections.filter { random.nextInt(64) < BetterFoliage.config.coral.chance }.forEach { face ->
|
||||
val isWater = ctx.state(face).material == Material.WATER
|
||||
@@ -70,16 +96,16 @@ class SandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
val coralCrustSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "blocks/better_crust_$idx")
|
||||
}
|
||||
val coralTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val coralTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
val shapes = BetterFoliage.config.coral.let { tuftShapeSet(it.size, 1.0, 1.0, it.hOffset) }
|
||||
allDirections.map { face ->
|
||||
tuftModelSet(shapes, Color.white.asInt) { coralTuftSprites[randomI()] }
|
||||
.transform { rotate(Rotation.fromUp[face]) }
|
||||
.withOpposites()
|
||||
.build(CUTOUT_MIPPED)
|
||||
.build(BlendMode.CUTOUT_MIPPED)
|
||||
}.toTypedArray()
|
||||
}
|
||||
val coralCrustModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
|
||||
val coralCrustModels by LazyInvalidatable(BakeWrapperManager) {
|
||||
allDirections.map { face ->
|
||||
Array(64) { idx ->
|
||||
listOf(horizontalRectangle(x1 = -0.5, x2 = 0.5, z1 = -0.5, z2 = 0.5, y = 0.0)
|
||||
@@ -88,7 +114,7 @@ class SandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
.rotate(Rotation.fromUp[face])
|
||||
.mirrorUV(randomB(), randomB()).rotateUV(randomI(max = 4))
|
||||
.sprite(coralCrustSprites[idx]).colorAndIndex(null)
|
||||
).build(CUTOUT_MIPPED)
|
||||
).build(BlendMode.CUTOUT_MIPPED)
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
@@ -2,13 +2,25 @@ package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.Quad
|
||||
import mods.betterfoliage.model.UV
|
||||
import mods.betterfoliage.model.Vertex
|
||||
import mods.betterfoliage.model.build
|
||||
import mods.betterfoliage.model.horizontalRectangle
|
||||
import mods.betterfoliage.model.verticalRectangle
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import net.minecraft.block.BlockRenderLayer.SOLID
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode.SOLID
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.util.math.Direction.*
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.util.math.Direction.EAST
|
||||
import net.minecraft.util.math.Direction.SOUTH
|
||||
import net.minecraft.util.math.Direction.UP
|
||||
|
||||
/**
|
||||
* Collection of dynamically generated meshes used to render rounded columns.
|
||||
|
||||
@@ -5,13 +5,13 @@ import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.NormalRender
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.resource.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
@@ -21,7 +21,8 @@ abstract class ColumnModelBase(wrapped: BakedModel) : WrappedBakedModel(wrapped)
|
||||
abstract val connectPerpendicular: Boolean
|
||||
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
override fun emitBlockQuads(blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
if (!enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
val ctx = CachedBlockCtx(blockView, pos)
|
||||
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
|
||||
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.chunk.ChunkOverlayLayer
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.chunk.dimType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.NONSOLID
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PARALLEL
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PERPENDICULAR
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.SOLID
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.face
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.plus
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.util.math.Direction.AxisDirection
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
/** Index of SOUTH-EAST quadrant. */
|
||||
const val SE = 0
|
||||
@@ -73,13 +85,14 @@ 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: ExtendedBlockView, pos: BlockPos) {
|
||||
override fun onBlockUpdate(world: WorldView, pos: BlockPos) {
|
||||
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(world.dimType, this, pos + offset) }
|
||||
}
|
||||
|
||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
||||
if (allDirections.all { ctx.offset(it).isNormalCube }) return ColumnLayerData.SkipRender
|
||||
// val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
||||
if (allDirections.all { dir ->
|
||||
ctx.offset(dir).let { it.isNormalCube && !BetterFoliage.blockTypes.hasTyped<RoundLogKey>(it.state) }
|
||||
}) 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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.reflectField
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
@@ -7,31 +9,45 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.*
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
|
||||
val AbstractQuadRenderer_blockInfo2 = YarnHelper.requiredField<TerrainBlockRenderInfo>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractQuadRenderer",
|
||||
"blockInfo", "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainBlockRenderInfo;"
|
||||
)
|
||||
val AbstractQuadRenderer_bufferFunc2 = YarnHelper.requiredField<java.util.function.Function<RenderLayer, VertexConsumer>>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractQuadRenderer",
|
||||
"bufferFunc", "Ljava/util/function/Function;"
|
||||
)
|
||||
val AbstractQuadRenderer_aoCalc = YarnHelper.requiredField<AoCalculator>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractQuadRenderer",
|
||||
"aoCalc", "Lnet/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator;"
|
||||
)
|
||||
val AbstractQuadRenderer_transform = YarnHelper.requiredField<RenderContext.QuadTransform>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractQuadRenderer",
|
||||
"transform", "Lnet/fabricmc/fabric/api/renderer/v1/render/RenderContext\$QuadTransform;"
|
||||
)
|
||||
|
||||
val MODIFIED_CONSUMER_POOL = ThreadLocal<ModifiedTerrainMeshConsumer>()
|
||||
|
||||
fun TerrainMeshConsumer.modified() = MODIFIED_CONSUMER_POOL.get() ?: let {
|
||||
val blockInfo = reflectField<TerrainBlockRenderInfo>("blockInfo")
|
||||
val chunkInfo = reflectField<ChunkRenderInfo>("chunkInfo")
|
||||
val aoCalc = reflectField<AoCalculator>("aoCalc")
|
||||
val transform = reflectField<RenderContext.QuadTransform>("transform")
|
||||
ModifiedTerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, transform)
|
||||
fun AbstractMeshConsumer.modified() = MODIFIED_CONSUMER_POOL.get() ?: let {
|
||||
ModifiedTerrainMeshConsumer(this)
|
||||
}.apply { MODIFIED_CONSUMER_POOL.set(this) }
|
||||
|
||||
/**
|
||||
* Render the given model at the given position.
|
||||
* Mutates the state of the [RenderContext]!!
|
||||
*/
|
||||
fun RenderContext.renderMasquerade(model: BakedModel, blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) = when(this) {
|
||||
fun RenderContext.renderMasquerade(model: BakedModel, blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) = when(this) {
|
||||
is TerrainRenderContext -> {
|
||||
val blockInfo = reflectField<TerrainBlockRenderInfo>("blockInfo")
|
||||
val blockInfo = meshConsumer()[AbstractQuadRenderer_blockInfo2]!!
|
||||
blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion())
|
||||
(model as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
@@ -43,7 +59,7 @@ fun RenderContext.renderMasquerade(model: BakedModel, blockView: ExtendedBlockVi
|
||||
/** Execute the provided block with a mesh consumer using the given custom lighting. */
|
||||
fun RenderContext.withLighting(lighter: CustomLighting, func: (Consumer<Mesh>)->Unit) = when(this) {
|
||||
is TerrainRenderContext -> {
|
||||
val consumer = (meshConsumer() as TerrainMeshConsumer).modified()
|
||||
val consumer = (meshConsumer() as AbstractMeshConsumer).modified()
|
||||
consumer.clearLighting()
|
||||
consumer.lighter = lighter
|
||||
func(consumer)
|
||||
@@ -51,3 +67,12 @@ fun RenderContext.withLighting(lighter: CustomLighting, func: (Consumer<Mesh>)->
|
||||
}
|
||||
else -> func(meshConsumer())
|
||||
}
|
||||
|
||||
/** Get the [BufferBuilder] responsible for a given [BlockRenderLayer] */
|
||||
fun RenderContext.getBufferBuilder(layer: RenderLayer) = when(this) {
|
||||
is TerrainRenderContext -> {
|
||||
val bufferFunc = meshConsumer()[AbstractQuadRenderer_bufferFunc2]!!
|
||||
bufferFunc.apply(layer)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package mods.betterfoliage.render.particle
|
||||
|
||||
import mods.betterfoliage.util.Double3
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.particle.SpriteBillboardParticle
|
||||
import net.minecraft.client.render.Camera
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.client.util.math.Vector3f
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.world.World
|
||||
|
||||
abstract class AbstractParticle(world: ClientWorld, x: Double, y: Double, z: Double) : SpriteBillboardParticle(world, x, y, z) {
|
||||
|
||||
companion object {
|
||||
// @JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
||||
// @JvmStatic val cos = Array(64) { idx -> Math.cos(PI2 / 64.0 * idx) }
|
||||
}
|
||||
|
||||
val billboardRot = Pair(Double3.zero, Double3.zero)
|
||||
val currentPos = Double3.zero
|
||||
val prevPos = Double3.zero
|
||||
val velocity = Double3.zero
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
currentPos.setTo(x, y, z)
|
||||
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
||||
velocity.setTo(velocityX, velocityY, velocityZ)
|
||||
update()
|
||||
x = currentPos.x; y = currentPos.y; z = currentPos.z;
|
||||
velocityX = velocity.x; velocityY = velocity.y; velocityZ = velocity.z;
|
||||
}
|
||||
|
||||
/** Update particle on world tick. */
|
||||
abstract fun update()
|
||||
|
||||
/** True if the particle is renderable. */
|
||||
abstract val isValid: Boolean
|
||||
|
||||
/** Add the particle to the effect renderer if it is valid. */
|
||||
fun addIfValid() { if (isValid) MinecraftClient.getInstance().particleManager.addParticle(this) }
|
||||
|
||||
override fun buildGeometry(vertexConsumer: VertexConsumer, camera: Camera, tickDelta: Float) {
|
||||
renderParticleQuad(vertexConsumer, camera, tickDelta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a particle quad.
|
||||
*
|
||||
* @param[tessellator] the [Tessellator] instance to use
|
||||
* @param[tickDelta] partial tick time
|
||||
* @param[currentPos] render position
|
||||
* @param[prevPos] previous tick position for interpolation
|
||||
* @param[size] particle size
|
||||
* @param[currentAngle] viewpoint-dependent particle rotation (64 steps)
|
||||
* @param[sprite] particle texture
|
||||
* @param[isMirrored] mirror particle texture along V-axis
|
||||
* @param[alpha] aplha blending
|
||||
*/
|
||||
fun renderParticleQuad(vertexConsumer: VertexConsumer,
|
||||
camera: Camera,
|
||||
tickDelta: Float,
|
||||
currentPos: Double3 = this.currentPos,
|
||||
prevPos: Double3 = this.prevPos,
|
||||
size: Double = scale.toDouble(),
|
||||
currentAngle: Float = this.angle,
|
||||
prevAngle: Float = this.prevAngle,
|
||||
sprite: Sprite = this.sprite,
|
||||
alpha: Float = this.colorAlpha) {
|
||||
|
||||
val center = Double3.lerp(tickDelta.toDouble(), prevPos, currentPos)
|
||||
val angle = MathHelper.lerp(tickDelta, prevAngle, currentAngle)
|
||||
val rotation = camera.rotation.copy().apply { hamiltonProduct(Vector3f.POSITIVE_Z.getRadialQuaternion(angle)) }
|
||||
val lightmapCoord = getColorMultiplier(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.pos.x, camera.pos.y, camera.pos.z) }
|
||||
|
||||
fun renderVertex(vertex: Double3, u: Float, v: Float) = vertexConsumer
|
||||
.vertex(vertex.x, vertex.y, vertex.z).texture(u, v)
|
||||
.color(colorRed, colorGreen, colorBlue, alpha).light(lightmapCoord)
|
||||
.next()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fun setColor(color: Int) {
|
||||
colorBlue = (color and 255) / 256.0f
|
||||
colorGreen = ((color shr 8) and 255) / 256.0f
|
||||
colorRed = ((color shr 16) and 255) / 256.0f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,27 @@ package mods.betterfoliage.render.particle
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.ClientWorldLoadCallback
|
||||
import mods.betterfoliage.render.AbstractParticle
|
||||
import mods.betterfoliage.render.block.vanilla.LeafKey
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.render.block.vanilla.LeafParticleKey
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.PI2
|
||||
import mods.betterfoliage.util.minmax
|
||||
import mods.betterfoliage.util.randomB
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomF
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.fabricmc.fabric.api.event.world.WorldTickCallback
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.BufferBuilder
|
||||
import net.minecraft.client.particle.ParticleTextureSheet
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.world.World
|
||||
import java.util.*
|
||||
import java.util.Random
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class FallingLeafParticle(
|
||||
world: World, pos: BlockPos, leafKey: LeafKey
|
||||
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
|
||||
) {
|
||||
@@ -27,32 +31,34 @@ class FallingLeafParticle(
|
||||
@JvmStatic val biomeBrightnessMultiplier = 0.5f
|
||||
}
|
||||
|
||||
var rotationSpeed = randomF(min = PI2 / 80.0, max = PI2 / 50.0)
|
||||
var rotPositive = true
|
||||
var rotationSpeed = random.randomF(min = PI2 / 80.0, max = PI2 / 50.0)
|
||||
val isMirrored = randomB()
|
||||
var wasCollided = false
|
||||
|
||||
init {
|
||||
angle = randomF(max = PI2)
|
||||
angle = random.randomF(max = PI2)
|
||||
prevAngle = angle - rotationSpeed
|
||||
|
||||
maxAge = MathHelper.floor(randomD(0.6, 1.0) * BetterFoliage.config.fallingLeaves.lifetime * 20.0)
|
||||
velocityY = -BetterFoliage.config.fallingLeaves.speed
|
||||
|
||||
scale = BetterFoliage.config.fallingLeaves.size.toFloat() * 0.1f
|
||||
|
||||
val state = world.getBlockState(pos)
|
||||
val blockColor = MinecraftClient.getInstance().blockColorMap.getColorMultiplier(state, world, pos, 0)
|
||||
sprite = LeafParticleRegistry[leafKey.leafType][randomI(max = 1024)]
|
||||
setParticleColor(leafKey.overrideColor, blockColor)
|
||||
|
||||
setColor(leaf.overrideColor?.asInt ?: blockColor)
|
||||
sprite = LeafParticleRegistry[leaf.leafType][randomI(max = 1024)]
|
||||
}
|
||||
|
||||
override val isValid: Boolean get() = (sprite != null)
|
||||
|
||||
override fun update() {
|
||||
if (randomF() > 0.95f) rotPositive = !rotPositive
|
||||
// if (age > maxAge - 20) colorAlpha = 0.05f * (maxAge - age)
|
||||
if (randomF() > 0.95f) rotationSpeed = -rotationSpeed
|
||||
if (age > maxAge - 20) colorAlpha = 0.05f * (maxAge - age)
|
||||
|
||||
if (onGround || wasCollided) {
|
||||
velocity.setTo(0.0, 0.0, 0.0)
|
||||
prevAngle = angle
|
||||
if (!wasCollided) {
|
||||
age = age.coerceAtLeast(maxAge - 20)
|
||||
wasCollided = true
|
||||
@@ -61,19 +67,19 @@ class FallingLeafParticle(
|
||||
val cosRotation = cos(angle).toDouble(); val sinRotation = sin(angle).toDouble()
|
||||
velocity.setTo(cosRotation, 0.0, sinRotation).mul(BetterFoliage.config.fallingLeaves.perturb)
|
||||
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(BetterFoliage.config.fallingLeaves.speed)
|
||||
angle += if (rotPositive) rotationSpeed else -rotationSpeed
|
||||
prevAngle = angle
|
||||
angle += rotationSpeed
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
||||
val tickAngle = angle + partialTickTime * (if (rotPositive) rotationSpeed else -rotationSpeed)
|
||||
renderParticleQuad(worldRenderer, partialTickTime, rotation = tickAngle.toDouble(), isMirrored = isMirrored)
|
||||
}
|
||||
|
||||
fun setParticleColor(overrideColor: Int?, blockColor: Int) {
|
||||
val color = overrideColor ?: blockColor
|
||||
setColor(color)
|
||||
}
|
||||
|
||||
override fun getType() =
|
||||
if (BetterFoliage.config.fallingLeaves.opacityHack) ParticleTextureSheet.PARTICLE_SHEET_OPAQUE
|
||||
else ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT
|
||||
}
|
||||
|
||||
object LeafWindTracker : WorldTickCallback, ClientWorldLoadCallback {
|
||||
|
||||
@@ -1,42 +1,76 @@
|
||||
package mods.betterfoliage.render.particle
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.resource.model.FixedSpriteSet
|
||||
import mods.betterfoliage.resource.model.SpriteSet
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.model.FixedSpriteSet
|
||||
import mods.betterfoliage.model.SpriteSet
|
||||
import mods.betterfoliage.resource.VeryEarlyReloadListener
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.getLines
|
||||
import mods.betterfoliage.util.resourceManager
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback
|
||||
import net.minecraft.client.texture.MissingSprite
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.Level
|
||||
import kotlin.collections.MutableList
|
||||
import kotlin.collections.distinct
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.firstOrNull
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.isNotEmpty
|
||||
import kotlin.collections.joinToString
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.set
|
||||
|
||||
object LeafParticleRegistry : ClientSpriteRegistryCallback {
|
||||
object LeafParticleRegistry : HasLogger(), ClientSpriteRegistryCallback, VeryEarlyReloadListener {
|
||||
val typeMappings = TextureMatcher()
|
||||
val allTypes get() = (typeMappings.mappings.map { it.type } + "default").distinct()
|
||||
|
||||
val ids = mutableMapOf<String, List<Identifier>>()
|
||||
val spriteSets = mutableMapOf<String, SpriteSet>()
|
||||
|
||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
||||
ids.clear()
|
||||
spriteSets.clear()
|
||||
override fun getFabricId() = Identifier(BetterFoliage.MOD_ID, "leaf-particles")
|
||||
|
||||
override fun onReloadStarted(resourceManager: ResourceManager) {
|
||||
typeMappings.loadMappings(Identifier(BetterFoliage.MOD_ID, "leaf_texture_mappings.cfg"))
|
||||
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
||||
val validIds = (0 until 16).map { idx -> Identifier(BetterFoliage.MOD_ID, "falling_leaf_${leafType}_$idx") }
|
||||
.filter { resourceManager.containsResource(Atlas.PARTICLES.wrap(it)) }
|
||||
ids[leafType] = validIds
|
||||
detailLogger.log(Level.INFO, "Loaded leaf particle mappings, types = [${allTypes.joinToString(", ")}]")
|
||||
}
|
||||
|
||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
||||
spriteSets.clear()
|
||||
allTypes.forEach { leafType ->
|
||||
val validIds = (0 until 16).map { idx -> Identifier(BetterFoliage.MOD_ID, "particle/falling_leaf_${leafType}_$idx") }
|
||||
.filter { resourceManager.containsResource(Atlas.PARTICLES.file(it)) }
|
||||
validIds.forEach { registry.register(it) }
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(type: String): SpriteSet {
|
||||
spriteSets[type]?.let { return it }
|
||||
ids[type]?.let {
|
||||
return FixedSpriteSet(Atlas.PARTICLES, it).apply { spriteSets[type] = this }
|
||||
}
|
||||
return if (type == "default") FixedSpriteSet(Atlas.PARTICLES, emptyList()).apply { spriteSets[type] = this }
|
||||
else get("default")
|
||||
operator fun get(leafType: String): SpriteSet {
|
||||
spriteSets[leafType]?.let { return it }
|
||||
|
||||
val sprites = (0 until 16)
|
||||
.map { idx -> Identifier(BetterFoliage.MOD_ID, "particle/falling_leaf_${leafType}_$idx") }
|
||||
.map { Atlas.PARTICLES[it] }
|
||||
.filter { it !is MissingSprite }
|
||||
|
||||
detailLogger.log(Level.INFO, "Leaf particle type [$leafType], ${sprites.size} sprites in atlas")
|
||||
if (sprites.isNotEmpty()) return FixedSpriteSet(sprites).apply { spriteSets[leafType] = this }
|
||||
|
||||
return if (leafType == "default")
|
||||
FixedSpriteSet(listOf(Atlas.PARTICLES[MissingSprite.getMissingSpriteId()])).apply { spriteSets[leafType] = this }
|
||||
else
|
||||
get("default").apply { spriteSets[leafType] = this }
|
||||
}
|
||||
|
||||
init {
|
||||
ClientSpriteRegistryCallback.event(SpriteAtlasTexture.PARTICLE_ATLAS_TEX).register(this)
|
||||
ClientSpriteRegistryCallback.event(SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE).register(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
package mods.betterfoliage.render.particle
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.AbstractParticle
|
||||
import mods.betterfoliage.resource.model.SpriteDelegate
|
||||
import mods.betterfoliage.resource.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.util.*
|
||||
import mods.betterfoliage.model.SpriteDelegate
|
||||
import mods.betterfoliage.model.SpriteSetDelegate
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.PI2
|
||||
import mods.betterfoliage.util.forEachPairIndexed
|
||||
import mods.betterfoliage.util.randomD
|
||||
import mods.betterfoliage.util.randomI
|
||||
import net.minecraft.client.particle.ParticleTextureSheet
|
||||
import net.minecraft.client.render.BufferBuilder
|
||||
import net.minecraft.client.render.Camera
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.world.World
|
||||
import java.util.*
|
||||
import java.util.Deque
|
||||
import java.util.LinkedList
|
||||
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
|
||||
) {
|
||||
|
||||
val particleTrail: Deque<Double3> = LinkedList<Double3>()
|
||||
val particleTrail: Deque<Double3> = LinkedList()
|
||||
val initialPhase = randomD(max = PI2)
|
||||
|
||||
init {
|
||||
@@ -34,8 +40,9 @@ class RisingSoulParticle(
|
||||
override val isValid: Boolean get() = true
|
||||
|
||||
override fun update() {
|
||||
val phase = initialPhase + (age.toDouble() * PI2 / 64.0 )
|
||||
val cosPhase = cos(phase); val sinPhase = sin(phase)
|
||||
val phase = initialPhase + (age.toDouble() * PI2 / 64.0)
|
||||
val cosPhase = cos(phase);
|
||||
val sinPhase = sin(phase)
|
||||
velocity.setTo(BetterFoliage.config.risingSoul.perturb.let { Double3(cosPhase * it, 0.1, sinPhase * it) })
|
||||
|
||||
particleTrail.addFirst(currentPos.copy())
|
||||
@@ -44,11 +51,12 @@ class RisingSoulParticle(
|
||||
if (!BetterFoliage.config.enabled) markDead()
|
||||
}
|
||||
|
||||
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
||||
override fun buildGeometry(vertexConsumer: VertexConsumer, camera: Camera, tickDelta: Float) {
|
||||
var alpha = BetterFoliage.config.risingSoul.opacity.toFloat()
|
||||
if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
|
||||
|
||||
renderParticleQuad(worldRenderer, partialTickTime,
|
||||
renderParticleQuad(
|
||||
vertexConsumer, camera, tickDelta,
|
||||
size = BetterFoliage.config.risingSoul.headSize * 0.25,
|
||||
alpha = alpha
|
||||
)
|
||||
@@ -57,20 +65,24 @@ class RisingSoulParticle(
|
||||
particleTrail.forEachPairIndexed { idx, current, previous ->
|
||||
scale *= BetterFoliage.config.risingSoul.sizeDecay
|
||||
alpha *= BetterFoliage.config.risingSoul.opacityDecay.toFloat()
|
||||
if (idx % BetterFoliage.config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
|
||||
currentPos = current,
|
||||
prevPos = previous,
|
||||
size = scale,
|
||||
alpha = alpha,
|
||||
sprite = trackIcon
|
||||
)
|
||||
if (idx % BetterFoliage.config.risingSoul.trailDensity == 0)
|
||||
renderParticleQuad(
|
||||
vertexConsumer, camera, tickDelta,
|
||||
currentPos = current,
|
||||
prevPos = previous,
|
||||
size = scale,
|
||||
alpha = alpha,
|
||||
sprite = trackIcon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType() = ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT
|
||||
|
||||
companion object {
|
||||
val headIcons by SpriteSetDelegate(Atlas.PARTICLES) { idx -> Identifier(BetterFoliage.MOD_ID, "rising_soul_$idx") }
|
||||
val trackIcon by SpriteDelegate(Atlas.PARTICLES) { Identifier(BetterFoliage.MOD_ID, "soul_track") }
|
||||
val headIcons by SpriteSetDelegate(Atlas.PARTICLES) { idx ->
|
||||
Identifier(BetterFoliage.MOD_ID, "particle/rising_soul_$idx")
|
||||
}
|
||||
val trackIcon by SpriteDelegate(Atlas.PARTICLES) { Identifier(BetterFoliage.MOD_ID, "particle/soul_track") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package mods.betterfoliage.resource
|
||||
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloadListener
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/**
|
||||
* Catch resource reload extremely early, before any of the reloaders
|
||||
* have started working.
|
||||
*/
|
||||
interface VeryEarlyReloadListener : ResourceReloadListener, IdentifiableResourceReloadListener {
|
||||
override fun reload(
|
||||
synchronizer: ResourceReloadListener.Synchronizer,
|
||||
resourceManager: ResourceManager,
|
||||
preparationsProfiler: Profiler,
|
||||
reloadProfiler: Profiler,
|
||||
backgroundExecutor: Executor,
|
||||
gameExecutor: Executor
|
||||
): CompletableFuture<Void> {
|
||||
onReloadStarted(resourceManager)
|
||||
return synchronizer.whenPrepared(null)
|
||||
}
|
||||
|
||||
fun onReloadStarted(resourceManager: ResourceManager) {}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BlockModelsReloadCallback
|
||||
import mods.betterfoliage.ModelLoadingCallback
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.get
|
||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.block.BlockModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.ModelLoader
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
|
||||
// net.minecraft.client.render.block.BlockModels.models
|
||||
val BlockModels_models = YarnHelper.requiredField<Map<BlockState, BakedModel>>("net.minecraft.class_773", "field_4162", "Ljava/util/Map;")
|
||||
|
||||
class BakedModelReplacer : ModelLoadingCallback, ClientSpriteRegistryCallback, BlockModelsReloadCallback, Invalidator, HasLogger {
|
||||
override val logger get() = BetterFoliage.logDetail
|
||||
|
||||
val discoverers = mutableListOf<ModelDiscovery>()
|
||||
override val callbacks = mutableListOf<WeakReference<()->Unit>>()
|
||||
|
||||
protected var keys = emptyMap<BlockState, BlockRenderKey>()
|
||||
|
||||
operator fun get(state: BlockState) = keys[state]
|
||||
inline fun <reified T> getTyped(state: BlockState) = get(state) as? T
|
||||
|
||||
var currentLoader: ModelLoader? = null
|
||||
|
||||
override fun beginLoadModels(loader: ModelLoader, manager: ResourceManager) {
|
||||
// Step 1: get a hold of the ModelLoader instance when model reloading starts
|
||||
currentLoader = loader
|
||||
log("reloading block discovery configuration")
|
||||
BetterFoliage.blockConfig.reloadConfig(manager)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun registerSprites(atlasTexture: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
||||
// Step 2: ModelLoader is finished with the unbaked models by now, we can inspect them
|
||||
log("discovering blocks")
|
||||
val idSet = Collections.synchronizedSet(mutableSetOf<Identifier>())
|
||||
val allKeys = discoverers.map {
|
||||
// run model discoverers in parallel
|
||||
CompletableFuture.supplyAsync(Supplier {
|
||||
it.discover(currentLoader!!, Consumer { idSet.add(it) })
|
||||
}, MinecraftClient.getInstance())
|
||||
}.map { it.join() }
|
||||
idSet.forEach { registry.register(it) }
|
||||
|
||||
val result = mutableMapOf<BlockState, BlockRenderKey>()
|
||||
allKeys.forEach { keys ->
|
||||
keys.entries.forEach { (state, key) ->
|
||||
val oldKey = result[state]
|
||||
if (oldKey != null) log("Replacing $oldKey with $key for state $state")
|
||||
else log("Adding replacement $key for state $state")
|
||||
result[state] = key
|
||||
}
|
||||
}
|
||||
|
||||
keys = result
|
||||
}
|
||||
|
||||
override fun reloadBlockModels(blockModels: BlockModels) {
|
||||
// Step 3: replace the baked models with our own
|
||||
log("block model baking finished")
|
||||
val modelMap = blockModels[BlockModels_models] as MutableMap<BlockState, BakedModel>
|
||||
keys.forEach { (state, key) ->
|
||||
val oldModel = modelMap[state]
|
||||
if (oldModel == null) log("Cannot find model for state $state, ignoring")
|
||||
else {
|
||||
try {
|
||||
val newModel = key.replace(oldModel, state)
|
||||
modelMap[state] = newModel
|
||||
log("Replaced model for state $state with $key")
|
||||
} catch (e: Exception) {
|
||||
log("Error creating model for state $state with $key", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
ModelLoadingCallback.EVENT.register(this)
|
||||
ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register(this)
|
||||
BlockModelsReloadCallback.EVENT.register(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.BlockModelsReloadCallback
|
||||
import mods.betterfoliage.ModelLoadingCallback
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.block.BlockModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.ModelBakeSettings
|
||||
import net.minecraft.client.render.model.ModelLoader
|
||||
import net.minecraft.client.render.model.UnbakedModel
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.client.util.SpriteIdentifier
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.Level.WARN
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.function.Function
|
||||
|
||||
data class ModelDiscoveryContext(
|
||||
val bakery: ModelLoader,
|
||||
val blockState: BlockState,
|
||||
val modelLocation: Identifier,
|
||||
val sprites: MutableSet<Identifier>,
|
||||
val replacements: MutableMap<Identifier, ModelBakingKey>,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked(location: Identifier = modelLocation) = bakery.getOrLoadModel(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")
|
||||
}
|
||||
}
|
||||
|
||||
interface ModelDiscovery {
|
||||
fun onModelsLoaded(
|
||||
bakery: ModelLoader,
|
||||
sprites: MutableSet<Identifier>,
|
||||
replacements: MutableMap<Identifier, ModelBakingKey>
|
||||
)
|
||||
}
|
||||
|
||||
data class ModelBakingContext(
|
||||
val bakery: ModelLoader,
|
||||
val spriteGetter: Function<SpriteIdentifier, Sprite>,
|
||||
val location: Identifier,
|
||||
val transform: ModelBakeSettings,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked() = bakery.getOrLoadModel(location)
|
||||
fun getBaked() = bakery.bake(location, transform)
|
||||
}
|
||||
|
||||
interface ModelBakingKey {
|
||||
fun bake(ctx: ModelBakingContext): BakedModel? =
|
||||
ctx.getUnbaked().bake(ctx.bakery, ctx.spriteGetter, ctx.transform, ctx.location)
|
||||
}
|
||||
|
||||
object BakeWrapperManager : HasLogger(), Invalidator, ModelLoadingCallback, ClientSpriteRegistryCallback, BlockModelsReloadCallback {
|
||||
init {
|
||||
ModelLoadingCallback.EVENT.register(this)
|
||||
ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).register(this)
|
||||
}
|
||||
val discoverers = mutableListOf<ModelDiscovery>()
|
||||
override val callbacks = mutableListOf<WeakReference<()->Unit>>()
|
||||
|
||||
private val replacements = mutableMapOf<Identifier, ModelBakingKey>()
|
||||
private val sprites = mutableSetOf<Identifier>()
|
||||
|
||||
override fun beginLoadModels(loader: ModelLoader, manager: ResourceManager) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
replacements.clear()
|
||||
sprites.clear()
|
||||
invalidate()
|
||||
BetterFoliage.blockTypes = BlockTypeCache()
|
||||
|
||||
logger.log(INFO, "starting model discovery (${discoverers.size} listeners)")
|
||||
discoverers.forEach { listener ->
|
||||
val replacementsLocal = mutableMapOf<Identifier, ModelBakingKey>()
|
||||
listener.onModelsLoaded(loader, sprites, replacements)
|
||||
}
|
||||
|
||||
val elapsed = System.currentTimeMillis() - startTime
|
||||
logger.log(INFO, "finished model discovery in $elapsed ms, ${replacements.size} top-level replacements")
|
||||
}
|
||||
|
||||
override fun registerSprites(atlas: SpriteAtlasTexture, registry: ClientSpriteRegistryCallback.Registry) {
|
||||
logger.log(INFO, "Adding ${sprites.size} sprites to block atlas")
|
||||
sprites.forEach { registry.register(it) }
|
||||
sprites.clear()
|
||||
}
|
||||
|
||||
override fun reloadBlockModels(blockModels: BlockModels) {
|
||||
replacements.clear()
|
||||
}
|
||||
|
||||
fun onBake(
|
||||
unbaked: UnbakedModel,
|
||||
bakery: ModelLoader,
|
||||
spriteGetter: Function<SpriteIdentifier, Sprite>,
|
||||
transform: ModelBakeSettings,
|
||||
location: Identifier
|
||||
): BakedModel? {
|
||||
val ctx = ModelBakingContext(bakery, spriteGetter, location, transform, detailLogger)
|
||||
// bake replacement if available
|
||||
replacements[location]?.let { replacement ->
|
||||
detailLogger.log(INFO, "Baking replacement for [${unbaked::class.java.simpleName}] $location -> $replacement")
|
||||
try {
|
||||
return replacement.bake(ctx)
|
||||
} catch (e: Exception) {
|
||||
detailLogger.log(WARN, "Error while baking $replacement", e)
|
||||
logger.log(WARN, "Error while baking $replacement", e)
|
||||
}
|
||||
}
|
||||
return unbaked.bake(bakery, spriteGetter, transform, location)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
|
||||
class BlockTypeCache {
|
||||
val leaf = mutableSetOf<BlockState>()
|
||||
val grass = mutableSetOf<BlockState>()
|
||||
val dirt = mutableSetOf<BlockState>()
|
||||
|
||||
val stateKeys = mutableMapOf<BlockState, ModelBakingKey>()
|
||||
|
||||
inline fun <reified T> getTyped(state: BlockState) = stateKeys[state] as? T
|
||||
inline fun <reified T> hasTyped(state: BlockState) = stateKeys[state] is T
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import com.google.common.base.Joiner
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.function.Consumer
|
||||
|
||||
// net.minecraft.client.render.model.json.JsonUnbakedModel.parent
|
||||
val JsonUnbakedModel_parent = YarnHelper.requiredField<JsonUnbakedModel>("net.minecraft.class_793", "field_4253", "Lnet/minecraft/class_793;")
|
||||
// net.minecraft.client.render.model.json.JsonUnbakedModel.parentId
|
||||
val JsonUnbakedModel_parentId = YarnHelper.requiredField<Identifier>("net.minecraft.class_793", "field_4247", "Lnet/minecraft/class_2960;")
|
||||
|
||||
fun Pair<JsonUnbakedModel, Identifier>.derivesFrom(targetLocation: Identifier): Boolean {
|
||||
if (second.stripStart("models/") == targetLocation) return true
|
||||
if (first[JsonUnbakedModel_parent] != null && first[JsonUnbakedModel_parentId] != null)
|
||||
return Pair(first[JsonUnbakedModel_parent]!!, first[JsonUnbakedModel_parentId]!!).derivesFrom(targetLocation)
|
||||
return false
|
||||
}
|
||||
|
||||
abstract class ConfigurableModelDiscovery : ModelDiscoveryBase() {
|
||||
|
||||
abstract val matchClasses: IBlockMatcher
|
||||
abstract val modelTextures: List<ModelTextureList>
|
||||
|
||||
abstract fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey?
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>): BlockRenderKey? {
|
||||
val matchClass = matchClasses.matchingClass(ctx.state.block) ?: return null
|
||||
log("block state ${ctx.state.toString()}")
|
||||
log(" class ${ctx.state.block.javaClass.name} matches ${matchClass.name}")
|
||||
|
||||
(ctx.models.filter { it.first is JsonUnbakedModel } as List<Pair<JsonUnbakedModel, Identifier>>).forEach { (model, location) ->
|
||||
val modelMatch = modelTextures.firstOrNull { (model to location).derivesFrom(it.modelLocation) }
|
||||
if (modelMatch != null) {
|
||||
log(" model ${model} matches ${modelMatch.modelLocation}")
|
||||
|
||||
val textures = modelMatch.textureNames.map { it to model.resolveTexture(it) }
|
||||
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
|
||||
log(" sprites [$texMapString]")
|
||||
|
||||
if (textures.all { it.second != "missingno" }) {
|
||||
// found a valid model (all required textures exist)
|
||||
return processModel(ctx.state, textures.map { it.second }, atlas)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.getLines
|
||||
import mods.betterfoliage.util.INTERMEDIARY
|
||||
import mods.betterfoliage.util.getJavaClass
|
||||
@@ -7,6 +8,8 @@ import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.Logger
|
||||
|
||||
interface IBlockMatcher {
|
||||
@@ -24,7 +27,7 @@ class SimpleBlockMatcher(vararg val classes: Class<*>) : IBlockMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigurableBlockMatcher(val logger: Logger, val location: Identifier) : IBlockMatcher {
|
||||
class ConfigurableBlockMatcher(val location: Identifier) : HasLogger(), IBlockMatcher {
|
||||
|
||||
val blackList = mutableListOf<Class<*>>()
|
||||
val whiteList = mutableListOf<Class<*>>()
|
||||
@@ -47,27 +50,35 @@ class ConfigurableBlockMatcher(val logger: Logger, val location: Identifier) : I
|
||||
blackList.clear()
|
||||
whiteList.clear()
|
||||
manager.getAllResources(location).forEach { resource ->
|
||||
logger.debug("Reading resource $location from pack ${resource.resourcePackName}")
|
||||
detailLogger.log(INFO, "Reading class list $location from pack ${resource.resourcePackName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
if (line.startsWith("-")) getBlockClass(line.substring(1))?.let { blackList.add(it) }
|
||||
else getBlockClass(line)?.let { whiteList.add(it) }
|
||||
val name = if (line.startsWith("-")) line.substring(1) else line
|
||||
val mappedName = FabricLoader.getInstance().mappingResolver.mapClassName(INTERMEDIARY, name)
|
||||
if (name != mappedName) logger.debug(" found yarn mapping for class: $name -> $mappedName")
|
||||
val klass = getJavaClass(mappedName)
|
||||
|
||||
val list = if (line.startsWith("-")) "blacklist" to blackList else "whitelist" to whiteList
|
||||
|
||||
if (klass != null) {
|
||||
logger.debug(" ${list.first} class $name found")
|
||||
list.second.add(klass)
|
||||
} else {
|
||||
logger.debug(" ${list.first} class $name not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBlockClass(name: String) = getJavaClass(FabricLoader.getInstance().mappingResolver.mapClassName(INTERMEDIARY, name))
|
||||
}
|
||||
|
||||
data class ModelTextureList(val modelLocation: Identifier, val textureNames: List<String>) {
|
||||
constructor(vararg args: String) : this(Identifier(args[0]), listOf(*args).drop(1))
|
||||
}
|
||||
|
||||
class ModelTextureListConfiguration(val logger: Logger, val location: Identifier) {
|
||||
class ModelTextureListConfiguration(val location: Identifier) : HasLogger() {
|
||||
val modelList = mutableListOf<ModelTextureList>()
|
||||
fun readDefaults(manager: ResourceManager) {
|
||||
manager.getAllResources(location).forEach { resource ->
|
||||
logger.debug("Reading resource $location from pack ${resource.resourcePackName}")
|
||||
detailLogger.log(INFO, "Reading model configuration $location from pack ${resource.resourcePackName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
val elements = line.split(",")
|
||||
modelList.add(ModelTextureList(Identifier(elements.first()), elements.drop(1)))
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import com.google.common.base.Joiner
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.get
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.render.block.BlockModels
|
||||
import net.minecraft.client.render.model.ModelLoader
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.client.render.model.json.WeightedUnbakedModel
|
||||
import net.minecraft.client.texture.MissingSprite
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.registry.Registry
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
override fun onModelsLoaded(
|
||||
bakery: ModelLoader,
|
||||
sprites: MutableSet<Identifier>,
|
||||
replacements: MutableMap<Identifier, ModelBakingKey>
|
||||
) {
|
||||
(Registry.BLOCK as Iterable<Block>)
|
||||
.flatMap { block -> block.stateManager.states }
|
||||
.forEach { state ->
|
||||
val location = BlockModels.getModelId(state)
|
||||
val ctx = ModelDiscoveryContext(bakery, state, location, sprites, replacements, detailLogger)
|
||||
try {
|
||||
processModel(ctx)
|
||||
} catch (e: Exception) {
|
||||
logger.log(Level.WARN, "Discovery error in $location", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
|
||||
// built-in support for container models
|
||||
if (model is WeightedUnbakedModel) {
|
||||
// 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<Identifier, ModelBakingKey>()
|
||||
model.variants.forEach { variant ->
|
||||
processModel(ctx.copy(modelLocation = variant.location, replacements = scopedReplacements))
|
||||
}
|
||||
if (scopedReplacements.isNotEmpty()) {
|
||||
ctx.addReplacement(WeightedUnbakedKey(scopedReplacements), addToStateKeys = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
||||
abstract val matchClasses: IBlockMatcher
|
||||
abstract val modelTextures: List<ModelTextureList>
|
||||
|
||||
abstract fun processModel(
|
||||
ctx: ModelDiscoveryContext,
|
||||
textureMatch: List<Identifier>
|
||||
)
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
if (model is JsonUnbakedModel) {
|
||||
val matchClass = matchClasses.matchingClass(ctx.blockState.block) ?: return
|
||||
|
||||
detailLogger.log(Level.INFO, "block state ${ctx.blockState}")
|
||||
detailLogger.log(Level.INFO, " model ${ctx.modelLocation}")
|
||||
detailLogger.log(Level.INFO, " class ${ctx.blockState.block.javaClass.name} matches ${matchClass.name}")
|
||||
|
||||
modelTextures
|
||||
.filter { matcher -> ctx.bakery.modelDerivesFrom(model, ctx.modelLocation, matcher.modelLocation) }
|
||||
.forEach { match ->
|
||||
detailLogger.log(Level.INFO, " model $model matches ${match.modelLocation}")
|
||||
|
||||
val materials = match.textureNames.map { it to model.resolveSprite(it) }
|
||||
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.textureId}" })
|
||||
detailLogger.log(Level.INFO, " sprites [$texMapString]")
|
||||
|
||||
if (materials.all { it.second.textureId != MissingSprite.getMissingSpriteId() }) {
|
||||
// found a valid model (all required textures exist)
|
||||
processModel(ctx, materials.map { it.second.textureId })
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// net.minecraft.client.render.model.json.JsonUnbakedModel.parentId
|
||||
val JsonUnbakedModel_parentId = YarnHelper.requiredField<Identifier>("net.minecraft.class_793", "field_4247", "Lnet/minecraft/class_2960;")
|
||||
|
||||
fun ModelLoader.modelDerivesFrom(model: JsonUnbakedModel, location: Identifier, target: Identifier): Boolean =
|
||||
if (location == target) true
|
||||
else model[JsonUnbakedModel_parentId]
|
||||
?.let { getOrLoadModel(it) as? JsonUnbakedModel }
|
||||
?.let { parent -> modelDerivesFrom(parent, model[JsonUnbakedModel_parentId]!!, target) }
|
||||
?: false
|
||||
@@ -1,76 +0,0 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.block.BlockModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.ModelLoader
|
||||
import net.minecraft.client.render.model.UnbakedModel
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel
|
||||
import net.minecraft.client.render.model.json.ModelVariant
|
||||
import net.minecraft.client.render.model.json.WeightedUnbakedModel
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.registry.Registry
|
||||
import java.util.function.Consumer
|
||||
|
||||
typealias RenderKeyFactory = (SpriteAtlasTexture)->BlockRenderKey
|
||||
|
||||
interface BlockRenderKey {
|
||||
fun replace(model: BakedModel, state: BlockState): BakedModel = model
|
||||
}
|
||||
|
||||
fun ModelLoader.iterateModels(func: (ModelDiscoveryContext)->Unit) {
|
||||
Registry.BLOCK.flatMap { block ->
|
||||
block.stateFactory.states.map { state -> state to BlockModels.getModelId(state) }
|
||||
}.forEach { (state, stateModelResource) ->
|
||||
func(ModelDiscoveryContext(this, state, stateModelResource))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about a single [BlockState] and all the [UnbakedModel]s it could render as.
|
||||
*/
|
||||
class ModelDiscoveryContext(
|
||||
loader: ModelLoader,
|
||||
val state: BlockState,
|
||||
val modelId: ModelIdentifier
|
||||
) {
|
||||
val models = loader.unwrapVariants(loader.getOrLoadModel(modelId) to modelId)
|
||||
.filter { it.second != loader.getOrLoadModel(ModelLoader.MISSING) }
|
||||
|
||||
fun ModelLoader.unwrapVariants(modelAndLoc: Pair<UnbakedModel, Identifier>): List<Pair<UnbakedModel, Identifier>> = when(val model = modelAndLoc.first) {
|
||||
is WeightedUnbakedModel -> (model.variants as List<ModelVariant>).flatMap {
|
||||
variant -> unwrapVariants(getOrLoadModel(variant.location) to variant.location)
|
||||
}
|
||||
is JsonUnbakedModel -> listOf(modelAndLoc)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
interface ModelDiscovery {
|
||||
fun discover(loader: ModelLoader, atlas: Consumer<Identifier>): Map<BlockState, BlockRenderKey>
|
||||
}
|
||||
|
||||
abstract class ModelDiscoveryBase : ModelDiscovery, HasLogger {
|
||||
override fun discover(loader: ModelLoader, atlas: Consumer<Identifier>): Map<BlockState, BlockRenderKey> {
|
||||
val keys = mutableMapOf<BlockState, BlockRenderKey>()
|
||||
var errors = 0
|
||||
|
||||
loader.iterateModels { ctx ->
|
||||
try {
|
||||
val result = processModel(ctx, atlas)
|
||||
result?.let { keys[ctx.state] = it }
|
||||
} catch (e: Exception) {
|
||||
errors++
|
||||
}
|
||||
}
|
||||
log("${keys.size} BlockStates discovered, $errors errors")
|
||||
return keys
|
||||
}
|
||||
|
||||
abstract fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>): BlockRenderKey?
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.model.WeightedModelWrapper
|
||||
import mods.betterfoliage.model.WrappedBakedModel
|
||||
import mods.betterfoliage.model.WrappedMeshModel
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.json.WeightedUnbakedModel
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.Level.WARN
|
||||
|
||||
class WeightedUnbakedKey(
|
||||
val replacements: Map<Identifier, ModelBakingKey>
|
||||
) : ModelBakingKey {
|
||||
|
||||
override fun bake(ctx: ModelBakingContext): BakedModel? {
|
||||
val unbaked = ctx.getUnbaked()
|
||||
if (unbaked !is WeightedUnbakedModel) return super.bake(ctx)
|
||||
|
||||
// bake all variants, replace as needed
|
||||
val bakedModels = unbaked.variants.mapNotNull {
|
||||
val variantCtx = ctx.copy(location = it.location, transform = it)
|
||||
val replacement = replacements[it.location]
|
||||
val baked = replacement?.let { replacement ->
|
||||
ctx.logger.log(INFO, "Baking replacement for variant [${variantCtx.getUnbaked()::class.java.simpleName}] ${variantCtx.location} -> $replacement")
|
||||
replacement.bake(variantCtx)
|
||||
} ?: variantCtx.getBaked()
|
||||
when(baked) {
|
||||
is WrappedBakedModel -> it to baked
|
||||
// just in case we replaced some variants in the list, but not others
|
||||
// this should not realistically happen, this is just a best-effort fallback
|
||||
is BasicBakedModel -> it to WrappedMeshModel.converter(
|
||||
state = null, unshade = false, noDiffuse = true, blendModeOverride = BlendMode.CUTOUT_MIPPED
|
||||
).convert(baked)!!
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
// something fishy going on, possibly unknown model type
|
||||
// let it through unchanged
|
||||
if (bakedModels.isEmpty()) return super.bake(ctx)
|
||||
|
||||
if (bakedModels.size < unbaked.variants.size) {
|
||||
detailLogger.log(
|
||||
WARN,
|
||||
"Dropped ${unbaked.variants.size - bakedModels.size} variants from model ${ctx.location}"
|
||||
)
|
||||
}
|
||||
val weightedSpecials = bakedModels.map { (variant, model) ->
|
||||
WeightedModelWrapper.WeightedModel(model, variant.weight)
|
||||
}
|
||||
return WeightedModelWrapper(weightedSpecials, weightedSpecials[0].model)
|
||||
}
|
||||
|
||||
override fun toString() = "[WeightedUnbakedKey, ${replacements.size} replacements]"
|
||||
|
||||
companion object : HasLogger()
|
||||
}
|
||||
@@ -13,7 +13,7 @@ data class CenteredSprite(val sprite: Identifier, val atlas: Atlas = Atlas.BLOCK
|
||||
fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw)
|
||||
|
||||
fun draw(resourceManager: ResourceManager): ByteArray {
|
||||
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
|
||||
val baseTexture = resourceManager.loadSprite(atlas.file(sprite))
|
||||
|
||||
val frameWidth = baseTexture.width
|
||||
val frameHeight = baseTexture.width * aspectHeight / aspectWidth
|
||||
|
||||
@@ -3,23 +3,20 @@ package mods.betterfoliage.resource.generated
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener
|
||||
import net.minecraft.client.resource.ClientResourcePackContainer
|
||||
import net.minecraft.resource.*
|
||||
import net.minecraft.resource.ResourcePackContainer.InsertionPosition
|
||||
import net.minecraft.resource.ResourceType.CLIENT_RESOURCES
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader
|
||||
import net.minecraft.text.LiteralText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import org.apache.logging.log4j.Logger
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import java.io.IOException
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Predicate
|
||||
import java.util.function.Supplier
|
||||
|
||||
/**
|
||||
* [ResourcePack] containing generated block textures
|
||||
@@ -30,13 +27,16 @@ import java.util.function.Supplier
|
||||
* @param[packDesc] Description of pack
|
||||
* @param[logger] Logger to log to when generating resources
|
||||
*/
|
||||
class GeneratedBlockTexturePack(val reloadId: Identifier, val nameSpace: String, val packName: String, val packDesc: String, override val logger: Logger) : HasLogger, ResourcePack, IdentifiableResourceReloadListener {
|
||||
class GeneratedBlockTexturePack(
|
||||
val reloadId: Identifier, val nameSpace: String, val packName: String, val packDesc: String
|
||||
) : HasLogger(), ResourcePack {
|
||||
|
||||
override fun getName() = reloadId.toString()
|
||||
override fun getNamespaces(type: ResourceType) = setOf(nameSpace)
|
||||
override fun <T : Any?> parseMetadata(deserializer: ResourceMetadataReader<T>) = null
|
||||
override fun openRoot(id: String) = null
|
||||
override fun findResources(type: ResourceType, path: String, maxDepth: Int, filter: Predicate<String>) = emptyList<Identifier>()
|
||||
override fun findResources(type: ResourceType, path: String, prefix: String, maxDepth: Int, filter: Predicate<String>) = emptyList<Identifier>()
|
||||
|
||||
override fun close() {}
|
||||
|
||||
protected var manager: ResourceManager? = null
|
||||
@@ -51,8 +51,8 @@ class GeneratedBlockTexturePack(val reloadId: Identifier, val nameSpace: String,
|
||||
val resource = func(manager!!)
|
||||
|
||||
identifiers[key] = id
|
||||
resources[Atlas.BLOCKS.wrap(id)] = resource
|
||||
log("generated resource $key -> $id")
|
||||
resources[Atlas.BLOCKS.file(id)] = resource
|
||||
detailLogger.log(INFO, "generated resource $key -> $id")
|
||||
return id
|
||||
}
|
||||
|
||||
@@ -64,29 +64,32 @@ class GeneratedBlockTexturePack(val reloadId: Identifier, val nameSpace: String,
|
||||
override fun contains(type: ResourceType, id: Identifier) =
|
||||
type == CLIENT_RESOURCES && resources.containsKey(id)
|
||||
|
||||
override fun reload(synchronizer: ResourceReloadListener.Synchronizer, manager: ResourceManager, prepareProfiler: Profiler, applyProfiler: Profiler, prepareExecutor: Executor, applyExecutor: Executor): CompletableFuture<Void> {
|
||||
this.manager = manager
|
||||
return synchronizer.whenPrepared(null).thenRun {
|
||||
this.manager = null
|
||||
identifiers.clear()
|
||||
resources.clear()
|
||||
/**
|
||||
* Provider for this resource pack. Adds pack as always-on and hidden.
|
||||
*/
|
||||
val finder = object : ResourcePackProvider {
|
||||
val packInfo = ResourcePackProfile(
|
||||
packName, true, { this@GeneratedBlockTexturePack },
|
||||
LiteralText(packName),
|
||||
LiteralText(packDesc),
|
||||
ResourcePackCompatibility.COMPATIBLE, ResourcePackProfile.InsertionPosition.TOP, true, ResourcePackSource.field_25347
|
||||
)
|
||||
|
||||
override fun register(consumer: Consumer<ResourcePackProfile>, factory: ResourcePackProfile.Factory) {
|
||||
consumer.accept(packInfo)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFabricId() = reloadId
|
||||
val reloader = object : IdentifiableResourceReloadListener {
|
||||
override fun getFabricId() = reloadId
|
||||
|
||||
/**
|
||||
* Supplier for this resource pack. Adds pack as always-on and hidden.
|
||||
*/
|
||||
val finder = object : ResourcePackCreator {
|
||||
val packInfo = ClientResourcePackContainer(
|
||||
packName, true, Supplier { this@GeneratedBlockTexturePack },
|
||||
LiteralText(packName),
|
||||
LiteralText(packDesc),
|
||||
ResourcePackCompatibility.COMPATIBLE, InsertionPosition.TOP, true, null
|
||||
)
|
||||
override fun <T : ResourcePackContainer> registerContainer(nameToPackMap: MutableMap<String, T>, packInfoFactory: ResourcePackContainer.Factory<T>) {
|
||||
(nameToPackMap as MutableMap<String, ResourcePackContainer>)[reloadId.toString()] = packInfo
|
||||
override fun reload(synchronizer: ResourceReloadListener.Synchronizer, manager: ResourceManager, prepareProfiler: Profiler, applyProfiler: Profiler, prepareExecutor: Executor, applyExecutor: Executor): CompletableFuture<Void> {
|
||||
this@GeneratedBlockTexturePack.manager = manager
|
||||
return synchronizer.whenPrepared(null).thenRun {
|
||||
this@GeneratedBlockTexturePack.manager = null
|
||||
identifiers.clear()
|
||||
resources.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ data class GeneratedGrassSprite(val sprite: Identifier, val isSnowed: Boolean, v
|
||||
fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw)
|
||||
|
||||
fun draw(resourceManager: ResourceManager): ByteArray {
|
||||
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
|
||||
val baseTexture = resourceManager.loadSprite(atlas.file(sprite))
|
||||
|
||||
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
val graphics = result.createGraphics()
|
||||
|
||||
@@ -20,7 +20,7 @@ data class GeneratedLeafSprite(val sprite: Identifier, val leafType: String, val
|
||||
fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw)
|
||||
|
||||
fun draw(resourceManager: ResourceManager): ByteArray {
|
||||
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
|
||||
val baseTexture = resourceManager.loadSprite(atlas.file(sprite))
|
||||
|
||||
val size = baseTexture.width
|
||||
val frames = baseTexture.height / size
|
||||
@@ -67,7 +67,7 @@ data class GeneratedLeafSprite(val sprite: Identifier, val leafType: String, val
|
||||
* @param[maxSize] Preferred mask size.
|
||||
*/
|
||||
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size ->
|
||||
Atlas.BLOCKS.wrap(Identifier(BetterFoliage.MOD_ID, "blocks/leafmask_${size}_${type}"))
|
||||
Atlas.BLOCKS.file(Identifier(BetterFoliage.MOD_ID, "blocks/leafmask_${size}_${type}"))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package mods.betterfoliage.resource.model
|
||||
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.get
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockRenderLayer
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BasicBakedModel
|
||||
import net.minecraft.client.render.model.WeightedBakedModel
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.WeightedPicker
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
// net.minecraft.client.render.model.WeightedBakedModel.totalWeight
|
||||
val WeightedBakedModel_totalWeight = YarnHelper.requiredField<Int>("net.minecraft.class_1097", "field_5433", "I")
|
||||
// net.minecraft.client.render.model.WeightedBakedModel.models
|
||||
val WeightedBakedModel_models = YarnHelper.requiredField<List<WeightedPicker.Entry>>("net.minecraft.class_1097", "field_5434", "Ljava/util/List;")
|
||||
// net.minecraft.client.render.model.WeightedBakedModel.ModelEntry.model
|
||||
val WeightedBakedModelEntry_model = YarnHelper.requiredField<BakedModel>("net.minecraft.class_1097\$class_1099", "field_5437", "Lnet/minecraft/class_1087;")
|
||||
// net.minecraft.util.WeightedPicker.Entry.weight
|
||||
val WeightedPickerEntry_weight = YarnHelper.requiredField<Int>("net.minecraft.class_3549\$class_3550", "field_15774", "I")
|
||||
|
||||
abstract class WrappedBakedModel(val wrapped: BakedModel) : BakedModel by wrapped, FabricBakedModel {
|
||||
override fun isVanillaAdapter() = false
|
||||
|
||||
override fun emitItemQuads(stack: ItemStack, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(wrapped as FabricBakedModel).emitItemQuads(stack, randomSupplier, context)
|
||||
}
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedMeshModel(wrapped: BasicBakedModel, val mesh: Mesh) : WrappedBakedModel(wrapped) {
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
context.meshConsumer().accept(mesh)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Converter for [BasicBakedModel] instances.
|
||||
* @param state [BlockState] to use when querying [BakedModel]
|
||||
* @param unshade undo vanilla diffuse lighting when unbaking the [BakedModel]
|
||||
* @param noDiffuse disable diffuse lighting when baking the [Mesh]
|
||||
* @param renderLayerOverride [BlockRenderLayer] to use instead of the one declared by the corresponding [Block]
|
||||
*/
|
||||
fun converter(state: BlockState, unshade: Boolean = false, noDiffuse: Boolean = true, renderLayerOverride: BlockRenderLayer? = null) = BakedModelConverter.of { model, _ ->
|
||||
if (model is BasicBakedModel) {
|
||||
val mesh = unbakeQuads(model, state, Random(42L), unshade).build(
|
||||
layer = renderLayerOverride ?: state.block.renderLayer,
|
||||
noDiffuse = noDiffuse,
|
||||
flatLighting = !model.useAmbientOcclusion()
|
||||
)
|
||||
WrappedMeshModel(model, mesh)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedWeightedModel(wrapped: WeightedBakedModel, transformer: BakedModelConverter) : WrappedBakedModel(wrapped) {
|
||||
val totalWeight = wrapped[WeightedBakedModel_totalWeight] as Int
|
||||
val models = wrapped[WeightedBakedModel_models]!!.map { entry ->
|
||||
Entry(transformer.convert(entry[WeightedBakedModelEntry_model]!!, transformer)!!, entry[WeightedPickerEntry_weight]!!)
|
||||
}
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
(WeightedPicker.getRandom(randomSupplier.get(), models, totalWeight).model as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
|
||||
class Entry(val model: BakedModel, weight: Int) : WeightedPicker.Entry(weight)
|
||||
|
||||
companion object {
|
||||
val converter = object : BakedModelConverter {
|
||||
override fun convert(model: BakedModel, converter: BakedModelConverter) =
|
||||
(model as? WeightedBakedModel)?.let { WrappedWeightedModel(it, converter) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.minecraft.util.math.Direction.Axis.*
|
||||
import net.minecraft.util.math.Direction.AxisDirection
|
||||
import net.minecraft.util.math.Direction.AxisDirection.*
|
||||
import net.minecraft.util.math.Direction.*
|
||||
import net.minecraft.util.math.Quaternion
|
||||
|
||||
// ================================
|
||||
// Axes and directions
|
||||
@@ -52,6 +53,7 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
||||
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
||||
Double3(v1.x * weight1 + v2.x * weight2, v1.y * weight1 + v2.y * weight2, v1.z * weight1 + v2.z * weight2)
|
||||
fun lerp(delta: Double, first: Double3, second: Double3) = first + (second - first) * delta
|
||||
}
|
||||
|
||||
// immutable operations
|
||||
@@ -68,6 +70,13 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||
rot.rotatedComponent(SOUTH, x, y, z)
|
||||
)
|
||||
|
||||
/** Rotate vector by the given [Quaternion] */
|
||||
fun rotate(quat: Quaternion) =
|
||||
quat.copy()
|
||||
.apply { hamiltonProduct(Quaternion(this@Double3.x.toFloat(), this@Double3.y.toFloat(), this@Double3.z.toFloat(), 0.0F)) }
|
||||
.apply { hamiltonProduct(quat.copy().apply(Quaternion::conjugate)) }
|
||||
.let { Double3(it.x, it.y, it.z) }
|
||||
|
||||
// mutable operations
|
||||
fun setTo(other: Double3): Double3 { x = other.x; y = other.y; z = other.z; return this }
|
||||
fun setTo(x: Double, y: Double, z: Double): Double3 { this.x = x; this.y = y; this.z = z; return this }
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import net.minecraft.text.LiteralText
|
||||
import net.minecraft.text.Style
|
||||
import net.minecraft.util.Formatting
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.lang.Math.*
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@@ -63,16 +62,13 @@ fun nextPowerOf2(x: Int): Int {
|
||||
// else -> false
|
||||
//}
|
||||
|
||||
interface HasLogger {
|
||||
val logger: Logger
|
||||
val logName: String get() = this::class.simpleName!!
|
||||
fun log(msg: String) = log(Level.INFO, msg)
|
||||
fun log(level: Level, msg: String) = logger.log(level, "[$logName] $msg")
|
||||
fun log(msg: String, e: Throwable) = log(Level.WARN, msg, e)
|
||||
fun log(level: Level, msg: String, e: Throwable) = logger.log(level, "[$logName] $msg", e)
|
||||
@Suppress("LeakingThis")
|
||||
abstract class HasLogger {
|
||||
val logger = BetterFoliage.logger(this)
|
||||
val detailLogger = BetterFoliage.detailLogger(this)
|
||||
}
|
||||
|
||||
fun textComponent(msg: String, color: Formatting = Formatting.GRAY): LiteralText {
|
||||
val style = Style().apply { this.color = color }
|
||||
val style = Style.EMPTY.withColor(color)
|
||||
return LiteralText(msg).apply { this.style = style }
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import kotlin.random.Random
|
||||
import java.util.Random
|
||||
|
||||
val random = Random(System.nanoTime())
|
||||
|
||||
fun randomB() = random.nextBoolean()
|
||||
fun randomI(min: Int = 0, max: Int = Int.MAX_VALUE) = random.nextInt(min, max)
|
||||
fun randomL(min: Long = 0, max: Long = Long.MAX_VALUE) = random.nextLong(min, max)
|
||||
fun randomF(min: Double = 0.0, max: Double = 1.0) = random.nextDouble(min, max).toFloat()
|
||||
fun randomD(min: Double = 0.0, max: Double = 1.0) = if (min == max) min else random.nextDouble(min, max)
|
||||
fun randomI(min: Int = 0, max: Int = Int.MAX_VALUE) = min + random.nextInt(max - min)
|
||||
fun randomF(min: Float = 0.0f, max: Float = 1.0f) = random.randomF(min, max)
|
||||
fun randomD(min: Double = 0.0, max: Double = 1.0) = random.randomD(min, max)
|
||||
|
||||
fun Random.randomF(min: Float = 0.0f, max: Float = 1.0f) = nextFloat() * (max - min) + min
|
||||
fun Random.randomF(min: Double = 0.0, max: Double = 1.0) = randomF(min.toFloat(), max.toFloat())
|
||||
fun Random.randomD(min: Double = 0.0, max: Double = 1.0) = nextDouble() * (max - min) + min
|
||||
|
||||
fun semiRandom(x: Int, y: Int, z: Int, seed: Int): Int {
|
||||
var value = (x * x + y * y + z * z + x * y + y * z + z * x + (seed * seed))
|
||||
|
||||
@@ -3,6 +3,7 @@ package mods.betterfoliage.util
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.fabricmc.mappings.EntryTriple
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.lang.Exception
|
||||
@@ -16,18 +17,27 @@ fun <T> Any.reflectField(name: String) = getFieldRecursive(this::class.java, nam
|
||||
it.get(this) as T
|
||||
}
|
||||
|
||||
/** Get the field on the class with the given name.
|
||||
* Does not handle overloads, suitable only for unique field names (like Yarn intermediate names)
|
||||
* */
|
||||
fun getFieldRecursive(cls: Class<*>, name: String): Field = try {
|
||||
cls.getDeclaredField(name)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
cls.superclass?.let { getFieldRecursive(it, name) } ?: throw e
|
||||
cls.superclass?.let { getFieldRecursive(it, name) } ?: throw IllegalArgumentException(e)
|
||||
}
|
||||
|
||||
/** Get the method on the class with the given name.
|
||||
* Does not handle overloads, suitable only for unique field names (like Yarn intermediate names)
|
||||
* */
|
||||
fun getMethodRecursive(cls: Class<*>, name: String): Method = try {
|
||||
cls.declaredMethods.find { it.name == name } ?: throw NoSuchMethodException()
|
||||
} catch (e: NoSuchMethodException) {
|
||||
cls.superclass?.let { getMethodRecursive(it, name) } ?: throw e
|
||||
cls.superclass?.let { getMethodRecursive(it, name) } ?: throw IllegalArgumentException(e)
|
||||
}
|
||||
|
||||
fun getAllMethods(className: String, methodName: String): List<Method> =
|
||||
tryDefault(null) { Class.forName(className) }?.declaredMethods?.filter { it.name == methodName }
|
||||
?: emptyList()
|
||||
|
||||
interface FieldRef<T> {
|
||||
val field: Field?
|
||||
@@ -59,14 +69,14 @@ object YarnHelper {
|
||||
val resolver = FabricLoader.getInstance().mappingResolver
|
||||
|
||||
fun <T> requiredField(className: String, fieldName: String, descriptor: String) = Field<T>(false, className, fieldName, descriptor)
|
||||
fun <T> requiredMethod(className: String, methodName: String, descriptor: String, vararg params: String) = Method<T>(false, className, methodName, descriptor)
|
||||
fun <T> requiredMethod(className: String, methodName: String, descriptor: String) = Method<T>(false, className, methodName, descriptor)
|
||||
|
||||
class Field<T>(val optional: Boolean, val className: String, val fieldName: String, descriptor: String) : FieldRef<T> {
|
||||
override val field = FabricLoader.getInstance().mappingResolver.let { resolver ->
|
||||
try {
|
||||
val classMapped = resolver.mapClassName(INTERMEDIARY, className)
|
||||
val fieldMapped = resolver.mapFieldName(INTERMEDIARY, className, fieldName, descriptor)
|
||||
Class.forName(classMapped)?.let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } }
|
||||
Class.forName(classMapped).let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } }
|
||||
} catch (e: Exception) {
|
||||
logger.log(
|
||||
if (optional) Level.DEBUG else Level.ERROR,
|
||||
@@ -82,7 +92,7 @@ object YarnHelper {
|
||||
try {
|
||||
val classMapped = resolver.mapClassName(INTERMEDIARY, className)
|
||||
val methodMapped = resolver.mapMethodName(INTERMEDIARY, className, methodName, descriptor)
|
||||
Class.forName(classMapped)?.let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } }
|
||||
Class.forName(classMapped).let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } }
|
||||
} catch (e: Exception) {
|
||||
logger.log(
|
||||
if (optional) Level.DEBUG else Level.ERROR,
|
||||
@@ -94,8 +104,6 @@ object YarnHelper {
|
||||
}
|
||||
}
|
||||
|
||||
//fun Any.isInstance(cls: ClassRefOld<*>) = cls.isInstance(this)
|
||||
|
||||
interface ReflectionCallable<T> {
|
||||
operator fun invoke(vararg args: Any): T
|
||||
}
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import mods.betterfoliage.resource.model.HSB
|
||||
import mods.betterfoliage.model.Color
|
||||
import mods.betterfoliage.model.HSB
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.resource.Resource
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.atan2
|
||||
|
||||
enum class Atlas(val basePath: String, val resourceId: Identifier) {
|
||||
BLOCKS("textures", SpriteAtlasTexture.BLOCK_ATLAS_TEX),
|
||||
PARTICLES("textures/particle", SpriteAtlasTexture.PARTICLE_ATLAS_TEX);
|
||||
enum class Atlas(val resourceId: Identifier) {
|
||||
BLOCKS(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE),
|
||||
PARTICLES(SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE);
|
||||
|
||||
/** Get the fully-qualified resource name for sprites belonging to this atlas*/
|
||||
fun wrap(resource: Identifier) = Identifier(resource.namespace, "$basePath/${resource.path}.png")
|
||||
|
||||
/** Get the short resource name for sprites belonging to this atlas*/
|
||||
fun unwrap(resource: Identifier) = resource.stripStart("$basePath/").stripEnd(".png")
|
||||
/** Get the fully-qualified resource name for sprites belonging to this atlas */
|
||||
fun file(resource: Identifier) = Identifier(resource.namespace, "textures/${resource.path}.png")
|
||||
|
||||
/** Reference to the atlas itself */
|
||||
val atlas: SpriteAtlasTexture get() = MinecraftClient.getInstance().textureManager.getTexture(resourceId) as SpriteAtlasTexture
|
||||
private val atlas: SpriteAtlasTexture get() = MinecraftClient.getInstance().textureManager.getTexture(resourceId) as SpriteAtlasTexture
|
||||
|
||||
/** Get a sprite from this atlas */
|
||||
operator fun get(location: Identifier) = atlas.getSprite(location)
|
||||
}
|
||||
|
||||
operator fun SpriteAtlasTexture.get(res: Identifier): Sprite? = getSprite(res)
|
||||
@@ -48,15 +52,17 @@ val BufferedImage.bytes: ByteArray get() =
|
||||
* Only non-transparent pixels are considered. Averages are taken in the HSB color space (note: Hue is a circular average),
|
||||
* and the result transformed back to the RGB color space.
|
||||
*/
|
||||
fun ResourceManager.averageImageColorHSB(id: Identifier, atlas: Atlas) = loadSprite(atlas.wrap(id)).let { image ->
|
||||
val Sprite_images = YarnHelper.requiredField<Array<NativeImage>>("net.minecraft.class_1058", "field_5262", "[Lnet/minecraft/class_1011;")
|
||||
|
||||
val Sprite.averageColor: HSB get() {
|
||||
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.height) {
|
||||
val pixel = image.get(x, y)
|
||||
for (x in 0 until width)
|
||||
for (y in 0 until height) {
|
||||
val pixel = this[Sprite_images]!![0].getPixelColor(x, y)
|
||||
val alpha = (pixel shr 24) and 255
|
||||
val hsb = HSB.fromColor(pixel)
|
||||
if (alpha == 255) {
|
||||
@@ -70,7 +76,7 @@ fun ResourceManager.averageImageColorHSB(id: Identifier, atlas: Atlas) = loadSpr
|
||||
|
||||
// circular average - transform sum vector to polar angle
|
||||
val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat()
|
||||
HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat())
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat())
|
||||
}
|
||||
|
||||
/** Weighted blend of 2 packed RGB colors */
|
||||
@@ -82,3 +88,15 @@ fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
||||
val result = ((a shl 24) or (r shl 16) or (g shl 8) or b)
|
||||
return result
|
||||
}
|
||||
|
||||
fun logColorOverride(logger: Logger, threshold: Double, hsb: HSB) {
|
||||
return if (hsb.saturation >= threshold) {
|
||||
logger.log(Level.INFO, " brightness ${hsb.brightness}")
|
||||
logger.log(Level.INFO, " saturation ${hsb.saturation} >= ${threshold}, will use texture color")
|
||||
} else {
|
||||
logger.log(Level.INFO, " saturation ${hsb.saturation} < ${threshold}, will use block color")
|
||||
}
|
||||
}
|
||||
|
||||
fun HSB.colorOverride(threshold: Double) =
|
||||
if (saturation < threshold) null else copy(brightness = (brightness * 2.0f).coerceAtMost(0.9f)).asColor.let { Color(it) }
|
||||
@@ -1,16 +1,24 @@
|
||||
package net.fabricmc.fabric.impl.client.indigo.renderer.render
|
||||
|
||||
import mods.betterfoliage.render.lighting.CustomLightingMeshConsumer
|
||||
import mods.betterfoliage.render.lighting.AbstractQuadRenderer_aoCalc
|
||||
import mods.betterfoliage.render.lighting.AbstractQuadRenderer_blockInfo2
|
||||
import mods.betterfoliage.render.lighting.AbstractQuadRenderer_bufferFunc2
|
||||
import mods.betterfoliage.render.lighting.AbstractQuadRenderer_transform
|
||||
import mods.betterfoliage.render.lighting.CustomLighting
|
||||
import mods.betterfoliage.util.*
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator
|
||||
import mods.betterfoliage.render.lighting.CustomLightingMeshConsumer
|
||||
import mods.betterfoliage.util.YarnHelper
|
||||
import mods.betterfoliage.util.cornerDirFromAo
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.WorldRenderer
|
||||
import net.minecraft.util.math.Direction
|
||||
|
||||
val AoCalculator_computeFace = YarnHelper.requiredMethod<Any>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator", "computeFace",
|
||||
"(Lnet/minecraft/util/math/Direction;Z)Lnet/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData;"
|
||||
"(Lnet/minecraft/util/math/Direction;ZZ)Lnet/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData;"
|
||||
)
|
||||
val AoFaceData_toArray = YarnHelper.requiredMethod<Unit>(
|
||||
"net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoFaceData", "toArray",
|
||||
@@ -18,11 +26,16 @@ val AoFaceData_toArray = YarnHelper.requiredMethod<Unit>(
|
||||
)
|
||||
|
||||
open class ModifiedTerrainMeshConsumer(
|
||||
blockInfo: TerrainBlockRenderInfo,
|
||||
chunkInfo: ChunkRenderInfo,
|
||||
aoCalc: AoCalculator,
|
||||
transform: RenderContext.QuadTransform
|
||||
) : TerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, transform), CustomLightingMeshConsumer {
|
||||
val original: AbstractMeshConsumer
|
||||
) : AbstractMeshConsumer(
|
||||
original[AbstractQuadRenderer_blockInfo2],
|
||||
original[AbstractQuadRenderer_bufferFunc2],
|
||||
original[AbstractQuadRenderer_aoCalc],
|
||||
original[AbstractQuadRenderer_transform]
|
||||
), CustomLightingMeshConsumer {
|
||||
override fun matrix() = original.matrix()
|
||||
override fun normalMatrix() = original.normalMatrix()
|
||||
override fun overlay() = original.overlay()
|
||||
|
||||
/** Custom lighting to use */
|
||||
var lighter: CustomLighting? = null
|
||||
@@ -34,21 +47,23 @@ open class ModifiedTerrainMeshConsumer(
|
||||
|
||||
/** Cached block brightness values for all neighbors */
|
||||
val brNeighbor = IntArray(6)
|
||||
|
||||
/** Cache validity for block brightness values (neighbors + self) */
|
||||
val brValid = Array(7) { false }
|
||||
|
||||
override var brSelf: Int = -1
|
||||
get() { if (brValid[6]) return field else
|
||||
field = blockInfo.blockView.getBlockState(blockInfo.blockPos).getBlockBrightness(blockInfo.blockView, blockInfo.blockPos)
|
||||
brValid[6] = true
|
||||
return field
|
||||
get() {
|
||||
if (brValid[6]) return field else {
|
||||
field = WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockPos)
|
||||
brValid[6] = true
|
||||
return field
|
||||
}
|
||||
}
|
||||
protected set
|
||||
|
||||
override fun brNeighbor(dir: Direction): Int {
|
||||
if (brValid[dir.ordinal]) return brNeighbor[dir.ordinal]
|
||||
blockInfo.blockView.getBlockState(blockInfo.blockPos)
|
||||
.getBlockBrightness(blockInfo.blockView, blockInfo.blockPos + dir.offset)
|
||||
WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockPos + dir.offset)
|
||||
.let { brNeighbor[dir.ordinal] = it; brValid[dir.ordinal] = true; return it }
|
||||
}
|
||||
|
||||
@@ -62,7 +77,12 @@ open class ModifiedTerrainMeshConsumer(
|
||||
|
||||
override fun fillAoData(lightFace: Direction) {
|
||||
if (!aoValid[lightFace.ordinal]) {
|
||||
AoFaceData_toArray.invoke(AoCalculator_computeFace.invoke(aoCalc, lightFace, true), aoFull, lightFull, cornerDirFromAo[lightFace.ordinal])
|
||||
AoFaceData_toArray.invoke(
|
||||
AoCalculator_computeFace.invoke(aoCalc, lightFace, true, false),
|
||||
aoFull,
|
||||
lightFull,
|
||||
cornerDirFromAo[lightFace.ordinal]
|
||||
)
|
||||
aoValid[lightFace.ordinal] = true
|
||||
}
|
||||
}
|
||||
@@ -72,32 +92,23 @@ open class ModifiedTerrainMeshConsumer(
|
||||
aoCalc.light[vIdx] = light
|
||||
}
|
||||
|
||||
override fun applyOffsets(quad: MutableQuadViewImpl) {
|
||||
// Moved farther back in the pipeline, after custom lighting is applied
|
||||
// Might possibly mess emissive multitexturing up, but seems to be OK for now
|
||||
}
|
||||
|
||||
override fun tesselateFlat(q: MutableQuadViewImpl, renderLayer: Int, blockColorIndex: Int) {
|
||||
override fun tesselateFlat(q: MutableQuadViewImpl, renderLayer: RenderLayer, blockColorIndex: Int) {
|
||||
lighter?.applyLighting(this, q, flat = true, emissive = false)
|
||||
super.applyOffsets(q)
|
||||
super.tesselateSmooth(q, renderLayer, blockColorIndex)
|
||||
}
|
||||
|
||||
override fun tesselateFlatEmissive(q: MutableQuadViewImpl, renderLayer: Int, blockColorIndex: Int, lightmaps: IntArray) {
|
||||
override fun tesselateFlatEmissive(q: MutableQuadViewImpl, renderLayer: RenderLayer, blockColorIndex: Int) {
|
||||
lighter?.applyLighting(this, q, flat = true, emissive = true)
|
||||
super.applyOffsets(q)
|
||||
super.tesselateSmoothEmissive(q, renderLayer, blockColorIndex)
|
||||
}
|
||||
|
||||
override fun tesselateSmooth(q: MutableQuadViewImpl, renderLayer: Int, blockColorIndex: Int) {
|
||||
override fun tesselateSmooth(q: MutableQuadViewImpl, renderLayer: RenderLayer, blockColorIndex: Int) {
|
||||
lighter?.applyLighting(this, q, flat = false, emissive = false)
|
||||
super.applyOffsets(q)
|
||||
super.tesselateSmooth(q, renderLayer, blockColorIndex)
|
||||
}
|
||||
|
||||
override fun tesselateSmoothEmissive(q: MutableQuadViewImpl, renderLayer: Int, blockColorIndex: Int) {
|
||||
override fun tesselateSmoothEmissive(q: MutableQuadViewImpl, renderLayer: RenderLayer, blockColorIndex: Int) {
|
||||
lighter?.applyLighting(this, q, flat = false, emissive = true)
|
||||
super.applyOffsets(q)
|
||||
super.tesselateSmoothEmissive(q, renderLayer, blockColorIndex)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,6 @@
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: betterfoliage
|
||||
Specification-Title: BetterFoliage
|
||||
Implementation-Title: BetterFoliage
|
||||
Specification-Vendor: octarine-noise
|
||||
Implementation-Vendor: octarine-noise
|
||||
MixinConnector: mods.betterfoliage.MixinConnector
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.CactusBlock
|
||||
//net.minecraft.block.CactusBlock
|
||||
net.minecraft.class_2266
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// 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
|
||||
//net.minecraft.block.FernBlock
|
||||
net.minecraft.class_2526
|
||||
//net.minecraft.block.CropBlock
|
||||
net.minecraft.class_2302
|
||||
|
||||
//-net.minecraft.block.TallPlantBlock
|
||||
-net.minecraft.class_2320
|
||||
|
||||
// Biomes O'Plenty
|
||||
biomesoplenty.common.block.BlockBOPFlower
|
||||
|
||||
@@ -26,91 +26,11 @@
|
||||
"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.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",
|
||||
@@ -137,8 +57,6 @@
|
||||
"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.connectedGrass.snowEnabled": "Enable under snow",
|
||||
"betterfoliage.connectedGrass.snowEnabled.tooltip": "Enable on snowed grass blocks?",
|
||||
|
||||
@@ -22,67 +22,8 @@
|
||||
"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.saturationThreshold": "채도 임계값",
|
||||
"betterfoliage.saturationThreshold.tooltip": "(특정 색상을 사용하여)\"무채색\"블록과 (바이옴 색을 사용하여)\"화려한\"블록 사이의 채도 차단",
|
||||
|
||||
"betterfoliage.leaves": "잎 추가",
|
||||
"betterfoliage.leaves.tooltip": "둥글게 나뭇잎을 추가시켜줍니다.",
|
||||
@@ -99,8 +40,6 @@
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "잔디 블록에 있는 기능을 활성화 시키겠습니까?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "눈 활성화",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "잔디 블록위에 있는 눈을 활성화 시키겠습니까?",
|
||||
"betterfoliage.shortGrass.saturationThreshold": "채도 임계값",
|
||||
"betterfoliage.shortGrass.saturationThreshold.tooltip": "(특정 색상을 사용하여)\"무채색\"블록과 (바이옴 색을 사용하여)\"화려한\"블록 사이의 채도 차단",
|
||||
|
||||
"betterfoliage.hangingGrass": "매달려있는 잔디",
|
||||
"betterfoliage.hangingGrass.tooltip": "잔디 블록 상단 가장자리에서 아래로 매달려 있는 잔디 다발",
|
||||
|
||||
@@ -22,67 +22,8 @@
|
||||
"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.saturationThreshold": "Порог насыщения",
|
||||
"betterfoliage.saturationThreshold.tooltip": "Насыщенность цвета разделяется на: \"обесцвеченные\" блоки (используя цвет биома) и \"цветные\" блоки (используя их собственный цвет)",
|
||||
|
||||
"betterfoliage.leaves": "Улучшенная листва",
|
||||
"betterfoliage.leaves.tooltip": "Дополнительное округление листьев на блоках листвы.",
|
||||
@@ -99,8 +40,6 @@
|
||||
"betterfoliage.shortGrass.grassEnabled.tooltip": "Включить эту особенность для блоков травы?",
|
||||
"betterfoliage.shortGrass.snowEnabled": "Включить траву под снегом",
|
||||
"betterfoliage.shortGrass.snowEnabled.tooltip": "Включить эту особенность для заснеженных блоков травы?",
|
||||
"betterfoliage.shortGrass.saturationThreshold": "Порог насыщения",
|
||||
"betterfoliage.shortGrass.saturationThreshold.tooltip": "Насыщенность цвета разделяется на: \"обесцвеченные\" блоки (используя цвет биома) и \"цветные\" блоки (используя их собственный цвет)",
|
||||
|
||||
"betterfoliage.hangingGrass": "Висячая трава",
|
||||
"betterfoliage.hangingGrass.tooltip": "Пучки травы свисают вниз с верхних краев блока травы.",
|
||||
|
||||
175
src/main/resources/assets/betterfoliage/lang/zh_cn.json
Normal file
175
src/main/resources/assets/betterfoliage/lang/zh_cn.json
Normal file
@@ -0,0 +1,175 @@
|
||||
{
|
||||
"key.betterfoliage.gui": "打开BF设置",
|
||||
|
||||
"betterfoliage.global.enabled": "模组使用",
|
||||
"betterfoliage.global.enabled.tooltip": "如果关闭,BetterFoliage不会呈现任何东西",
|
||||
"betterfoliage.global.nVidia": "nVidia GPU",
|
||||
"betterfoliage.global.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.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,8 +1,6 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.BlockLilyPad
|
||||
//net.minecraft.block.LilyPadBlock
|
||||
net.minecraft.class_2553
|
||||
|
||||
// Biomes O'Plenty
|
||||
biomesoplenty.common.block.BlockBOPLilypad
|
||||
|
||||
// TerraFirmaCraft
|
||||
com.bioxx.tfc.Blocks.Vanilla.BlockCustomLilyPad
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.LogBlock
|
||||
//net.minecraft.block.PillarBlock
|
||||
net.minecraft.class_2465
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Vanilla
|
||||
block/column_side,side,end
|
||||
block/cube_column,side,end
|
||||
block/cube_column_horizontal,side,end
|
||||
block/cube_all,all,all
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.MyceliumBlock
|
||||
//net.minecraft.block.MyceliumBlock
|
||||
net.minecraft.class_2418
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.NetherrackBlock
|
||||
@@ -1,5 +1,3 @@
|
||||
// Vanilla
|
||||
net.minecraft.block.BlockSand
|
||||
|
||||
// TerraFirmaCraft
|
||||
com.bioxx.tfc.Blocks.Terrain.BlockSand
|
||||
//net.minecraft.block.SandBlock
|
||||
net.minecraft.class_2468
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"refmap": "betterfoliage-refmap.json",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"minVersion": "0.8-SNAPSHOT",
|
||||
"plugin": "mods.betterfoliage.MixinConfigPlugin",
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
@@ -13,7 +14,9 @@
|
||||
"MixinClientWorld",
|
||||
"MixinClientChunkManager",
|
||||
"MixinClientChunkManagerChunkMap",
|
||||
"MixinModelLoader"
|
||||
"MixinModelLoader",
|
||||
"MixinModelLoaderVanilla",
|
||||
"MixinModelLoaderOptifine"
|
||||
],
|
||||
"server": [
|
||||
],
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 682 B |
@@ -39,9 +39,9 @@
|
||||
],
|
||||
|
||||
"depends": {
|
||||
"fabricloader": ">=0.7.3",
|
||||
"fabricloader": ">=0.11.3",
|
||||
"fabric": "*",
|
||||
"fabric-language-kotlin": "*",
|
||||
"minecraft": "1.14.4"
|
||||
"minecraft": "1.16.5"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user