diff --git a/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt index cb388d4..a837565 100644 --- a/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt @@ -4,6 +4,7 @@ import me.zeroeightsix.fiber.JanksonSettings import mods.betterfoliage.chunk.ChunkOverlayManager import mods.betterfoliage.config.BlockConfig import mods.betterfoliage.config.MainConfig +import mods.betterfoliage.render.ShadersModIntegration import mods.betterfoliage.render.block.vanilla.* import mods.betterfoliage.render.particle.LeafParticleRegistry import mods.betterfoliage.render.particle.RisingSoulParticle @@ -83,6 +84,7 @@ object BetterFoliage : ClientModInitializer { MyceliumModel.Companion NetherrackModel.Companion RisingSoulParticle.Companion + ShadersModIntegration } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/CommonRefs.kt b/src/main/kotlin/mods/betterfoliage/CommonRefs.kt deleted file mode 100644 index 110033f..0000000 --- a/src/main/kotlin/mods/betterfoliage/CommonRefs.kt +++ /dev/null @@ -1,29 +0,0 @@ -package mods.betterfoliage - - -// Optifine -//val OptifineClassTransformer = ClassRefOld("optifine.OptiFineClassTransformer") -//val BlockPosM = ClassRefOld("net.optifine.BlockPosM") -//object ChunkCacheOF : ClassRefOld("net.optifine.override.ChunkCacheOF") { -// val chunkCache = FieldRefOld(this, "chunkCache", ChunkRendererRegion) -//} - -//object RenderEnv : ClassRefOld("net.optifine.render.RenderEnv") { -// val reset = MethodRefOld(this, "reset", void, BlockState, BlockPos) -//} - -// Optifine custom colors -//val IColorizer = ClassRefOld("net.optifine.CustomColors\$IColorizer") -//object CustomColors : ClassRefOld("net.optifine.CustomColors") { -// val getColorMultiplier = MethodRefOld(this, "getColorMultiplier", int, BakedQuad, BlockState, ExtendedBlockView, BlockPos, RenderEnv) -//} - -// Optifine shaders -//object SVertexBuilder : ClassRefOld("net.optifine.shaders.SVertexBuilder") { -// val pushState = MethodRefOld(this, "pushEntity", void, BlockState, BlockPos, ExtendedBlockView, BufferBuilder) -// val pushNum = MethodRefOld(this, "pushEntity", void, long) -// val pop = MethodRefOld(this, "popEntity", void) -//} - - - diff --git a/src/main/kotlin/mods/betterfoliage/config/MainConfig.kt b/src/main/kotlin/mods/betterfoliage/config/MainConfig.kt index eeafaa1..f582f09 100644 --- a/src/main/kotlin/mods/betterfoliage/config/MainConfig.kt +++ b/src/main/kotlin/mods/betterfoliage/config/MainConfig.kt @@ -39,6 +39,7 @@ class LeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) { val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring) val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring) val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring) + val shaderWind by boolean(true, langKey = recurring) } class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData { @@ -86,6 +87,7 @@ class LilypadConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationC override val enabled by boolean(true, langKey = recurring) val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring) override val population by population(16) + val shaderWind by boolean(true, langKey = recurring) } class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData { diff --git a/src/main/kotlin/mods/betterfoliage/render/ShadersModIntegration.kt b/src/main/kotlin/mods/betterfoliage/render/ShadersModIntegration.kt index b843828..d2c6cde 100644 --- a/src/main/kotlin/mods/betterfoliage/render/ShadersModIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/render/ShadersModIntegration.kt @@ -1,64 +1,54 @@ package mods.betterfoliage.render import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.util.get -import net.minecraft.block.BlockRenderType -import net.minecraft.block.BlockRenderType.MODEL +import mods.betterfoliage.render.lighting.getBufferBuilder +import mods.betterfoliage.util.getAllMethods +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext +import net.minecraft.block.BlockRenderLayer +import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED import net.minecraft.block.BlockState import net.minecraft.block.Blocks -import net.minecraft.util.math.BlockPos -import net.minecraft.world.ExtendedBlockView -import org.apache.logging.log4j.Level.INFO +import net.minecraft.client.render.BufferBuilder /** * Integration for ShadersMod. */ -/* object ShadersModIntegration { - @JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop) + val BufferBuilder_SVertexBuilder = BufferBuilder::class.java.fields.find { it.name == "sVertexBuilder" } + val SVertexBuilder_pushState = getAllMethods("net.optifine.shaders.SVertexBuilder", "pushEntity").find { it.parameterCount == 1 } + val SVertexBuilder_popState = getAllMethods("net.optifine.shaders.SVertexBuilder", "popEntity").find { it.parameterCount == 0 } + val BlockAliases_getAliasBlockId = getAllMethods("net.optifine.shaders.BlockAliases", "getAliasBlockId").firstOrNull() + + @JvmStatic val isAvailable = + listOf(BufferBuilder_SVertexBuilder).all { it != null } && + listOf(SVertexBuilder_pushState, SVertexBuilder_popState, BlockAliases_getAliasBlockId).all { it != null } val defaultLeaves = Blocks.OAK_LEAVES.defaultState val defaultGrass = Blocks.TALL_GRASS.defaultState - /** - * Called from transformed ShadersMod code. - * @see mods.betterfoliage.loader.BetterFoliageTransformer - */ - @JvmStatic fun getBlockStateOverride(state: BlockState, world: ExtendedBlockView, pos: BlockPos): BlockState { -// if (LeafRegistry[state, world, pos] != null) return defaultLeaves - if (BetterFoliage.blockConfig.crops.matchesClass(state.block)) return defaultGrass - return state - } - init { - BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }") + BetterFoliage.logger.info("[BetterFoliage] ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }") } - inline fun renderAs(ctx: CombinedContext, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) = - renderAs(ctx, ctx.state, renderType, enabled, func) - /** Quads rendered inside this block will use the given block entity data in shader programs. */ - inline fun renderAs(ctx: CombinedContext, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) { + inline fun renderAs(ctx: RenderContext, state: BlockState, layer: BlockRenderLayer, enabled: Boolean = true, func: ()->Unit) { if (isAvailable && enabled) { - val buffer = ctx.renderCtx.renderBuffer - val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder] - SVertexBuilder.pushState.invoke(sVertexBuilder!!, ctx.state, ctx.pos, ctx.world, buffer) + val sVertexBuilder = BufferBuilder_SVertexBuilder!!.get(ctx.getBufferBuilder(layer)) + val aliasBlockId = BlockAliases_getAliasBlockId!!.invoke(null, state) + SVertexBuilder_pushState!!.invoke(sVertexBuilder, aliasBlockId) func() - SVertexBuilder.pop.invoke(sVertexBuilder) + SVertexBuilder_popState!!.invoke(sVertexBuilder) } else { func() } } /** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */ - inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = - renderAs(ctx, defaultGrass, MODEL, enabled, func) + inline fun grass(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) = + renderAs(ctx, defaultGrass, CUTOUT_MIPPED, enabled, func) /** Quads rendered inside this block will behave as leaf blocks in shader programs. */ - inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = - renderAs(ctx, defaultLeaves, MODEL, enabled, func) + inline fun leaves(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) = + renderAs(ctx, defaultLeaves, CUTOUT_MIPPED, enabled, func) } - - - */ diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt index 7193b59..1f686c6 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Dirt.kt @@ -4,6 +4,7 @@ import mods.betterfoliage.BetterFoliage import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.render.DIRT_BLOCKS import mods.betterfoliage.render.SALTWATER_BIOMES +import mods.betterfoliage.render.ShadersModIntegration import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.render.lighting.reedLighting @@ -66,12 +67,16 @@ class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) { val random = randomSupplier.get() if (BetterFoliage.config.algae.enabled(random) && isDeepWater) { - context.withLighting(algaeLighting) { - it.accept(algaeModels[random]) + ShadersModIntegration.grass(context, BetterFoliage.config.algae.shaderWind) { + context.withLighting(algaeLighting) { + it.accept(algaeModels[random]) + } } } else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) { - context.withLighting(reedLighting) { - it.accept(reedModels[random]) + ShadersModIntegration.grass(context, BetterFoliage.config.reed.shaderWind) { + context.withLighting(reedLighting) { + it.accept(reedModels[random]) + } } } } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt index b902d7e..367db57 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Grass.kt @@ -3,6 +3,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.render.SNOW_MATERIALS +import mods.betterfoliage.render.ShadersModIntegration import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.util.Atlas @@ -78,8 +79,10 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra } if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) { - context.withLighting(tuftLighting) { - it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random]) + ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) { + context.withLighting(tuftLighting) { + it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random]) + } } } } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt index ee0f504..931a6b1 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Leaf.kt @@ -1,8 +1,11 @@ package mods.betterfoliage.render.block.vanilla +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction import mods.betterfoliage.BetterFoliage import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.render.SNOW_MATERIALS +import mods.betterfoliage.render.ShadersModIntegration +import mods.betterfoliage.render.lighting.getBufferBuilder import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.roundLeafLighting import mods.betterfoliage.render.particle.LeafParticleRegistry @@ -13,6 +16,11 @@ import mods.betterfoliage.resource.model.* import mods.betterfoliage.util.* import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext +import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessBufferBuilder +import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo +import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainMeshConsumer +import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext +import net.minecraft.block.BlockRenderLayer import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED import net.minecraft.block.BlockState import net.minecraft.client.render.model.BakedModel @@ -77,17 +85,19 @@ class NormalLeavesModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(w val leafLighting = roundLeafLighting() override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier, context: RenderContext) { - super.emitBlockQuads(blockView, state, pos, randomSupplier, context) - if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return + ShadersModIntegration.leaves(context, BetterFoliage.config.leaves.shaderWind) { + super.emitBlockQuads(blockView, state, pos, randomSupplier, context) + if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return - val ctx = BasicBlockCtx(blockView, pos) - val stateAbove = ctx.state(UP) - val isSnowed = stateAbove.material in SNOW_MATERIALS + val ctx = BasicBlockCtx(blockView, pos) + val stateAbove = ctx.state(UP) + val isSnowed = stateAbove.material in SNOW_MATERIALS - val random = randomSupplier.get() - context.withLighting(leafLighting) { - it.accept(leafNormal[random]) - if (isSnowed) it.accept(leafSnowed[random]) + val random = randomSupplier.get() + context.withLighting(leafLighting) { + it.accept(leafNormal[random]) + if (isSnowed) it.accept(leafSnowed[random]) + } } } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt index 8cd033e..877b23f 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Lilypad.kt @@ -1,6 +1,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.render.ShadersModIntegration import mods.betterfoliage.util.Atlas import mods.betterfoliage.resource.discovery.BlockRenderKey import mods.betterfoliage.resource.discovery.ModelDiscoveryBase @@ -39,7 +40,9 @@ class LilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) { if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return val random = randomSupplier.get() - context.meshConsumer().accept(lilypadRootModels[random]) + ShadersModIntegration.grass(context, BetterFoliage.config.lilypad.shaderWind) { + context.meshConsumer().accept(lilypadRootModels[random]) + } if (random.nextInt(64) < BetterFoliage.config.lilypad.population) { context.meshConsumer().accept(lilypadFlowerModels[random]) } diff --git a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt index a7258d7..ef46d04 100644 --- a/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt +++ b/src/main/kotlin/mods/betterfoliage/render/block/vanilla/Mycelium.kt @@ -1,6 +1,7 @@ package mods.betterfoliage.render.block.vanilla import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.render.ShadersModIntegration import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.util.Atlas @@ -45,8 +46,10 @@ class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) { BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } && blockView.getBlockState(pos + UP.offset).isAir ) { - context.withLighting(tuftLighting) { - it.accept(myceliumTuftModels[random]) + ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) { + context.withLighting(tuftLighting) { + it.accept(myceliumTuftModels[random]) + } } } } diff --git a/src/main/kotlin/mods/betterfoliage/render/lighting/Indigo.kt b/src/main/kotlin/mods/betterfoliage/render/lighting/Indigo.kt index f19ffc1..8b34c23 100644 --- a/src/main/kotlin/mods/betterfoliage/render/lighting/Indigo.kt +++ b/src/main/kotlin/mods/betterfoliage/render/lighting/Indigo.kt @@ -1,13 +1,15 @@ package mods.betterfoliage.render.lighting +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction import mods.betterfoliage.util.reflectField import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext +import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessBufferBuilder import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator import net.fabricmc.fabric.impl.client.indigo.renderer.render.* +import net.minecraft.block.BlockRenderLayer import net.minecraft.block.BlockState -import net.minecraft.client.MinecraftClient import net.minecraft.client.render.model.BakedModel import net.minecraft.util.math.BlockPos import net.minecraft.world.ExtendedBlockView @@ -25,13 +27,17 @@ fun TerrainMeshConsumer.modified() = MODIFIED_CONSUMER_POOL.get() ?: let { ModifiedTerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, transform) }.apply { MODIFIED_CONSUMER_POOL.set(this) } +val TerrainRenderContext_blockInfo = TerrainRenderContext::class.java.declaredFields.find { it.name == "blockInfo" }?.apply { isAccessible = true } +val BlockRenderInfo_layerIndexOrDefault = BlockRenderInfo::class.java.declaredMethods.find { it.name == "layerIndexOrDefault" }?.apply { isAccessible = true } +val AbstractQuadRenderer_bufferFunc = AbstractQuadRenderer::class.java.declaredFields.find { it.name == "bufferFunc" }?.apply { isAccessible = true } + /** * Render the given model at the given position. * Mutates the state of the [RenderContext]!! */ fun RenderContext.renderMasquerade(model: BakedModel, blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier, context: RenderContext) = when(this) { is TerrainRenderContext -> { - val blockInfo = reflectField("blockInfo") + val blockInfo = TerrainRenderContext_blockInfo!!.get(this) as BlockRenderInfo blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion()) (model as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context) } @@ -51,3 +57,14 @@ fun RenderContext.withLighting(lighter: CustomLighting, func: (Consumer)-> } else -> func(meshConsumer()) } + +/** Get the [BufferBuilder] responsible for a given [BlockRenderLayer] */ +fun RenderContext.getBufferBuilder(layer: BlockRenderLayer) = when(this) { + is TerrainRenderContext -> { + val blockInfo = TerrainRenderContext_blockInfo!!.get(this) as BlockRenderInfo + val layerIdx = BlockRenderInfo_layerIndexOrDefault!!.invoke(blockInfo, layer) as Int + val bufferFunc = AbstractQuadRenderer_bufferFunc!!.get(meshConsumer()) as Int2ObjectFunction + bufferFunc[layerIdx] + } + else -> null +} \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/util/Reflection.kt b/src/main/kotlin/mods/betterfoliage/util/Reflection.kt index 7fcfde4..7fdc0b5 100644 --- a/src/main/kotlin/mods/betterfoliage/util/Reflection.kt +++ b/src/main/kotlin/mods/betterfoliage/util/Reflection.kt @@ -16,18 +16,27 @@ fun Any.reflectField(name: String) = getFieldRecursive(this::class.java, nam it.get(this) as T } +/** Get the field on the class with the given name. + * Does not handle overloads, suitable only for unique field names (like Yarn intermediate names) + * */ fun getFieldRecursive(cls: Class<*>, name: String): Field = try { cls.getDeclaredField(name) } catch (e: NoSuchFieldException) { cls.superclass?.let { getFieldRecursive(it, name) } ?: throw e } +/** Get the method on the class with the given name. + * Does not handle overloads, suitable only for unique field names (like Yarn intermediate names) + * */ fun getMethodRecursive(cls: Class<*>, name: String): Method = try { cls.declaredMethods.find { it.name == name } ?: throw NoSuchMethodException() } catch (e: NoSuchMethodException) { cls.superclass?.let { getMethodRecursive(it, name) } ?: throw e } +fun getAllMethods(className: String, methodName: String): List = + tryDefault(null) { Class.forName(className) }?.declaredMethods?.filter { it.name == methodName } + ?: emptyList() interface FieldRef { val field: Field? @@ -66,7 +75,7 @@ object YarnHelper { try { val classMapped = resolver.mapClassName(INTERMEDIARY, className) val fieldMapped = resolver.mapFieldName(INTERMEDIARY, className, fieldName, descriptor) - Class.forName(classMapped)?.let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } } + Class.forName(classMapped).let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } } } catch (e: Exception) { logger.log( if (optional) Level.DEBUG else Level.ERROR, @@ -82,7 +91,7 @@ object YarnHelper { try { val classMapped = resolver.mapClassName(INTERMEDIARY, className) val methodMapped = resolver.mapMethodName(INTERMEDIARY, className, methodName, descriptor) - Class.forName(classMapped)?.let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } } + Class.forName(classMapped).let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } } } catch (e: Exception) { logger.log( if (optional) Level.DEBUG else Level.ERROR,