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.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<ModelBakery>("net.minecraft.client.renderer.model.
val topUnbakedModels = FieldRef(this, "topUnbakedModels", mapRefMutable<ResourceLocation, IUnbakedModel>())
}
object RenderTypeLookup : ClassRef<RenderTypeLookup>("net.minecraft.client.renderer.RenderTypeLookup") {
val blockRenderChecks = FieldRef(this, "blockRenderChecks", mapRefMutable<IRegistryDelegate<Block>, Predicate<RenderType>>())
}
// Optifine
val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer")
val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM")

View File

@@ -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

View File

@@ -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() {

View File

@@ -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<WeightedModel>, baseModel: SpecialRenderModel
): IBakedModel by baseModel, SpecialRenderModel {
) : IBakedModel by baseModel, SpecialRenderModel {
class WeightedModel(val model: SpecialRenderModel, weight: Int) : WeightedRandom.Item(weight)
val totalWeight = models.sumBy { it.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
}

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)) }
data class TuftShapeKey(
val size: Double,
val height: Double,

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<ResourceLocation>) {
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 {

View File

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

View File

@@ -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 {

View File

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

View File

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

View File

@@ -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<Int?>,
val tuftIdx: Array<Int?>
): SpecialRenderData {
override fun canRenderInLayer(layer: RenderType) = when {
(crustIdx.any { it != null } || tuftIdx.any { it != null }) && layer == Layers.coral -> true
else -> false
}
}
class StandardSandModel(
wrapped: SpecialRenderModel
) : HalfBakedSpecialWrapper(wrapped) {
val coralLighting = Direction.values().mapArray { LightingPreferredFace(it) }
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
super.render(ctx, noDecorations)
if (noDecorations || !Config.enabled || !Config.coral.enabled(ctx.random)) return
if (ctx.biome?.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<Int?>(6) { null }
val tuftIdx = Array<Int?>(6) { null }
allDirections.filter { random.nextInt(64) < Config.coral.chance }.forEach { face ->
val isWater = ctx.state(face).material == Material.WATER
val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER
if (isDeepWater) {
crustIdx[face.ordinal] = random.idx(coralCrustModels)
tuftIdx[face.ordinal] = random.idx(coralTuftModels)
}
}
return SandRenderData(crustIdx, tuftIdx)
}
override fun renderLayer(ctx: RenderCtxBase, data: Any, layer: RenderType) {
super.renderLayer(ctx, data, layer)
if (data is SandRenderData && layer == Layers.coral) {
for (face in 0 until 6) {
ctx.vertexLighter = coralLighting[face]
ctx.renderQuads(coralCrustModels[face][ctx.random])
ctx.renderQuads(coralTuftModels[face][ctx.random])
data.crustIdx[face]?.let { ctx.renderQuads(coralCrustModels[face][it]) }
data.tuftIdx[face]?.let { ctx.renderQuads(coralTuftModels[face][it]) }
}
}
}

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.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

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 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 <reified T> withRenderData(renderFunc: (T)->Boolean) = (modelRenderData as? T?).let {
if (it == null) false else renderFunc(it)
}
val blockModelShapes = Minecraft.getInstance().blockRenderer.blockModelShaper
var vertexLighter: VanillaVertexLighter = VanillaFullBlockLighting
protected val lightingData = RenderCtxBase.lightingData.get().apply {
@@ -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<HalfBakedQuad>) {
@@ -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<Any?>()
}
}

View File

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

View File

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

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 */
inline operator fun <T> Array<T>.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size)
inline fun Random.idx(array: Array<*>) = nextInt(Int.MAX_VALUE) % array.size
inline fun Random.idxOrNull(array: Array<*>, predicate: ()->Boolean) = if (predicate()) idx(array) else null
fun <T> Iterable<T>.toImmutableList() = ImmutableList.builder<T>().let { builder ->
forEach { builder.add(it) }
builder.build()

View File

@@ -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<T>(init: () -> T) {
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { tlVal.set(value) }
}
fun <T> ThreadLocal<T?>.getWithDefault(factory: ()->T): T {
get()?.let { return it }
return factory().apply { set(this) }
}
/** Call the supplied lambda and return its result, or the given default value if an exception is thrown. */
fun <T> tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable) { default }
@@ -56,6 +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.

View File

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