Multi-layer rendering support

This commit is contained in:
octarine-noise
2021-07-06 00:06:11 +02:00
parent 78c7b53595
commit 29ab544269
22 changed files with 479 additions and 146 deletions

View File

@@ -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);
}
}

View File

@@ -10,6 +10,7 @@ import net.minecraft.block.BlockState
import net.minecraft.client.renderer.BlockModelRenderer import net.minecraft.client.renderer.BlockModelRenderer
import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.BufferBuilder import net.minecraft.client.renderer.BufferBuilder
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.chunk.ChunkRenderCache import net.minecraft.client.renderer.chunk.ChunkRenderCache
import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.model.BakedQuad
import net.minecraft.client.renderer.model.IUnbakedModel import net.minecraft.client.renderer.model.IUnbakedModel
@@ -21,7 +22,9 @@ import net.minecraft.world.IBlockDisplayReader
import net.minecraft.world.IBlockReader import net.minecraft.world.IBlockReader
import net.minecraftforge.client.model.pipeline.BlockInfo import net.minecraftforge.client.model.pipeline.BlockInfo
import net.minecraftforge.client.model.pipeline.VertexLighterFlat import net.minecraftforge.client.model.pipeline.VertexLighterFlat
import net.minecraftforge.registries.IRegistryDelegate
import java.util.Random import java.util.Random
import java.util.function.Predicate
typealias Sprite = TextureAtlasSprite typealias Sprite = TextureAtlasSprite
@@ -61,6 +64,10 @@ object ModelBakery : ClassRef<ModelBakery>("net.minecraft.client.renderer.model.
val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>()) val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>())
} }
object RenderTypeLookup : ClassRef<RenderTypeLookup>("net.minecraft.client.renderer.RenderTypeLookup") {
val blockRenderChecks = FieldRef(this, "blockRenderChecks", mapRefMutable<IRegistryDelegate<Block>, Predicate<RenderType>>())
}
// Optifine // Optifine
val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer") val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer")
val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM") val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM")

View File

@@ -22,6 +22,8 @@ interface BlockCtx {
val world: IBlockDisplayReader val world: IBlockDisplayReader
val pos: BlockPos val pos: BlockPos
val seed: Long get() = state.getSeed(pos)
fun offset(dir: Direction) = offset(dir.offset) fun offset(dir: Direction) = offset(dir.offset)
fun offset(offset: Int3): BlockCtx fun offset(offset: Int3): BlockCtx

View File

@@ -1,12 +1,19 @@
package mods.betterfoliage.model package mods.betterfoliage.model
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.render.pipeline.RenderCtxBase 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.ModelBakingContext
import mods.betterfoliage.resource.discovery.ModelBakingKey import mods.betterfoliage.resource.discovery.ModelBakingKey
import mods.betterfoliage.util.Double3 import mods.betterfoliage.util.Double3
import mods.betterfoliage.util.HasLogger import mods.betterfoliage.util.HasLogger
import mods.betterfoliage.util.directionsAndNull import mods.betterfoliage.util.directionsAndNull
import mods.betterfoliage.util.mapArray 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.BakedQuad
import net.minecraft.client.renderer.model.IBakedModel import net.minecraft.client.renderer.model.IBakedModel
import net.minecraft.client.renderer.model.SimpleBakedModel import net.minecraft.client.renderer.model.SimpleBakedModel
@@ -27,15 +34,23 @@ data class HalfBakedQuad(
open class HalfBakedSimpleModelWrapper(baseModel: SimpleBakedModel): IBakedModel by baseModel, SpecialRenderModel { open class HalfBakedSimpleModelWrapper(baseModel: SimpleBakedModel): IBakedModel by baseModel, SpecialRenderModel {
val baseQuads = baseModel.unbakeQuads() val baseQuads = baseModel.unbakeQuads()
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random) = Unit
ctx.renderQuads(baseQuads)
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 { open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): SpecialRenderModel by baseModel {
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
baseModel.render(ctx, noDecorations)
}
} }
abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() { abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() {

View File

@@ -1,7 +1,10 @@
package mods.betterfoliage.model package mods.betterfoliage.model
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.render.pipeline.RenderCtxBase 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.IBakedModel
import net.minecraft.client.renderer.model.WeightedBakedModel
import net.minecraft.util.WeightedRandom import net.minecraft.util.WeightedRandom
import java.util.Random import java.util.Random
@@ -9,18 +12,46 @@ import java.util.Random
* Model that makes use of advanced rendering features. * Model that makes use of advanced rendering features.
*/ */
interface SpecialRenderModel : IBakedModel { 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( class WeightedModelWrapper(
val models: List<WeightedModel>, baseModel: SpecialRenderModel val models: List<WeightedModel>, baseModel: SpecialRenderModel
): IBakedModel by baseModel, SpecialRenderModel { ) : IBakedModel by baseModel, SpecialRenderModel {
class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight) class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight)
val totalWeight = models.sumBy { it.weight } val totalWeight = models.sumBy { it.weight }
fun getModel(random: Random) = WeightedRandom.getWeightedItem(models, random.nextInt(totalWeight)) fun getModel(random: Random) = WeightedRandom.getWeightedItem(models, random.nextInt(totalWeight))
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun resolve(random: Random) = getModel(random).model.resolve(random)
getModel(ctx.random).model.render(ctx, noDecorations)
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
}

View File

@@ -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)) } fun xzDisk(modelIdx: Int) = (PI2 * modelIdx.toDouble() / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
data class TuftShapeKey( data class TuftShapeKey(
val size: Double, val size: Double,
val height: Double, val height: Double,

View File

@@ -1,11 +1,13 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.CACTUS_BLOCKS import mods.betterfoliage.config.CACTUS_BLOCKS
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.buildTufts
@@ -24,14 +26,15 @@ import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Atlas
import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.Rotation import mods.betterfoliage.util.Rotation
import mods.betterfoliage.util.get
import mods.betterfoliage.util.horizontalDirections import mods.betterfoliage.util.horizontalDirections
import mods.betterfoliage.util.idx
import mods.betterfoliage.util.randomD import mods.betterfoliage.util.randomD
import mods.betterfoliage.util.randomI 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.client.renderer.model.BlockModel
import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.DOWN
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardCactusDiscovery : AbstractModelDiscovery() { object StandardCactusDiscovery : AbstractModelDiscovery() {
override fun processModel(ctx: ModelDiscoveryContext) { override fun processModel(ctx: ModelDiscoveryContext) {
@@ -49,22 +52,30 @@ object StandardCactusKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped) override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped)
} }
class CactusRenderData(val armSide: Int, val armIdx: Int, val crossIdx: Int)
class StandardCactusModel( class StandardCactusModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : 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() val armLighting = horizontalDirections.map { LightingPreferredFace(it) }.toTypedArray()
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
ctx.checkSides = false super.renderLayer(ctx, data, layer)
super.render(ctx, noDecorations) if (data is CactusRenderData) {
if (!Config.enabled || !Config.cactus.enabled) return ctx.vertexLighter = armLighting[data.armSide]
ctx.renderQuads(cactusArmModels[data.armSide][data.armIdx])
val armSide = ctx.random.nextInt() and 3
ctx.vertexLighter = armLighting[armSide]
ctx.renderQuads(cactusArmModels[armSide][ctx.random])
ctx.vertexLighter = RoundLeafLighting ctx.vertexLighter = RoundLeafLighting
ctx.renderQuads(cactusCrossModels[ctx.random]) ctx.renderQuads(cactusCrossModels[data.crossIdx])
}
} }
companion object { companion object {

View File

@@ -2,6 +2,7 @@ package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.DIRT_BLOCKS import mods.betterfoliage.config.DIRT_BLOCKS
import mods.betterfoliage.config.SALTWATER_BIOMES import mods.betterfoliage.config.SALTWATER_BIOMES
@@ -9,13 +10,16 @@ import mods.betterfoliage.config.isSnow
import mods.betterfoliage.integration.ShadersModIntegration import mods.betterfoliage.integration.ShadersModIntegration
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.buildTufts
import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftModelSet
import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.model.tuftShapeSet
import mods.betterfoliage.render.lighting.LightingPreferredFace import mods.betterfoliage.render.lighting.LightingPreferredFace
import mods.betterfoliage.render.pipeline.Layers
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import mods.betterfoliage.render.pipeline.extendLayers
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.BakeWrapperManager
import mods.betterfoliage.resource.discovery.ModelBakingContext import mods.betterfoliage.resource.discovery.ModelBakingContext
@@ -25,27 +29,25 @@ import mods.betterfoliage.util.Atlas
import mods.betterfoliage.util.Int3 import mods.betterfoliage.util.Int3
import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.get import mods.betterfoliage.util.get
import mods.betterfoliage.util.getBlockModel
import mods.betterfoliage.util.idxOrNull
import mods.betterfoliage.util.offset import mods.betterfoliage.util.offset
import mods.betterfoliage.util.randomI import mods.betterfoliage.util.randomI
import net.minecraft.block.material.Material import net.minecraft.block.material.Material
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.RenderTypeLookup
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardDirtDiscovery : AbstractModelDiscovery() { 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) { override fun processModel(ctx: ModelDiscoveryContext) {
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in DIRT_BLOCKS) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in DIRT_BLOCKS) {
BetterFoliage.blockTypes.dirt.add(ctx.blockState) BetterFoliage.blockTypes.dirt.add(ctx.blockState)
ctx.addReplacement(StandardDirtKey) ctx.addReplacement(StandardDirtKey)
RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer) ctx.blockState.block.extendLayers()
} }
super.processModel(ctx) super.processModel(ctx)
} }
@@ -55,49 +57,72 @@ object StandardDirtKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardDirtModel(wrapped) 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( class StandardDirtModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : HalfBakedSpecialWrapper(wrapped) {
val vanillaTuftLighting = LightingPreferredFace(UP) val vanillaTuftLighting = LightingPreferredFace(UP)
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random): Any {
if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations) if (!Config.enabled) return Unit
val stateUp = ctx.state(UP) val stateUp = ctx.state(UP)
val state2Up = ctx.state(Int3(0, 2, 0)) val state2Up = ctx.state(Int3(0, 2, 0))
val isConnectedGrass = Config.connectedGrass.enabled && val isConnectedGrass = Config.connectedGrass.enabled &&
stateUp in BetterFoliage.blockTypes.grass && stateUp in BetterFoliage.blockTypes.grass &&
(Config.connectedGrass.snowEnabled || !state2Up.isSnow) (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 isWater = stateUp.material == Material.WATER
val isDeepWater = isWater && state2Up.material == Material.WATER val isDeepWater = isWater && state2Up.material == Material.WATER
val isShallowWater = isWater && state2Up.isAir val isShallowWater = isWater && state2Up.isAir
val isSaltWater = isWater && ctx.biome?.biomeCategory in SALTWATER_BIOMES val isSaltWater = isWater && ctx.biome?.biomeCategory in SALTWATER_BIOMES
if (Config.algae.enabled(ctx.random) && isDeepWater) { 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)
}
if (layer == Layers.tufts) {
data.algaeIdx?.let {
ctx.vertexLighter = vanillaTuftLighting ctx.vertexLighter = vanillaTuftLighting
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) { ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
ctx.renderQuads(algaeModels[ctx.random]) ctx.renderQuads(algaeModels[it])
} }
} else if (Config.reed.enabled(ctx.random) && isShallowWater && !isSaltWater) { }
data.reedIdx?.let {
ctx.vertexLighter = vanillaTuftLighting ctx.vertexLighter = vanillaTuftLighting
ShadersModIntegration.grass(ctx, Config.reed.shaderWind) { ShadersModIntegration.grass(ctx, Config.reed.shaderWind) {
ctx.renderQuads(reedModels[ctx.random]) ctx.renderQuads(reedModels[it])
} }
} }
} }
} else super.renderLayer(ctx, data, layer)
}
companion object { companion object {
val algaeSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx -> val algaeSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->

View File

@@ -2,6 +2,7 @@ package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.BlockConfig import mods.betterfoliage.config.BlockConfig
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.isSnow import mods.betterfoliage.config.isSnow
@@ -9,6 +10,7 @@ import mods.betterfoliage.integration.ShadersModIntegration
import mods.betterfoliage.model.Color import mods.betterfoliage.model.Color
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.buildTufts
@@ -16,7 +18,9 @@ import mods.betterfoliage.model.fullCubeTextured
import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftModelSet
import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.model.tuftShapeSet
import mods.betterfoliage.render.lighting.LightingPreferredFace import mods.betterfoliage.render.lighting.LightingPreferredFace
import mods.betterfoliage.render.pipeline.Layers
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import mods.betterfoliage.render.pipeline.extendLayers
import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.BakeWrapperManager
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
@@ -28,12 +32,14 @@ import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.LazyMapInvalidatable import mods.betterfoliage.util.LazyMapInvalidatable
import mods.betterfoliage.util.averageColor import mods.betterfoliage.util.averageColor
import mods.betterfoliage.util.colorOverride import mods.betterfoliage.util.colorOverride
import mods.betterfoliage.util.get import mods.betterfoliage.util.idxOrNull
import mods.betterfoliage.util.logColorOverride import mods.betterfoliage.util.logColorOverride
import mods.betterfoliage.util.randomI import mods.betterfoliage.util.randomI
import net.minecraft.client.renderer.RenderType
import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardGrassDiscovery : ConfigurableModelDiscovery() { object StandardGrassDiscovery : ConfigurableModelDiscovery() {
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
@@ -42,6 +48,7 @@ object StandardGrassDiscovery : ConfigurableModelDiscovery() {
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) { override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
ctx.addReplacement(StandardGrassKey(textureMatch[0], null)) ctx.addReplacement(StandardGrassKey(textureMatch[0], null))
BetterFoliage.blockTypes.grass.add(ctx.blockState) 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( class StandardGrassModel(
wrapped: SpecialRenderModel, wrapped: SpecialRenderModel,
key: StandardGrassKey key: StandardGrassKey
@@ -70,8 +89,8 @@ class StandardGrassModel(
val fullBlock by grassFullBlockMeshes.delegate(key) val fullBlock by grassFullBlockMeshes.delegate(key)
val tuftLighting = LightingPreferredFace(UP) val tuftLighting = LightingPreferredFace(UP)
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random): Any {
if (!Config.enabled || noDecorations) return super.render(ctx, noDecorations) if (!Config.enabled) return Unit
val stateBelow = ctx.state(DOWN) val stateBelow = ctx.state(DOWN)
val stateAbove = ctx.state(UP) val stateAbove = ctx.state(UP)
@@ -81,18 +100,32 @@ class StandardGrassModel(
(!isSnowed || Config.connectedGrass.snowEnabled) && (!isSnowed || Config.connectedGrass.snowEnabled) &&
BetterFoliage.blockTypes.run { stateBelow in grass || stateBelow in dirt } BetterFoliage.blockTypes.run { stateBelow in grass || stateBelow in dirt }
if (connected) { return GrassRenderData(
ctx.renderQuads(if (isSnowed) snowFullBlockMeshes[ctx.random] else fullBlock[ctx.random]) isSnowed = isSnowed,
} else { connectedGrassIdx = random.idxOrNull(if (isSnowed) snowFullBlockMeshes else fullBlock) { connected },
super.render(ctx, noDecorations) tuftIdx = random.idxOrNull(if (isSnowed) tuftSnowed else tuftNormal) {
Config.shortGrass.enabled(random) &&
Config.shortGrass.grassEnabled &&
(isAir || isSnowed)
}
)
} }
if (Config.shortGrass.enabled(ctx.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 ctx.vertexLighter = tuftLighting
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) { ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
ctx.renderQuads(if (isSnowed) tuftSnowed[ctx.random] else tuftNormal[ctx.random]) ctx.renderQuads((if (data.isSnowed) tuftSnowed else tuftNormal)[data.tuftIdx])
} }
} }
} else super.renderLayer(ctx, data, layer)
} }
companion object { companion object {

View File

@@ -30,6 +30,7 @@ import mods.betterfoliage.util.LazyMapInvalidatable
import mods.betterfoliage.util.averageColor import mods.betterfoliage.util.averageColor
import mods.betterfoliage.util.colorOverride import mods.betterfoliage.util.colorOverride
import mods.betterfoliage.util.logColorOverride import mods.betterfoliage.util.logColorOverride
import net.minecraft.client.renderer.RenderType
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.INFO import org.apache.logging.log4j.Level.INFO
@@ -72,10 +73,10 @@ class StandardLeafModel(
val leafNormal by leafModelsNormal.delegate(key) val leafNormal by leafModelsNormal.delegate(key)
val leafSnowed by leafModelsSnowed.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) { ShadersModIntegration.leaves(ctx, true) {
super.render(ctx, noDecorations) super.renderLayer(ctx, data, layer)
if (!Config.enabled || !Config.leaves.enabled || noDecorations) return if (!Config.enabled || !Config.leaves.enabled) return
ctx.vertexLighter = RoundLeafLightingPreferUp ctx.vertexLighter = RoundLeafLightingPreferUp
val leafIdx = ctx.random.nextInt(64) val leafIdx = ctx.random.nextInt(64)

View File

@@ -1,6 +1,7 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.LILYPAD_BLOCKS import mods.betterfoliage.config.LILYPAD_BLOCKS
import mods.betterfoliage.integration.ShadersModIntegration import mods.betterfoliage.integration.ShadersModIntegration
@@ -21,12 +22,16 @@ import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Atlas
import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.get import mods.betterfoliage.util.get
import mods.betterfoliage.util.idx
import mods.betterfoliage.util.idxOrNull
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.DOWN
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardLilypadDiscovery : AbstractModelDiscovery() { object StandardLilypadDiscovery : AbstractModelDiscovery() {
override fun processModel(ctx: ModelDiscoveryContext) { override fun processModel(ctx: ModelDiscoveryContext) {
@@ -41,18 +46,32 @@ object StandardLilypadKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped) override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped)
} }
class LilypadRenderData(
val rootIdx: Int,
val flowerIdx: Int?
)
class StandardLilypadModel( class StandardLilypadModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : 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) { override fun prepare(ctx: BlockCtx, random: Random): Any {
ctx.renderQuads(lilypadRootModels[ctx.random]) 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 { companion object {

View File

@@ -1,17 +1,21 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.MYCELIUM_BLOCKS import mods.betterfoliage.config.MYCELIUM_BLOCKS
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.buildTufts
import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftModelSet
import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.model.tuftShapeSet
import mods.betterfoliage.render.lighting.LightingPreferredFace import mods.betterfoliage.render.lighting.LightingPreferredFace
import mods.betterfoliage.render.pipeline.Layers
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import mods.betterfoliage.render.pipeline.extendLayers
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.BakeWrapperManager
import mods.betterfoliage.resource.discovery.ModelBakingContext 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.Atlas
import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.get import mods.betterfoliage.util.get
import mods.betterfoliage.util.idxOrNull
import mods.betterfoliage.util.randomI import mods.betterfoliage.util.randomI
import net.minecraft.block.Blocks
import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.RenderTypeLookup
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.util.Direction import net.minecraft.util.Direction
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardMyceliumDiscovery : AbstractModelDiscovery() { object StandardMyceliumDiscovery : AbstractModelDiscovery() {
override fun processModel(ctx: ModelDiscoveryContext) { override fun processModel(ctx: ModelDiscoveryContext) {
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in MYCELIUM_BLOCKS) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in MYCELIUM_BLOCKS) {
ctx.addReplacement(StandardMyceliumKey) ctx.addReplacement(StandardMyceliumKey)
RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.cutout()) ctx.blockState.block.extendLayers()
} }
super.processModel(ctx) super.processModel(ctx)
} }
@@ -41,21 +46,34 @@ object StandardMyceliumKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardMyceliumModel(wrapped) 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( class StandardMyceliumModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : HalfBakedSpecialWrapper(wrapped) {
val tuftLighting = LightingPreferredFace(Direction.UP) val tuftLighting = LightingPreferredFace(Direction.UP)
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random): Any {
super.render(ctx, noDecorations) if (!Config.enabled) return Unit
return MyceliumRenderData(
if (Config.shortGrass.enabled(ctx.random) && random.idxOrNull(myceliumTuftModels) {
Config.shortGrass.enabled(random) &&
Config.shortGrass.myceliumEnabled && Config.shortGrass.myceliumEnabled &&
ctx.state(Direction.UP).isAir(ctx.world, ctx.pos) 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.vertexLighter = tuftLighting
ctx.renderQuads(myceliumTuftModels[ctx.random]) ctx.renderQuads(myceliumTuftModels[data.tuftIndex])
} }
} }

View File

@@ -2,10 +2,12 @@ package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.NETHERRACK_BLOCKS import mods.betterfoliage.config.NETHERRACK_BLOCKS
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.buildTufts import mods.betterfoliage.model.buildTufts
@@ -13,7 +15,9 @@ import mods.betterfoliage.model.transform
import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftModelSet
import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.model.tuftShapeSet
import mods.betterfoliage.render.lighting.LightingPreferredFace import mods.betterfoliage.render.lighting.LightingPreferredFace
import mods.betterfoliage.render.pipeline.Layers
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import mods.betterfoliage.render.pipeline.extendLayers
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.BakeWrapperManager
import mods.betterfoliage.resource.discovery.ModelBakingContext import mods.betterfoliage.resource.discovery.ModelBakingContext
@@ -22,26 +26,23 @@ import mods.betterfoliage.util.Atlas
import mods.betterfoliage.util.LazyInvalidatable import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.Rotation import mods.betterfoliage.util.Rotation
import mods.betterfoliage.util.get import mods.betterfoliage.util.get
import mods.betterfoliage.util.idxOrNull
import mods.betterfoliage.util.randomI import mods.betterfoliage.util.randomI
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.RenderTypeLookup import net.minecraft.client.renderer.RenderTypeLookup
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.util.Direction
import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.DOWN
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardNetherrackDiscovery : AbstractModelDiscovery() { 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) { override fun processModel(ctx: ModelDiscoveryContext) {
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in NETHERRACK_BLOCKS) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in NETHERRACK_BLOCKS) {
BetterFoliage.blockTypes.dirt.add(ctx.blockState) BetterFoliage.blockTypes.dirt.add(ctx.blockState)
ctx.addReplacement(StandardNetherrackKey) ctx.addReplacement(StandardNetherrackKey)
RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer) ctx.blockState.block.extendLayers()
} }
super.processModel(ctx) super.processModel(ctx)
} }
@@ -51,19 +52,33 @@ object StandardNetherrackKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardNetherrackModel(wrapped) 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( class StandardNetherrackModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : HalfBakedSpecialWrapper(wrapped) {
val tuftLighting = LightingPreferredFace(DOWN) val tuftLighting = LightingPreferredFace(DOWN)
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random): Any {
super.render(ctx, noDecorations) if (!Config.enabled) return Unit
if (!Config.enabled || !Config.netherrack.enabled) return 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.vertexLighter = tuftLighting
ctx.renderQuads(netherrackTuftModels[ctx.random]) ctx.renderQuads(netherrackTuftModels[data.tuftIndex])
} }
} }

View File

@@ -2,12 +2,14 @@ package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.config.Config import mods.betterfoliage.config.Config
import mods.betterfoliage.config.SALTWATER_BIOMES import mods.betterfoliage.config.SALTWATER_BIOMES
import mods.betterfoliage.config.SAND_BLOCKS import mods.betterfoliage.config.SAND_BLOCKS
import mods.betterfoliage.model.HalfBakedSpecialWrapper import mods.betterfoliage.model.HalfBakedSpecialWrapper
import mods.betterfoliage.model.HalfBakedWrapperKey import mods.betterfoliage.model.HalfBakedWrapperKey
import mods.betterfoliage.model.Quad import mods.betterfoliage.model.Quad
import mods.betterfoliage.model.SpecialRenderData
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.model.SpriteSetDelegate import mods.betterfoliage.model.SpriteSetDelegate
import mods.betterfoliage.model.bake import mods.betterfoliage.model.bake
@@ -16,7 +18,9 @@ import mods.betterfoliage.model.transform
import mods.betterfoliage.model.tuftModelSet import mods.betterfoliage.model.tuftModelSet
import mods.betterfoliage.model.tuftShapeSet import mods.betterfoliage.model.tuftShapeSet
import mods.betterfoliage.render.lighting.LightingPreferredFace import mods.betterfoliage.render.lighting.LightingPreferredFace
import mods.betterfoliage.render.pipeline.Layers
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import mods.betterfoliage.render.pipeline.extendLayers
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
import mods.betterfoliage.resource.discovery.BakeWrapperManager import mods.betterfoliage.resource.discovery.BakeWrapperManager
import mods.betterfoliage.resource.discovery.ModelBakingContext import mods.betterfoliage.resource.discovery.ModelBakingContext
@@ -26,6 +30,7 @@ import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.Rotation import mods.betterfoliage.util.Rotation
import mods.betterfoliage.util.allDirections import mods.betterfoliage.util.allDirections
import mods.betterfoliage.util.get import mods.betterfoliage.util.get
import mods.betterfoliage.util.idx
import mods.betterfoliage.util.mapArray import mods.betterfoliage.util.mapArray
import mods.betterfoliage.util.randomB import mods.betterfoliage.util.randomB
import mods.betterfoliage.util.randomD 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
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import java.util.Random
object StandardSandDiscovery : AbstractModelDiscovery() { object StandardSandDiscovery : AbstractModelDiscovery() {
override fun processModel(ctx: ModelDiscoveryContext) { override fun processModel(ctx: ModelDiscoveryContext) {
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in SAND_BLOCKS) { if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in SAND_BLOCKS) {
BetterFoliage.blockTypes.dirt.add(ctx.blockState) BetterFoliage.blockTypes.dirt.add(ctx.blockState)
ctx.addReplacement(StandardSandKey) ctx.addReplacement(StandardSandKey)
RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.cutoutMipped()) ctx.blockState.block.extendLayers()
} }
super.processModel(ctx) super.processModel(ctx)
} }
@@ -53,23 +59,46 @@ object StandardSandKey : HalfBakedWrapperKey() {
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped) override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped)
} }
class SandRenderData(
val crustIdx: Array<Int?>,
val tuftIdx: Array<Int?>
): SpecialRenderData {
override fun canRenderInLayer(layer: RenderType) = when {
(crustIdx.any { it != null } || tuftIdx.any { it != null }) && layer == Layers.coral -> true
else -> false
}
}
class StandardSandModel( class StandardSandModel(
wrapped: SpecialRenderModel wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) { ) : HalfBakedSpecialWrapper(wrapped) {
val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) } val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) }
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun prepare(ctx: BlockCtx, random: Random): Any {
super.render(ctx, noDecorations) if (!Config.enabled) return Unit
if (noDecorations || !Config.enabled || !Config.coral.enabled(ctx.random)) return if (!Config.coral.enabled(random)) return Unit
if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return if (ctx.biome?.biomeCategory !in SALTWATER_BIOMES) return Unit
allDirections.filter { ctx.random.nextInt(64) < Config.coral.chance }.forEach { face -> val crustIdx = Array<Int?>(6) { null }
val tuftIdx = Array<Int?>(6) { null }
allDirections.filter { random.nextInt(64) < Config.coral.chance }.forEach { face ->
val isWater = ctx.state(face).material == Material.WATER val isWater = ctx.state(face).material == Material.WATER
val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER
if (isDeepWater) { 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.vertexLighter = coralLighting[face]
ctx.renderQuads(coralCrustModels[face][ctx.random]) data.crustIdx[face]?.let { ctx.renderQuads(coralCrustModels[face][it]) }
ctx.renderQuads(coralTuftModels[face][ctx.random]) data.tuftIdx[face]?.let { ctx.renderQuads(coralTuftModels[face][it]) }
} }
} }
} }

View File

@@ -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.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
import mods.betterfoliage.render.lighting.ColumnLighting import mods.betterfoliage.render.lighting.ColumnLighting
import mods.betterfoliage.render.pipeline.RenderCtxBase import mods.betterfoliage.render.pipeline.RenderCtxBase
import net.minecraft.client.renderer.RenderType
import net.minecraft.util.Direction.Axis import net.minecraft.util.Direction.Axis
abstract class ColumnModelBase( abstract class ColumnModelBase(
@@ -24,21 +25,21 @@ abstract class ColumnModelBase(
abstract val connectPerpendicular: Boolean abstract val connectPerpendicular: Boolean
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) { override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
if (!enabled) return super.render(ctx, noDecorations) if (!enabled) return super.renderLayer(ctx, data, layer)
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx) val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
when(roundLog) { when(roundLog) {
ColumnLayerData.SkipRender -> return ColumnLayerData.SkipRender -> return
NormalRender -> return super.render(ctx, noDecorations) NormalRender -> return super.renderLayer(ctx, data, layer)
ColumnLayerData.ResolveError, null -> { 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 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) { if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
return super.render(ctx, noDecorations) return super.renderLayer(ctx, data, layer)
} }
ctx.vertexLighter = ColumnLighting ctx.vertexLighter = ColumnLighting

View File

@@ -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<RenderType> { layer -> layer == RenderType.solid() }
class WrappedLayerPredicate(val original: Predicate<RenderType>, val func: (RenderType, Predicate<RenderType>) -> Boolean) : Predicate<RenderType> {
override fun test(layer: RenderType) = func(layer, original)
}
/**
* Extension method to access the canRenderInLayer() predicate in [RenderTypeLookup]
*/
var Block.layerPredicate : Predicate<RenderType>?
get() = RenderTypeLookup.blockRenderChecks.getStatic()[delegate]
set(value) {
RenderTypeLookup.blockRenderChecks.getStatic()[delegate] = value!!
}
/**
* Add a wrapper to the block's canRenderInLayer() predicate to enable dynamic multi-layer rendering.
* If the render data for the block implements [SpecialRenderData], the layers it enables will be
* rendered _in addition to_ the block's normal layers.
*/
fun Block.extendLayers() {
val original = layerPredicate ?: defaultLayerBehaviour
if (original !is WrappedLayerPredicate) layerPredicate = WrappedLayerPredicate(original) { layer, original ->
original.test(layer) ||
(RenderCtxBase.specialRenderData.get() as? SpecialRenderData)?.canRenderInLayer(layer) ?: false
}
}

View File

@@ -12,9 +12,10 @@ import mods.betterfoliage.util.Int3
import mods.betterfoliage.util.plus import mods.betterfoliage.util.plus
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.client.Minecraft 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.Direction
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockDisplayReader
import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.client.model.data.IModelData
import java.util.Random import java.util.Random
@@ -25,17 +26,19 @@ import java.util.Random
* push-based partial rendering pipeline for [SpecialRenderModel] instances. * push-based partial rendering pipeline for [SpecialRenderModel] instances.
*/ */
abstract class RenderCtxBase( abstract class RenderCtxBase(
world: IBlockDisplayReader, blockCtx: BlockCtx,
pos: BlockPos,
val matrixStack: MatrixStack, val matrixStack: MatrixStack,
var checkSides: Boolean, var checkSides: Boolean,
val random: Random, val random: Random,
val modelData: IModelData val modelData: IModelData,
) : BlockCtx by BasicBlockCtx(world, pos) { ) : BlockCtx by blockCtx {
abstract fun renderQuad(quad: HalfBakedQuad)
var hasRendered = false var hasRendered = false
var modelRenderData: Any? = null
inline fun <reified T> withRenderData(renderFunc: (T)->Boolean) = (modelRenderData as? T?).let {
if (it == null) false else renderFunc(it)
}
val blockModelShapes = Minecraft.getInstance().blockRenderer.blockModelShaper val blockModelShapes = Minecraft.getInstance().blockRenderer.blockModelShaper
var vertexLighter: VanillaVertexLighter = VanillaFullBlockLighting var vertexLighter: VanillaVertexLighter = VanillaFullBlockLighting
protected val lightingData = RenderCtxBase.lightingData.get().apply { protected val lightingData = RenderCtxBase.lightingData.get().apply {
@@ -43,6 +46,8 @@ abstract class RenderCtxBase(
blockColors = Minecraft.getInstance().blockColors blockColors = Minecraft.getInstance().blockColors
} }
abstract fun renderQuad(quad: HalfBakedQuad)
inline fun Direction?.shouldRender() = this == null || !checkSides || Block.shouldRenderFace(state, world, pos, this) inline fun Direction?.shouldRender() = this == null || !checkSides || Block.shouldRenderFace(state, world, pos, this)
fun renderQuads(quads: Iterable<HalfBakedQuad>) { fun renderQuads(quads: Iterable<HalfBakedQuad>) {
@@ -61,6 +66,17 @@ abstract class RenderCtxBase(
} }
companion object { 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 lightingData = ThreadLocal.withInitial { VanillaQuadLighting() }
val specialRenderData = ThreadLocal<Any?>()
} }
} }

View File

@@ -1,27 +1,31 @@
package mods.betterfoliage.render.pipeline package mods.betterfoliage.render.pipeline
import com.mojang.blaze3d.matrix.MatrixStack 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.HalfBakedQuad
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.render.lighting.ForgeVertexLighter import mods.betterfoliage.render.lighting.ForgeVertexLighter
import mods.betterfoliage.render.lighting.ForgeVertexLighterAccess import mods.betterfoliage.render.lighting.ForgeVertexLighterAccess
import mods.betterfoliage.util.getWithDefault
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.renderer.LightTexture import net.minecraft.client.renderer.LightTexture
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockDisplayReader 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.data.IModelData
import net.minecraftforge.client.model.pipeline.VertexLighterFlat import net.minecraftforge.client.model.pipeline.VertexLighterFlat
import java.util.Random import java.util.Random
class RenderCtxForge( class RenderCtxForge(
world: IBlockDisplayReader, blockCtx: BlockCtx,
pos: BlockPos,
val lighter: VertexLighterFlat, val lighter: VertexLighterFlat,
matrixStack: MatrixStack, matrixStack: MatrixStack,
checkSides: Boolean, checkSides: Boolean,
random: Random, random: Random,
modelData: IModelData modelData: IModelData,
): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData), ForgeVertexLighter { ) : RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData), ForgeVertexLighter {
override fun renderQuad(quad: HalfBakedQuad) { override fun renderQuad(quad: HalfBakedQuad) {
// set Forge lighter AO calculator to us // 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[0] = lightingData.tint[0] * lightingData.colorMultiplier[vIdx]
color[1] = lightingData.tint[1] * lightingData.colorMultiplier[vIdx] color[1] = lightingData.tint[1] * lightingData.colorMultiplier[vIdx]
color[2] = lightingData.tint[2] * lightingData.colorMultiplier[vIdx] color[2] = lightingData.tint[2] * lightingData.colorMultiplier[vIdx]
@@ -55,17 +67,21 @@ class RenderCtxForge(
pos: BlockPos, pos: BlockPos,
matrixStack: MatrixStack, matrixStack: MatrixStack,
checkSides: Boolean, checkSides: Boolean,
rand: Random, seed: Long, random: Random, seed: Long,
modelData: IModelData modelData: IModelData
): Boolean { ): Boolean {
val blockCtx = BasicBlockCtx(world, pos)
val ctx = RenderCtxForge(blockCtx, lighter, matrixStack, checkSides, random, modelData).apply {
lighter.setWorld(world) lighter.setWorld(world)
lighter.setState(state) lighter.setState(state)
lighter.setBlockPos(pos) lighter.setBlockPos(pos)
rand.setSeed(seed)
lighter.updateBlockInfo() lighter.updateBlockInfo()
return RenderCtxForge(world, pos, lighter, matrixStack, checkSides, rand, modelData).let { }
// render layer
return ctx.let {
(lighter as ForgeVertexLighterAccess).vertexLighter = it (lighter as ForgeVertexLighterAccess).vertexLighter = it
model.render(it, false) model.renderLayer(it, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer())
lighter.resetBlockInfo() lighter.resetBlockInfo()
it.hasRendered it.hasRendered
} }

View File

@@ -2,28 +2,31 @@ package mods.betterfoliage.render.pipeline
import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.matrix.MatrixStack
import com.mojang.blaze3d.vertex.IVertexBuilder 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.HalfBakedQuad
import mods.betterfoliage.model.SpecialRenderModel import mods.betterfoliage.model.SpecialRenderModel
import mods.betterfoliage.util.getWithDefault
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.renderer.BlockModelRenderer import net.minecraft.client.renderer.BlockModelRenderer
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockDisplayReader import net.minecraft.world.IBlockDisplayReader
import net.minecraftforge.client.MinecraftForgeClient
import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.client.model.data.IModelData
import java.util.Random import java.util.Random
class RenderCtxVanilla( class RenderCtxVanilla(
val renderer: BlockModelRenderer, val renderer: BlockModelRenderer,
world: IBlockDisplayReader, blockCtx: BlockCtx,
pos: BlockPos,
val buffer: IVertexBuilder, val buffer: IVertexBuilder,
val combinedOverlay: Int, val combinedOverlay: Int,
matrixStack: MatrixStack, matrixStack: MatrixStack,
checkSides: Boolean, checkSides: Boolean,
random: Random, random: Random,
val seed: Long, val randomSeed: Long,
modelData: IModelData, modelData: IModelData,
val useAO: Boolean val useAO: Boolean
): RenderCtxBase(world, pos, matrixStack, checkSides, random, modelData) { ): RenderCtxBase(blockCtx, matrixStack, checkSides, random, modelData) {
override fun renderQuad(quad: HalfBakedQuad) { override fun renderQuad(quad: HalfBakedQuad) {
vertexLighter.updateLightmapAndColor(quad, lightingData) vertexLighter.updateLightmapAndColor(quad, lightingData)
@@ -47,20 +50,16 @@ class RenderCtxVanilla(
buffer: IVertexBuilder, buffer: IVertexBuilder,
checkSides: Boolean, checkSides: Boolean,
random: Random, random: Random,
rand: Long, seed: Long,
combinedOverlay: Int, combinedOverlay: Int,
modelData: IModelData, modelData: IModelData,
smooth: Boolean smooth: Boolean
): Boolean { ): Boolean {
random.setSeed(rand) val blockCtx = BasicBlockCtx(world, pos)
val ctx = RenderCtxVanilla(renderer, world, pos, buffer, combinedOverlay, matrixStack, checkSides, random, rand, modelData, smooth) // init context if missing (this is the first render layer)
lightingData.apply { val ctx = RenderCtxVanilla(renderer, blockCtx, buffer, combinedOverlay, matrixStack, checkSides, random, seed, modelData, smooth)
model.renderLayer(ctx, specialRenderData.get()!!, MinecraftForgeClient.getRenderLayer())
}
model.render(ctx, false)
return ctx.hasRendered return ctx.hasRendered
} }
} }
} }

View File

@@ -65,6 +65,9 @@ inline fun <T> MutableList<T>.exchange(idx1: Int, idx2: Int) {
/** Return a random element from the array using the provided random generator */ /** Return a random element from the array using the provided random generator */
inline operator fun <T> Array<T>.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size) inline operator fun <T> Array<T>.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size)
inline fun Random.idx(array: Array<*>) = nextInt(Int.MAX_VALUE) % array.size
inline fun Random.idxOrNull(array: Array<*>, predicate: ()->Boolean) = if (predicate()) idx(array) else null
fun <T> Iterable<T>.toImmutableList() = ImmutableList.builder<T>().let { builder -> fun <T> Iterable<T>.toImmutableList() = ImmutableList.builder<T>().let { builder ->
forEach { builder.add(it) } forEach { builder.add(it) }
builder.build() builder.build()

View File

@@ -2,6 +2,8 @@
package mods.betterfoliage.util package mods.betterfoliage.util
import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.BetterFoliageMod
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.World import net.minecraft.world.World
@@ -31,6 +33,11 @@ class ThreadLocalDelegate<T>(init: () -> T) {
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { tlVal.set(value) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { tlVal.set(value) }
} }
fun <T> ThreadLocal<T?>.getWithDefault(factory: ()->T): T {
get()?.let { return it }
return factory().apply { set(this) }
}
/** Call the supplied lambda and return its result, or the given default value if an exception is thrown. */ /** Call the supplied lambda and return its result, or the given default value if an exception is thrown. */
fun <T> tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable) { default } fun <T> tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable) { default }
@@ -56,6 +63,7 @@ abstract class HasLogger {
val detailLogger = BetterFoliageMod.detailLogger(this) 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. * Check if the Chunk containing the given [BlockPos] is loaded.
* Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances. * Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances.

View File

@@ -10,6 +10,7 @@
"MixinBlock", "MixinBlock",
"MixinBlockState", "MixinBlockState",
"MixinBlockModelRenderer", "MixinBlockModelRenderer",
"MixinChunkRendererDispatcher",
"MixinClientWorld", "MixinClientWorld",
"MixinModelBakery", "MixinModelBakery",
"MixinForgeBlockModelRenderer", "MixinForgeBlockModelRenderer",