From 29ab5442697412da996f49d185b5c5ce01910aed Mon Sep 17 00:00:00 2001 From: octarine-noise Date: Tue, 6 Jul 2021 00:06:11 +0200 Subject: [PATCH] Multi-layer rendering support --- .../mixin/MixinChunkRendererDispatcher.java | 42 +++++++++ .../kotlin/mods/betterfoliage/CommonRefs.kt | 7 ++ .../mods/betterfoliage/chunk/BlockContext.kt | 2 + .../mods/betterfoliage/model/HalfBaked.kt | 27 ++++-- .../model/SpecialRenderModels.kt | 39 ++++++++- .../mods/betterfoliage/model/TuftMeshes.kt | 1 - .../render/block/vanilla/Cactus.kt | 37 +++++--- .../render/block/vanilla/Dirt.kt | 87 ++++++++++++------- .../render/block/vanilla/Grass.kt | 61 ++++++++++--- .../render/block/vanilla/Leaf.kt | 7 +- .../render/block/vanilla/Lilypad.kt | 33 +++++-- .../render/block/vanilla/Mycelium.kt | 36 ++++++-- .../render/block/vanilla/Netherrack.kt | 39 ++++++--- .../render/block/vanilla/Sand.kt | 45 ++++++++-- .../render/column/ColumnModel.kt | 11 +-- .../betterfoliage/render/pipeline/Layers.kt | 42 +++++++++ .../render/pipeline/RenderCtxBase.kt | 30 +++++-- .../render/pipeline/RenderCtxForge.kt | 42 ++++++--- .../render/pipeline/RenderCtxVanilla.kt | 25 +++--- .../mods/betterfoliage/util/Collections.kt | 3 + .../kotlin/mods/betterfoliage/util/Misc.kt | 8 ++ .../betterfoliage.common.mixins.json | 1 + 22 files changed, 479 insertions(+), 146 deletions(-) create mode 100644 src/main/java/mods/betterfoliage/mixin/MixinChunkRendererDispatcher.java create mode 100644 src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt diff --git a/src/main/java/mods/betterfoliage/mixin/MixinChunkRendererDispatcher.java b/src/main/java/mods/betterfoliage/mixin/MixinChunkRendererDispatcher.java new file mode 100644 index 0000000..4140eb2 --- /dev/null +++ b/src/main/java/mods/betterfoliage/mixin/MixinChunkRendererDispatcher.java @@ -0,0 +1,42 @@ +package mods.betterfoliage.mixin; + +import com.mojang.blaze3d.matrix.MatrixStack; +import mods.betterfoliage.render.pipeline.RenderCtxBase; +import mods.betterfoliage.render.pipeline.RenderCtxForge; +import mods.betterfoliage.render.pipeline.RenderCtxVanilla; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.RegionRenderCacheBuilder; +import net.minecraft.client.renderer.chunk.ChunkRenderCache; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.client.renderer.chunk.VisGraph; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Iterator; +import java.util.Random; +import java.util.Set; + +@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask") +public class MixinChunkRendererDispatcher { + + private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;"; + private static final String getBlockState = "Lnet/minecraft/client/renderer/chunk/ChunkRenderCache;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"; + + @Inject(method = compile, at = @At(value = "INVOKE", target = getBlockState), locals = LocalCapture.CAPTURE_FAILHARD) + void onStartBlockRender( + float p_228940_1_, float p_228940_2_, float p_228940_3_, + ChunkRenderDispatcher.CompiledChunk p_228940_4_, RegionRenderCacheBuilder p_228940_5_, + CallbackInfoReturnable ci, + int i, BlockPos blockpos, BlockPos blockpos1, VisGraph visgraph, Set set, + ChunkRenderCache chunkrendercache, MatrixStack matrixstack, + Random random, + BlockRendererDispatcher blockrendererdispatcher, Iterator var15, + BlockPos blockpos2) { + RenderCtxBase.reset(chunkrendercache, blockrendererdispatcher, blockpos2, random); + } +} diff --git a/src/main/kotlin/mods/betterfoliage/CommonRefs.kt b/src/main/kotlin/mods/betterfoliage/CommonRefs.kt index ecdf015..31bd6f1 100644 --- a/src/main/kotlin/mods/betterfoliage/CommonRefs.kt +++ b/src/main/kotlin/mods/betterfoliage/CommonRefs.kt @@ -10,6 +10,7 @@ import net.minecraft.block.BlockState import net.minecraft.client.renderer.BlockModelRenderer import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.BufferBuilder +import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.chunk.ChunkRenderCache import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.model.IUnbakedModel @@ -21,7 +22,9 @@ import net.minecraft.world.IBlockDisplayReader import net.minecraft.world.IBlockReader import net.minecraftforge.client.model.pipeline.BlockInfo import net.minecraftforge.client.model.pipeline.VertexLighterFlat +import net.minecraftforge.registries.IRegistryDelegate import java.util.Random +import java.util.function.Predicate typealias Sprite = TextureAtlasSprite @@ -61,6 +64,10 @@ object ModelBakery : ClassRef("net.minecraft.client.renderer.model. val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable()) } +object RenderTypeLookup : ClassRef("net.minecraft.client.renderer.RenderTypeLookup") { + val blockRenderChecks = FieldRef(this, "blockRenderChecks", mapRefMutable, Predicate>()) +} + // Optifine val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer") val BlockPosM = ClassRef("net.optifine.BlockPosM") diff --git a/src/main/kotlin/mods/betterfoliage/chunk/BlockContext.kt b/src/main/kotlin/mods/betterfoliage/chunk/BlockContext.kt index eab7fc6..3e29f29 100644 --- a/src/main/kotlin/mods/betterfoliage/chunk/BlockContext.kt +++ b/src/main/kotlin/mods/betterfoliage/chunk/BlockContext.kt @@ -22,6 +22,8 @@ interface BlockCtx { val world: IBlockDisplayReader val pos: BlockPos + val seed: Long get() = state.getSeed(pos) + fun offset(dir: Direction) = offset(dir.offset) fun offset(offset: Int3): BlockCtx diff --git a/src/main/kotlin/mods/betterfoliage/model/HalfBaked.kt b/src/main/kotlin/mods/betterfoliage/model/HalfBaked.kt index ef6f109..806294a 100644 --- a/src/main/kotlin/mods/betterfoliage/model/HalfBaked.kt +++ b/src/main/kotlin/mods/betterfoliage/model/HalfBaked.kt @@ -1,12 +1,19 @@ package mods.betterfoliage.model +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.WrappedLayerPredicate +import mods.betterfoliage.render.pipeline.layerPredicate import mods.betterfoliage.resource.discovery.ModelBakingContext import mods.betterfoliage.resource.discovery.ModelBakingKey import mods.betterfoliage.util.Double3 import mods.betterfoliage.util.HasLogger import mods.betterfoliage.util.directionsAndNull import mods.betterfoliage.util.mapArray +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.model.IBakedModel import net.minecraft.client.renderer.model.SimpleBakedModel @@ -27,15 +34,23 @@ data class HalfBakedQuad( open class HalfBakedSimpleModelWrapper(baseModel: SimpleBakedModel): IBakedModel by baseModel, SpecialRenderModel { val baseQuads = baseModel.unbakeQuads() - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - ctx.renderQuads(baseQuads) + override fun prepare(ctx: BlockCtx, random: Random) = Unit + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + // if the passed data is a BlockState, render on the same layer(s) as that block + val testState = (data as? BlockState) ?: ctx.state + + // this could get called for more layers than the underlying model is on + // ignore extra decoration layers + val shouldRender = when(val predicate = testState.block.layerPredicate) { + is WrappedLayerPredicate -> predicate.original.test(layer) + else -> RenderTypeLookup.canRenderInLayer(testState, layer) + } + if (shouldRender) ctx.renderQuads(baseQuads) } } -open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): IBakedModel by baseModel, SpecialRenderModel { - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - baseModel.render(ctx, noDecorations) - } +open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): SpecialRenderModel by baseModel { } abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() { diff --git a/src/main/kotlin/mods/betterfoliage/model/SpecialRenderModels.kt b/src/main/kotlin/mods/betterfoliage/model/SpecialRenderModels.kt index a0e67b3..91c2c09 100644 --- a/src/main/kotlin/mods/betterfoliage/model/SpecialRenderModels.kt +++ b/src/main/kotlin/mods/betterfoliage/model/SpecialRenderModels.kt @@ -1,7 +1,10 @@ package mods.betterfoliage.model +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.render.pipeline.RenderCtxBase +import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.model.IBakedModel +import net.minecraft.client.renderer.model.WeightedBakedModel import net.minecraft.util.WeightedRandom import java.util.Random @@ -9,18 +12,46 @@ import java.util.Random * Model that makes use of advanced rendering features. */ interface SpecialRenderModel : IBakedModel { - fun render(ctx: RenderCtxBase, noDecorations: Boolean = false) + /** + * Create custom renderdata object. Called once per block. Result is passed to renderLayer(). + */ + fun prepare(ctx: BlockCtx, random: Random): Any + fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) + + /** + * Get the actual model that will be rendered. Useful for container models (like [WeightedBakedModel]). + */ + fun resolve(random: Random) = this +} + +interface SpecialRenderData { + fun canRenderInLayer(layer: RenderType) = false } class WeightedModelWrapper( val models: List, baseModel: SpecialRenderModel -): IBakedModel by baseModel, SpecialRenderModel { +) : IBakedModel by baseModel, SpecialRenderModel { class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight) + val totalWeight = models.sumBy { it.weight } fun getModel(random: Random) = WeightedRandom.getWeightedItem(models, random.nextInt(totalWeight)) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - getModel(ctx.random).model.render(ctx, noDecorations) + override fun resolve(random: Random) = getModel(random).model.resolve(random) + + override fun prepare(ctx: BlockCtx, random: Random) = getModel(random).model.let { actual -> + WeightedRenderData(actual, actual.prepare(ctx, random)) + } + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) = when (data) { + is WeightedRenderData -> data.model.renderLayer(ctx, data.modelRenderData, layer) + else -> getModel(ctx.random).model.renderLayer(ctx, data, layer) } } + +data class WeightedRenderData( + val model: SpecialRenderModel, + val modelRenderData: Any +) : SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = (modelRenderData as? SpecialRenderData)?.canRenderInLayer(layer) ?: false +} \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/model/TuftMeshes.kt b/src/main/kotlin/mods/betterfoliage/model/TuftMeshes.kt index 4e9f9e7..4109ab3 100644 --- a/src/main/kotlin/mods/betterfoliage/model/TuftMeshes.kt +++ b/src/main/kotlin/mods/betterfoliage/model/TuftMeshes.kt @@ -18,7 +18,6 @@ import kotlin.math.sin fun xzDisk(modelIdx: Int) = (PI2 * modelIdx.toDouble() / 64.0).let { Double3(cos(it), 0.0, sin(it)) } - data class TuftShapeKey( val size: Double, val height: Double, diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Cactus.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Cactus.kt index c188974..fe5748e 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Cactus.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Cactus.kt @@ -1,11 +1,13 @@ package mods.betterfoliage.render.block.vanilla -import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.CACTUS_BLOCKS import mods.betterfoliage.config.Config import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.buildTufts @@ -24,14 +26,15 @@ import mods.betterfoliage.resource.discovery.ModelDiscoveryContext import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.Rotation -import mods.betterfoliage.util.get import mods.betterfoliage.util.horizontalDirections +import mods.betterfoliage.util.idx import mods.betterfoliage.util.randomD import mods.betterfoliage.util.randomI -import net.minecraft.block.Blocks +import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.model.BlockModel import net.minecraft.util.Direction.DOWN import net.minecraft.util.ResourceLocation +import java.util.Random object StandardCactusDiscovery : AbstractModelDiscovery() { override fun processModel(ctx: ModelDiscoveryContext) { @@ -49,22 +52,30 @@ object StandardCactusKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped) } +class CactusRenderData(val armSide: Int, val armIdx: Int, val crossIdx: Int) + class StandardCactusModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { + override fun prepare(ctx: BlockCtx, random: Random): Any = when { + !Config.enabled || !Config.cactus.enabled -> Unit + else -> CactusRenderData( + armSide = random.nextInt() and 3, + armIdx = random.idx(cactusArmModels), + crossIdx = random.idx(cactusCrossModels) + ) + } val armLighting = horizontalDirections.map { LightingPreferredFace(it) }.toTypedArray() - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - ctx.checkSides = false - super.render(ctx, noDecorations) - if (!Config.enabled || !Config.cactus.enabled) return - - val armSide = ctx.random.nextInt() and 3 - ctx.vertexLighter = armLighting[armSide] - ctx.renderQuads(cactusArmModels[armSide][ctx.random]) - ctx.vertexLighter = RoundLeafLighting - ctx.renderQuads(cactusCrossModels[ctx.random]) + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + super.renderLayer(ctx, data, layer) + if (data is CactusRenderData) { + ctx.vertexLighter = armLighting[data.armSide] + ctx.renderQuads(cactusArmModels[data.armSide][data.armIdx]) + ctx.vertexLighter = RoundLeafLighting + ctx.renderQuads(cactusCrossModels[data.crossIdx]) + } } companion object { diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt index 27e2c0d..0f24980 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt @@ -2,6 +2,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.Config import mods.betterfoliage.config.DIRT_BLOCKS import mods.betterfoliage.config.SALTWATER_BIOMES @@ -9,13 +10,16 @@ import mods.betterfoliage.config.isSnow import mods.betterfoliage.integration.ShadersModIntegration import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.render.lighting.LightingPreferredFace +import mods.betterfoliage.render.pipeline.Layers import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.extendLayers import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.ModelBakingContext @@ -25,27 +29,25 @@ import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Int3 import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.get +import mods.betterfoliage.util.getBlockModel +import mods.betterfoliage.util.idxOrNull import mods.betterfoliage.util.offset import mods.betterfoliage.util.randomI import net.minecraft.block.material.Material +import net.minecraft.client.Minecraft import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.model.BlockModel import net.minecraft.util.Direction.UP import net.minecraft.util.ResourceLocation +import java.util.Random object StandardDirtDiscovery : AbstractModelDiscovery() { - fun canRenderInLayer(layer: RenderType) = when { - !Config.enabled -> layer == RenderType.solid() - !Config.connectedGrass.enabled && !Config.algae.enabled && !Config.reed.enabled -> layer == RenderType.solid() - else -> layer == RenderType.cutoutMipped() - } - override fun processModel(ctx: ModelDiscoveryContext) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in DIRT_BLOCKS) { BetterFoliage.blockTypes.dirt.add(ctx.blockState) ctx.addReplacement(StandardDirtKey) - RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer) + ctx.blockState.block.extendLayers() } super.processModel(ctx) } @@ -55,48 +57,71 @@ object StandardDirtKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardDirtModel(wrapped) } +class DirtRenderData( + val connectedGrassModel: SpecialRenderModel?, + val algaeIdx: Int?, + val reedIdx: Int? +) : SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = when { + connectedGrassModel != null && layer == Layers.connectedDirt -> true + (algaeIdx != null || reedIdx != null) && layer == Layers.tufts -> true + else -> false + } +} + class StandardDirtModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { val vanillaTuftLighting = LightingPreferredFace(UP) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations) - + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit val stateUp = ctx.state(UP) val state2Up = ctx.state(Int3(0, 2, 0)) val isConnectedGrass = Config.connectedGrass.enabled && stateUp in BetterFoliage.blockTypes.grass && (Config.connectedGrass.snowEnabled || !state2Up.isSnow) - if (isConnectedGrass) { - (ctx.blockModelShapes.getBlockModel(stateUp) as? SpecialRenderModel)?.let { grassModel -> - ctx.renderMasquerade(UP.offset) { - grassModel.render(ctx, true) - } - return - } - return super.render(ctx, false) - } - - super.render(ctx, false) - val isWater = stateUp.material == Material.WATER val isDeepWater = isWater && state2Up.material == Material.WATER val isShallowWater = isWater && state2Up.isAir val isSaltWater = isWater && ctx.biome?.biomeCategory in SALTWATER_BIOMES - if (Config.algae.enabled(ctx.random) && isDeepWater) { - ctx.vertexLighter = vanillaTuftLighting - ShadersModIntegration.grass(ctx, Config.algae.shaderWind) { - ctx.renderQuads(algaeModels[ctx.random]) + return DirtRenderData( + connectedGrassModel = if (isConnectedGrass) (getBlockModel(stateUp) as? SpecialRenderModel)?.resolve(random) else null, + algaeIdx = random.idxOrNull(algaeModels) { Config.algae.enabled(random) && isDeepWater && isSaltWater }, + reedIdx = random.idxOrNull(reedModels) { Config.reed.enabled(random) && isShallowWater && !isSaltWater } + ) + } + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + if (data is DirtRenderData) { + if (data.connectedGrassModel != null) { + if (layer == Layers.connectedDirt) { + ctx.renderMasquerade(UP.offset) { + data.connectedGrassModel.renderLayer(ctx, ctx.state(UP), layer) + } + } + } else { + // render non-connected grass + super.renderLayer(ctx, data, layer) } - } else if (Config.reed.enabled(ctx.random) && isShallowWater && !isSaltWater) { - ctx.vertexLighter = vanillaTuftLighting - ShadersModIntegration.grass(ctx, Config.reed.shaderWind) { - ctx.renderQuads(reedModels[ctx.random]) + + if (layer == Layers.tufts) { + data.algaeIdx?.let { + ctx.vertexLighter = vanillaTuftLighting + ShadersModIntegration.grass(ctx, Config.algae.shaderWind) { + ctx.renderQuads(algaeModels[it]) + } + } + data.reedIdx?.let { + ctx.vertexLighter = vanillaTuftLighting + ShadersModIntegration.grass(ctx, Config.reed.shaderWind) { + ctx.renderQuads(reedModels[it]) + } + } } - } + } else super.renderLayer(ctx, data, layer) } companion object { diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt index bccc6c9..8ed9ad4 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt @@ -2,6 +2,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.BlockConfig import mods.betterfoliage.config.Config import mods.betterfoliage.config.isSnow @@ -9,6 +10,7 @@ import mods.betterfoliage.integration.ShadersModIntegration import mods.betterfoliage.model.Color import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.buildTufts @@ -16,7 +18,9 @@ import mods.betterfoliage.model.fullCubeTextured import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.render.lighting.LightingPreferredFace +import mods.betterfoliage.render.pipeline.Layers import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.extendLayers import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery @@ -28,12 +32,14 @@ import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyMapInvalidatable import mods.betterfoliage.util.averageColor import mods.betterfoliage.util.colorOverride -import mods.betterfoliage.util.get +import mods.betterfoliage.util.idxOrNull import mods.betterfoliage.util.logColorOverride import mods.betterfoliage.util.randomI +import net.minecraft.client.renderer.RenderType import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.UP import net.minecraft.util.ResourceLocation +import java.util.Random object StandardGrassDiscovery : ConfigurableModelDiscovery() { override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks @@ -42,6 +48,7 @@ object StandardGrassDiscovery : ConfigurableModelDiscovery() { override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List) { ctx.addReplacement(StandardGrassKey(textureMatch[0], null)) BetterFoliage.blockTypes.grass.add(ctx.blockState) + ctx.blockState.block.extendLayers() } } @@ -60,6 +67,18 @@ data class StandardGrassKey( } } +class GrassRenderData( + val isSnowed: Boolean, + val connectedGrassIdx: Int?, + val tuftIdx: Int? +): SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = when { + connectedGrassIdx != null && layer == Layers.connectedGrass -> true + tuftIdx != null && layer == Layers.tufts -> true + else -> false + } +} + class StandardGrassModel( wrapped: SpecialRenderModel, key: StandardGrassKey @@ -70,8 +89,8 @@ class StandardGrassModel( val fullBlock by grassFullBlockMeshes.delegate(key) val tuftLighting = LightingPreferredFace(UP) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations) + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit val stateBelow = ctx.state(DOWN) val stateAbove = ctx.state(UP) @@ -81,18 +100,32 @@ class StandardGrassModel( (!isSnowed || Config.connectedGrass.snowEnabled) && BetterFoliage.blockTypes.run { stateBelow in grass || stateBelow in dirt } - if (connected) { - ctx.renderQuads(if (isSnowed) snowFullBlockMeshes[ctx.random] else fullBlock[ctx.random]) - } else { - super.render(ctx, noDecorations) - } - - if (Config.shortGrass.enabled(ctx.random) && Config.shortGrass.grassEnabled && (isAir || isSnowed)) { - ctx.vertexLighter = tuftLighting - ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) { - ctx.renderQuads(if (isSnowed) tuftSnowed[ctx.random] else tuftNormal[ctx.random]) + return GrassRenderData( + isSnowed = isSnowed, + connectedGrassIdx = random.idxOrNull(if (isSnowed) snowFullBlockMeshes else fullBlock) { connected }, + tuftIdx = random.idxOrNull(if (isSnowed) tuftSnowed else tuftNormal) { + Config.shortGrass.enabled(random) && + Config.shortGrass.grassEnabled && + (isAir || isSnowed) } - } + ) + } + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + if (data is GrassRenderData) { + if (data.connectedGrassIdx != null) { + if (layer == Layers.connectedGrass) + ctx.renderQuads((if (data.isSnowed) snowFullBlockMeshes else fullBlock)[data.connectedGrassIdx]) + } else { + super.renderLayer(ctx, data, layer) + } + if (data.tuftIdx != null && layer == Layers.tufts) { + ctx.vertexLighter = tuftLighting + ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) { + ctx.renderQuads((if (data.isSnowed) tuftSnowed else tuftNormal)[data.tuftIdx]) + } + } + } else super.renderLayer(ctx, data, layer) } companion object { diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt index 54f35ed..6f8c83f 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt @@ -30,6 +30,7 @@ import mods.betterfoliage.util.LazyMapInvalidatable import mods.betterfoliage.util.averageColor import mods.betterfoliage.util.colorOverride import mods.betterfoliage.util.logColorOverride +import net.minecraft.client.renderer.RenderType import net.minecraft.util.Direction.UP import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.INFO @@ -72,10 +73,10 @@ class StandardLeafModel( val leafNormal by leafModelsNormal.delegate(key) val leafSnowed by leafModelsSnowed.delegate(key) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { ShadersModIntegration.leaves(ctx, true) { - super.render(ctx, noDecorations) - if (!Config.enabled || !Config.leaves.enabled || noDecorations) return + super.renderLayer(ctx, data, layer) + if (!Config.enabled || !Config.leaves.enabled) return ctx.vertexLighter = RoundLeafLightingPreferUp val leafIdx = ctx.random.nextInt(64) diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt index 36099b3..9995357 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt @@ -1,6 +1,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.Config import mods.betterfoliage.config.LILYPAD_BLOCKS import mods.betterfoliage.integration.ShadersModIntegration @@ -21,12 +22,16 @@ import mods.betterfoliage.resource.discovery.ModelDiscoveryContext import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.get +import mods.betterfoliage.util.idx +import mods.betterfoliage.util.idxOrNull import net.minecraft.block.BlockState import net.minecraft.block.Blocks +import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.util.Direction.DOWN import net.minecraft.util.ResourceLocation +import java.util.Random object StandardLilypadDiscovery : AbstractModelDiscovery() { override fun processModel(ctx: ModelDiscoveryContext) { @@ -41,18 +46,32 @@ object StandardLilypadKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped) } +class LilypadRenderData( + val rootIdx: Int, + val flowerIdx: Int? +) + class StandardLilypadModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - ctx.checkSides = false - super.render(ctx, noDecorations) - if (!Config.enabled || !Config.lilypad.enabled) return - ShadersModIntegration.grass(ctx, Config.lilypad.shaderWind) { - ctx.renderQuads(lilypadRootModels[ctx.random]) + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit + return LilypadRenderData( + rootIdx = random.idx(lilypadRootModels), + flowerIdx = random.idxOrNull(lilypadFlowerModels) { Config.lilypad.enabled(random) } + ) + } + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + ctx.checkSides = false + super.renderLayer(ctx, data, layer) + if (data is LilypadRenderData) { + data.flowerIdx?.let { ctx.renderQuads(lilypadFlowerModels[it]) } + ShadersModIntegration.grass(ctx, Config.lilypad.shaderWind) { + ctx.renderQuads(lilypadRootModels[data.rootIdx]) + } } - if (Config.lilypad.enabled(ctx.random)) ctx.renderQuads(lilypadFlowerModels[ctx.random]) } companion object { diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt index 679e467..bce09c1 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt @@ -1,17 +1,21 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.Config import mods.betterfoliage.config.MYCELIUM_BLOCKS import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.render.lighting.LightingPreferredFace +import mods.betterfoliage.render.pipeline.Layers import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.extendLayers import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.ModelBakingContext @@ -19,19 +23,20 @@ import mods.betterfoliage.resource.discovery.ModelDiscoveryContext import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.get +import mods.betterfoliage.util.idxOrNull import mods.betterfoliage.util.randomI -import net.minecraft.block.Blocks import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.model.BlockModel import net.minecraft.util.Direction import net.minecraft.util.ResourceLocation +import java.util.Random object StandardMyceliumDiscovery : AbstractModelDiscovery() { override fun processModel(ctx: ModelDiscoveryContext) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in MYCELIUM_BLOCKS) { ctx.addReplacement(StandardMyceliumKey) - RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.cutout()) + ctx.blockState.block.extendLayers() } super.processModel(ctx) } @@ -41,21 +46,34 @@ object StandardMyceliumKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardMyceliumModel(wrapped) } +class MyceliumRenderData( + val tuftIndex: Int? +) : SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts +} + class StandardMyceliumModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { val tuftLighting = LightingPreferredFace(Direction.UP) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - super.render(ctx, noDecorations) + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit + return MyceliumRenderData( + random.idxOrNull(myceliumTuftModels) { + Config.shortGrass.enabled(random) && + Config.shortGrass.myceliumEnabled && + ctx.state(Direction.UP).isAir(ctx.world, ctx.pos) + } + ) + } - if (Config.shortGrass.enabled(ctx.random) && - Config.shortGrass.myceliumEnabled && - ctx.state(Direction.UP).isAir(ctx.world, ctx.pos) - ) { + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + super.renderLayer(ctx, data, layer) + if (data is MyceliumRenderData && data.tuftIndex != null && layer == Layers.tufts) { ctx.vertexLighter = tuftLighting - ctx.renderQuads(myceliumTuftModels[ctx.random]) + ctx.renderQuads(myceliumTuftModels[data.tuftIndex]) } } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Netherrack.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Netherrack.kt index 3ab10b6..240c6db 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Netherrack.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Netherrack.kt @@ -2,10 +2,12 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.Config import mods.betterfoliage.config.NETHERRACK_BLOCKS import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.buildTufts @@ -13,7 +15,9 @@ import mods.betterfoliage.model.transform import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.render.lighting.LightingPreferredFace +import mods.betterfoliage.render.pipeline.Layers import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.extendLayers import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.ModelBakingContext @@ -22,26 +26,23 @@ import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.Rotation import mods.betterfoliage.util.get +import mods.betterfoliage.util.idxOrNull import mods.betterfoliage.util.randomI import net.minecraft.block.Blocks import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.model.BlockModel +import net.minecraft.util.Direction import net.minecraft.util.Direction.DOWN import net.minecraft.util.ResourceLocation +import java.util.Random object StandardNetherrackDiscovery : AbstractModelDiscovery() { - fun canRenderInLayer(layer: RenderType) = when { - !Config.enabled -> layer == RenderType.solid() - !Config.netherrack.enabled -> layer == RenderType.solid() - else -> layer == RenderType.cutoutMipped() - } - override fun processModel(ctx: ModelDiscoveryContext) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in NETHERRACK_BLOCKS) { BetterFoliage.blockTypes.dirt.add(ctx.blockState) ctx.addReplacement(StandardNetherrackKey) - RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer) + ctx.blockState.block.extendLayers() } super.processModel(ctx) } @@ -51,19 +52,33 @@ object StandardNetherrackKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardNetherrackModel(wrapped) } +class NetherrackRenderData( + val tuftIndex: Int? +): SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = tuftIndex != null && layer == Layers.tufts +} + class StandardNetherrackModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { val tuftLighting = LightingPreferredFace(DOWN) - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - super.render(ctx, noDecorations) - if (!Config.enabled || !Config.netherrack.enabled) return + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit + return NetherrackRenderData( + random.idxOrNull(netherrackTuftModels) { + Config.netherrack.enabled && + ctx.isAir(DOWN) + } + ) + } - if (ctx.isAir(DOWN)) { + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + super.renderLayer(ctx, data, layer) + if (data is NetherrackRenderData && data.tuftIndex != null && layer == Layers.tufts) { ctx.vertexLighter = tuftLighting - ctx.renderQuads(netherrackTuftModels[ctx.random]) + ctx.renderQuads(netherrackTuftModels[data.tuftIndex]) } } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Sand.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Sand.kt index e34996d..139b043 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Sand.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Sand.kt @@ -2,12 +2,14 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.config.Config import mods.betterfoliage.config.SALTWATER_BIOMES import mods.betterfoliage.config.SAND_BLOCKS import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.Quad +import mods.betterfoliage.model.SpecialRenderData import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.bake @@ -16,7 +18,9 @@ import mods.betterfoliage.model.transform import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.render.lighting.LightingPreferredFace +import mods.betterfoliage.render.pipeline.Layers import mods.betterfoliage.render.pipeline.RenderCtxBase +import mods.betterfoliage.render.pipeline.extendLayers import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.ModelBakingContext @@ -26,6 +30,7 @@ import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.Rotation import mods.betterfoliage.util.allDirections import mods.betterfoliage.util.get +import mods.betterfoliage.util.idx import mods.betterfoliage.util.mapArray import mods.betterfoliage.util.randomB import mods.betterfoliage.util.randomD @@ -37,13 +42,14 @@ import net.minecraft.client.renderer.model.BlockModel import net.minecraft.util.Direction import net.minecraft.util.Direction.UP import net.minecraft.util.ResourceLocation +import java.util.Random object StandardSandDiscovery : AbstractModelDiscovery() { override fun processModel(ctx: ModelDiscoveryContext) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in SAND_BLOCKS) { BetterFoliage.blockTypes.dirt.add(ctx.blockState) ctx.addReplacement(StandardSandKey) - RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.cutoutMipped()) + ctx.blockState.block.extendLayers() } super.processModel(ctx) } @@ -53,23 +59,46 @@ object StandardSandKey : HalfBakedWrapperKey() { override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped) } +class SandRenderData( + val crustIdx: Array, + val tuftIdx: Array +): SpecialRenderData { + override fun canRenderInLayer(layer: RenderType) = when { + (crustIdx.any { it != null } || tuftIdx.any { it != null }) && layer == Layers.coral -> true + else -> false + } +} + class StandardSandModel( wrapped: SpecialRenderModel ) : HalfBakedSpecialWrapper(wrapped) { val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) } - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - super.render(ctx, noDecorations) - if (noDecorations || !Config.enabled || !Config.coral.enabled(ctx.random)) return - if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return + override fun prepare(ctx: BlockCtx, random: Random): Any { + if (!Config.enabled) return Unit + if (!Config.coral.enabled(random)) return Unit + if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return Unit - allDirections.filter { ctx.random.nextInt(64) < Config.coral.chance }.forEach { face -> + val crustIdx = Array(6) { null } + val tuftIdx = Array(6) { null } + allDirections.filter { random.nextInt(64) < Config.coral.chance }.forEach { face -> val isWater = ctx.state(face).material == Material.WATER val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER if (isDeepWater) { + crustIdx[face.ordinal] = random.idx(coralCrustModels) + tuftIdx[face.ordinal] = random.idx(coralTuftModels) + } + } + return SandRenderData(crustIdx, tuftIdx) + } + + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + super.renderLayer(ctx, data, layer) + if (data is SandRenderData && layer == Layers.coral) { + for (face in 0 until 6) { ctx.vertexLighter = coralLighting[face] - ctx.renderQuads(coralCrustModels[face][ctx.random]) - ctx.renderQuads(coralTuftModels[face][ctx.random]) + data.crustIdx[face]?.let { ctx.renderQuads(coralCrustModels[face][it]) } + data.tuftIdx[face]?.let { ctx.renderQuads(coralTuftModels[face][it]) } } } } diff --git a/src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt b/src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt index 903cd72..b479ff8 100644 --- a/src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt +++ b/src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt @@ -13,6 +13,7 @@ import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantTy import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE import mods.betterfoliage.render.lighting.ColumnLighting import mods.betterfoliage.render.pipeline.RenderCtxBase +import net.minecraft.client.renderer.RenderType import net.minecraft.util.Direction.Axis abstract class ColumnModelBase( @@ -24,21 +25,21 @@ abstract class ColumnModelBase( abstract val connectPerpendicular: Boolean abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet - override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { - if (!enabled) return super.render(ctx, noDecorations) + override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) { + if (!enabled) return super.renderLayer(ctx, data, layer) val roundLog = ChunkOverlayManager.get(overlayLayer, ctx) when(roundLog) { ColumnLayerData.SkipRender -> return - NormalRender -> return super.render(ctx, noDecorations) + NormalRender -> return super.renderLayer(ctx, data, layer) ColumnLayerData.ResolveError, null -> { - return super.render(ctx, noDecorations) + return super.renderLayer(ctx, data, layer) } } // if log axis is not defined and "Default to vertical" config option is not set, render normally if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) { - return super.render(ctx, noDecorations) + return super.renderLayer(ctx, data, layer) } ctx.vertexLighter = ColumnLighting diff --git a/src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt b/src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt new file mode 100644 index 0000000..867bd4e --- /dev/null +++ b/src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt @@ -0,0 +1,42 @@ +package mods.betterfoliage.render.pipeline + +import mods.betterfoliage.model.SpecialRenderData +import mods.octarinecore.RenderTypeLookup +import net.minecraft.block.Block +import net.minecraft.client.renderer.RenderType +import java.util.function.Predicate + +object Layers { + val tufts = RenderType.cutout() + val connectedGrass = RenderType.solid() + val connectedDirt = RenderType.cutoutMipped() + val coral = RenderType.cutoutMipped() +} + +val defaultLayerBehaviour = Predicate { layer -> layer == RenderType.solid() } + +class WrappedLayerPredicate(val original: Predicate, val func: (RenderType, Predicate) -> Boolean) : Predicate { + override fun test(layer: RenderType) = func(layer, original) +} + +/** + * Extension method to access the canRenderInLayer() predicate in [RenderTypeLookup] + */ +var Block.layerPredicate : Predicate? + get() = RenderTypeLookup.blockRenderChecks.getStatic()[delegate] + set(value) { + RenderTypeLookup.blockRenderChecks.getStatic()[delegate] = value!! + } + +/** + * Add a wrapper to the block's canRenderInLayer() predicate to enable dynamic multi-layer rendering. + * If the render data for the block implements [SpecialRenderData], the layers it enables will be + * rendered _in addition to_ the block's normal layers. + */ +fun Block.extendLayers() { + val original = layerPredicate ?: defaultLayerBehaviour + if (original !is WrappedLayerPredicate) layerPredicate = WrappedLayerPredicate(original) { layer, original -> + original.test(layer) || + (RenderCtxBase.specialRenderData.get() as? SpecialRenderData)?.canRenderInLayer(layer) ?: false + } +} diff --git a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxBase.kt b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxBase.kt index bccd9f3..c8082d4 100644 --- a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxBase.kt +++ b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxBase.kt @@ -12,9 +12,10 @@ import mods.betterfoliage.util.Int3 import mods.betterfoliage.util.plus import net.minecraft.block.Block import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.BlockRendererDispatcher +import net.minecraft.client.renderer.chunk.ChunkRenderCache import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockDisplayReader import net.minecraftforge.client.model.data.IModelData import java.util.Random @@ -25,17 +26,19 @@ import java.util.Random * push-based partial rendering pipeline for [SpecialRenderModel] instances. */ abstract class RenderCtxBase( - world: IBlockDisplayReader, - pos: BlockPos, + blockCtx: BlockCtx, val matrixStack: MatrixStack, var checkSides: Boolean, val random: Random, - val modelData: IModelData -) : BlockCtx by BasicBlockCtx(world, pos) { - - abstract fun renderQuad(quad: HalfBakedQuad) + val modelData: IModelData, +) : BlockCtx by blockCtx { var hasRendered = false + var modelRenderData: Any? = null + inline fun withRenderData(renderFunc: (T)->Boolean) = (modelRenderData as? T?).let { + if (it == null) false else renderFunc(it) + } + val blockModelShapes = Minecraft.getInstance().blockRenderer.blockModelShaper var vertexLighter: VanillaVertexLighter = VanillaFullBlockLighting protected val lightingData = RenderCtxBase.lightingData.get().apply { @@ -43,6 +46,8 @@ abstract class RenderCtxBase( blockColors = Minecraft.getInstance().blockColors } + abstract fun renderQuad(quad: HalfBakedQuad) + inline fun Direction?.shouldRender() = this == null || !checkSides || Block.shouldRenderFace(state, world, pos, this) fun renderQuads(quads: Iterable) { @@ -61,6 +66,17 @@ abstract class RenderCtxBase( } companion object { + @JvmStatic + fun reset(chunkRenderCache: ChunkRenderCache, blockRendererDispatcher: BlockRendererDispatcher, pos: BlockPos, random: Random) { + // prepare render data + val blockCtx = BasicBlockCtx(chunkRenderCache, pos) + val model = blockRendererDispatcher.getBlockModel(blockCtx.state) + random.setSeed(blockCtx.seed) + val data = if (model is SpecialRenderModel) model.prepare(blockCtx, random) else Unit + specialRenderData.set(data) + } + val lightingData = ThreadLocal.withInitial { VanillaQuadLighting() } + val specialRenderData = ThreadLocal() } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxForge.kt b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxForge.kt index f2b5359..6ea7eb1 100644 --- a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxForge.kt +++ b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxForge.kt @@ -1,27 +1,31 @@ package mods.betterfoliage.render.pipeline import com.mojang.blaze3d.matrix.MatrixStack +import mods.betterfoliage.chunk.BasicBlockCtx +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.model.HalfBakedQuad import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.render.lighting.ForgeVertexLighter import mods.betterfoliage.render.lighting.ForgeVertexLighterAccess +import mods.betterfoliage.util.getWithDefault import net.minecraft.block.BlockState import net.minecraft.client.renderer.LightTexture import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockDisplayReader +import net.minecraftforge.client.ForgeHooksClient +import net.minecraftforge.client.MinecraftForgeClient import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.client.model.pipeline.VertexLighterFlat import java.util.Random class RenderCtxForge( - world: IBlockDisplayReader, - pos: BlockPos, + blockCtx: BlockCtx, val lighter: VertexLighterFlat, matrixStack: MatrixStack, checkSides: Boolean, random: Random, - modelData: IModelData -): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData), ForgeVertexLighter { + modelData: IModelData, +) : RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData), ForgeVertexLighter { override fun renderQuad(quad: HalfBakedQuad) { // set Forge lighter AO calculator to us @@ -38,7 +42,15 @@ class RenderCtxForge( } } - override fun updateVertexColor(normal: FloatArray, color: FloatArray, x: Float, y: Float, z: Float, tint: Float, multiplier: Int) { + override fun updateVertexColor( + normal: FloatArray, + color: FloatArray, + x: Float, + y: Float, + z: Float, + tint: Float, + multiplier: Int + ) { color[0] = lightingData.tint[0] * lightingData.colorMultiplier[vIdx] color[1] = lightingData.tint[1] * lightingData.colorMultiplier[vIdx] color[2] = lightingData.tint[2] * lightingData.colorMultiplier[vIdx] @@ -55,17 +67,21 @@ class RenderCtxForge( pos: BlockPos, matrixStack: MatrixStack, checkSides: Boolean, - rand: Random, seed: Long, + random: Random, seed: Long, modelData: IModelData ): Boolean { - lighter.setWorld(world) - lighter.setState(state) - lighter.setBlockPos(pos) - rand.setSeed(seed) - lighter.updateBlockInfo() - return RenderCtxForge(world, pos, lighter, matrixStack, checkSides, rand, modelData).let { + val blockCtx = BasicBlockCtx(world, pos) + val ctx = RenderCtxForge(blockCtx, lighter, matrixStack, checkSides, random, modelData).apply { + lighter.setWorld(world) + lighter.setState(state) + lighter.setBlockPos(pos) + lighter.updateBlockInfo() + } + + // render layer + return ctx.let { (lighter as ForgeVertexLighterAccess).vertexLighter = it - model.render(it, false) + model.renderLayer(it, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer()) lighter.resetBlockInfo() it.hasRendered } diff --git a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxVanilla.kt b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxVanilla.kt index 0e06fe5..7202b5a 100644 --- a/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxVanilla.kt +++ b/src/main/kotlin/mods/betterfoliage/render/pipeline/RenderCtxVanilla.kt @@ -2,28 +2,31 @@ package mods.betterfoliage.render.pipeline import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.vertex.IVertexBuilder +import mods.betterfoliage.chunk.BasicBlockCtx +import mods.betterfoliage.chunk.BlockCtx import mods.betterfoliage.model.HalfBakedQuad import mods.betterfoliage.model.SpecialRenderModel +import mods.betterfoliage.util.getWithDefault import net.minecraft.block.BlockState import net.minecraft.client.renderer.BlockModelRenderer import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockDisplayReader +import net.minecraftforge.client.MinecraftForgeClient import net.minecraftforge.client.model.data.IModelData import java.util.Random class RenderCtxVanilla( val renderer: BlockModelRenderer, - world: IBlockDisplayReader, - pos: BlockPos, + blockCtx: BlockCtx, val buffer: IVertexBuilder, val combinedOverlay: Int, matrixStack: MatrixStack, checkSides: Boolean, random: Random, - val seed: Long, + val randomSeed: Long, modelData: IModelData, val useAO: Boolean -): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData) { +): RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData) { override fun renderQuad(quad: HalfBakedQuad) { vertexLighter.updateLightmapAndColor(quad, lightingData) @@ -47,20 +50,16 @@ class RenderCtxVanilla( buffer: IVertexBuilder, checkSides: Boolean, random: Random, - rand: Long, + seed: Long, combinedOverlay: Int, modelData: IModelData, smooth: Boolean ): Boolean { - random.setSeed(rand) - val ctx = RenderCtxVanilla(renderer, world, pos, buffer, combinedOverlay, matrixStack, checkSides, random, rand, modelData, smooth) - lightingData.apply { - - } - model.render(ctx, false) + val blockCtx = BasicBlockCtx(world, pos) + // init context if missing (this is the first render layer) + val ctx = RenderCtxVanilla(renderer, blockCtx, buffer, combinedOverlay, matrixStack, checkSides, random, seed, modelData, smooth) + model.renderLayer(ctx, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer()) return ctx.hasRendered } - - } } diff --git a/src/main/kotlin/mods/betterfoliage/util/Collections.kt b/src/main/kotlin/mods/betterfoliage/util/Collections.kt index 2985ce6..161c21e 100644 --- a/src/main/kotlin/mods/betterfoliage/util/Collections.kt +++ b/src/main/kotlin/mods/betterfoliage/util/Collections.kt @@ -65,6 +65,9 @@ inline fun MutableList.exchange(idx1: Int, idx2: Int) { /** Return a random element from the array using the provided random generator */ inline operator fun Array.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size) +inline fun Random.idx(array: Array<*>) = nextInt(Int.MAX_VALUE) % array.size +inline fun Random.idxOrNull(array: Array<*>, predicate: ()->Boolean) = if (predicate()) idx(array) else null + fun Iterable.toImmutableList() = ImmutableList.builder().let { builder -> forEach { builder.add(it) } builder.build() diff --git a/src/main/kotlin/mods/betterfoliage/util/Misc.kt b/src/main/kotlin/mods/betterfoliage/util/Misc.kt index 350379c..d170c42 100644 --- a/src/main/kotlin/mods/betterfoliage/util/Misc.kt +++ b/src/main/kotlin/mods/betterfoliage/util/Misc.kt @@ -2,6 +2,8 @@ package mods.betterfoliage.util import mods.betterfoliage.BetterFoliageMod +import net.minecraft.block.BlockState +import net.minecraft.client.Minecraft import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos import net.minecraft.world.World @@ -31,6 +33,11 @@ class ThreadLocalDelegate(init: () -> T) { operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { tlVal.set(value) } } +fun ThreadLocal.getWithDefault(factory: ()->T): T { + get()?.let { return it } + return factory().apply { set(this) } +} + /** Call the supplied lambda and return its result, or the given default value if an exception is thrown. */ fun tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable) { default } @@ -56,6 +63,7 @@ abstract class HasLogger { val detailLogger = BetterFoliageMod.detailLogger(this) } +fun getBlockModel(state: BlockState) = Minecraft.getInstance().blockRenderer.blockModelShaper.getBlockModel(state) /** * Check if the Chunk containing the given [BlockPos] is loaded. * Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances. diff --git a/src/main/resources/betterfoliage.common.mixins.json b/src/main/resources/betterfoliage.common.mixins.json index e1ee813..2614d3f 100644 --- a/src/main/resources/betterfoliage.common.mixins.json +++ b/src/main/resources/betterfoliage.common.mixins.json @@ -10,6 +10,7 @@ "MixinBlock", "MixinBlockState", "MixinBlockModelRenderer", + "MixinChunkRendererDispatcher", "MixinClientWorld", "MixinModelBakery", "MixinForgeBlockModelRenderer",