Multi-layer rendering support
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
42
src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt
Normal file
42
src/main/kotlin/mods/betterfoliage/render/pipeline/Layers.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package mods.betterfoliage.render.pipeline
|
||||
|
||||
import mods.betterfoliage.model.SpecialRenderData
|
||||
import mods.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
|
||||
}
|
||||
}
|
||||
@@ -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?>()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"MixinBlock",
|
||||
"MixinBlockState",
|
||||
"MixinBlockModelRenderer",
|
||||
"MixinChunkRendererDispatcher",
|
||||
"MixinClientWorld",
|
||||
"MixinModelBakery",
|
||||
"MixinForgeBlockModelRenderer",
|
||||
|
||||
Reference in New Issue
Block a user