diff --git a/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt index d66a396..f877b75 100644 --- a/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt @@ -9,8 +9,15 @@ import net.minecraftforge.fml.common.event.FMLPostInitializationEvent import net.minecraftforge.fml.common.event.FMLPreInitializationEvent import net.minecraftforge.fml.common.network.NetworkCheckHandler import net.minecraftforge.fml.relauncher.Side +import org.apache.logging.log4j.Level.DEBUG import org.apache.logging.log4j.Level.INFO import org.apache.logging.log4j.Logger +import org.apache.logging.log4j.simple.SimpleLogger +import org.apache.logging.log4j.simple.SimpleLoggerContext +import org.apache.logging.log4j.util.PropertiesUtil +import java.io.File +import java.io.PrintStream +import java.util.* @Mod( modid = BetterFoliageMod.MOD_ID, @@ -28,6 +35,8 @@ object BetterFoliageMod { const val GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory" lateinit var log: Logger + lateinit var logDetail: Logger + var config: Configuration? = null var isAfterPostInit = false @@ -39,8 +48,16 @@ object BetterFoliageMod { @Mod.EventHandler fun preInit(event: FMLPreInitializationEvent) { log = event.modLog + logDetail = SimpleLogger( + "BetterFoliage", + DEBUG, + false, false, true, false, + "yyyy-MM-dd HH:mm:ss", + null, + PropertiesUtil(Properties()), + PrintStream(File(event.modConfigurationDirectory.parentFile, "logs/betterfoliage.log")) + ) config = Configuration(event.suggestedConfigurationFile, null, false) - } @Mod.EventHandler diff --git a/src/main/kotlin/mods/betterfoliage/client/Client.kt b/src/main/kotlin/mods/betterfoliage/client/Client.kt index 9b5efbd..3188fd1 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Client.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Client.kt @@ -5,10 +5,7 @@ import mods.betterfoliage.client.gui.ConfigGuiFactory import mods.betterfoliage.client.integration.OptifineCTM import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.render.* -import mods.betterfoliage.client.texture.GrassGenerator -import mods.betterfoliage.client.texture.GrassRegistry -import mods.betterfoliage.client.texture.LeafGenerator -import mods.betterfoliage.client.texture.LeafRegistry +import mods.betterfoliage.client.texture.* import mods.octarinecore.client.KeyHandler import mods.octarinecore.client.resource.CenteringTextureGenerator import mods.octarinecore.client.resource.GeneratorPack @@ -63,14 +60,21 @@ object Client { ) val singletons = listOf( - LeafRegistry, - GrassRegistry, + StandardLeafSupport, + StandardGrassSupport, LeafWindTracker, RisingSoulTextures, ShadersModIntegration, OptifineCTM ) - fun log(level: Level, msg: String) = BetterFoliageMod.log.log(level, msg) + fun log(level: Level, msg: String) { + BetterFoliageMod.log.log(level, msg) + BetterFoliageMod.logDetail.log(level, msg) + } + + fun logDetail(msg: String) { + BetterFoliageMod.logDetail.log(Level.DEBUG, msg) + } } diff --git a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt index 3c323f5..727f50b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt @@ -15,35 +15,38 @@ import net.minecraft.block.Block import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.VertexBuffer +import net.minecraft.client.renderer.block.model.IBakedModel import net.minecraft.init.Blocks import net.minecraft.util.BlockRenderLayer -import net.minecraft.util.BlockRenderLayer.* +import net.minecraft.util.BlockRenderLayer.CUTOUT +import net.minecraft.util.BlockRenderLayer.CUTOUT_MIPPED import net.minecraft.util.EnumFacing import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockAccess import net.minecraft.world.World +import net.minecraftforge.client.model.IModel import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly fun doesSideBlockRenderingOverride(original: Boolean, blockAccess: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean { - return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(blockAccess.getBlockState(pos).block)); + return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesClass(blockAccess.getBlockState(pos).block)); } fun isOpaqueCubeOverride(original: Boolean, state: IBlockState): Boolean { // caution: blocks are initialized and the method called during startup if (!BetterFoliageMod.isAfterPostInit) return original - return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(state.block)) + return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesClass(state.block)) } fun getAmbientOcclusionLightValueOverride(original: Float, state: IBlockState): Float { - if (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(state.block)) return Config.roundLogs.dimming; + if (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesClass(state.block)) return Config.roundLogs.dimming; return original; } fun getUseNeighborBrightnessOverride(original: Boolean, state: IBlockState): Boolean { - return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(state.block)); + return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesClass(state.block)); } fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) { @@ -57,7 +60,7 @@ fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) { if (Config.enabled && Config.fallingLeaves.enabled && - Config.blocks.leaves.matchesID(state.block) && + Config.blocks.leavesClasses.matchesClass(state.block) && world.isAirBlock(pos + down1) && Math.random() < Config.fallingLeaves.chance) { EntityFallingLeavesFX(world, pos).addIfValid() diff --git a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt index 5184a1e..a6372b5 100644 --- a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt +++ b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt @@ -31,9 +31,11 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI var enabled by boolean(true) object blocks { + val leavesClasses = BlockMatcher(BetterFoliageMod.DOMAIN, "LeavesBlocksDefault.cfg") + val leavesModels = ModelTextureListConfigOption(BetterFoliageMod.DOMAIN, "LeavesModelsDefault.cfg", 1) + val grassClasses = BlockMatcher(BetterFoliageMod.DOMAIN, "GrassBlocksDefault.cfg") + val grassModels = ModelTextureListConfigOption(BetterFoliageMod.DOMAIN, "GrassModelsDefault.cfg", 1) val dirt = BlockMatcher(BetterFoliageMod.DOMAIN, "DirtDefault.cfg") - val grass = BlockMatcher(BetterFoliageMod.DOMAIN, "GrassDefault.cfg") - val leaves = BlockMatcher(BetterFoliageMod.DOMAIN, "LeavesDefault.cfg") val crops = BlockMatcher(BetterFoliageMod.DOMAIN, "CropDefault.cfg") val logs = BlockMatcher(BetterFoliageMod.DOMAIN, "LogDefault.cfg") val sand = BlockMatcher(BetterFoliageMod.DOMAIN, "SandDefault.cfg") @@ -177,9 +179,17 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI val trailDensity by int(min=1, max=16, default=3) } + val forceReloadOptions = listOf( + blocks.leavesClasses, + blocks.leavesModels, + blocks.grassClasses, + blocks.grassModels, + shortGrass["saturationThreshold"] + ) + override fun onChange(event: ConfigChangedEvent.OnConfigChangedEvent) { super.onChange(event) - if (hasChanged(blocks, shortGrass["saturationThreshold"])) + if (hasChanged(forceReloadOptions)) Minecraft.getMinecraft().refreshResources() else Minecraft.getMinecraft().renderGlobal.loadRenderers() diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCTM.kt b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCTM.kt index f74e4ad..c3e815f 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCTM.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCTM.kt @@ -61,6 +61,9 @@ object OptifineCTM { return result } + fun getAllCTM(states: List, icon: TextureAtlasSprite): Collection = + states.flatMap { getAllCTM(it, icon) }.toSet() + fun override(texture: TextureAtlasSprite, ctx: BlockContext, face: EnumFacing) = override(texture, ctx.world!!, ctx.pos, face) diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt index 8f44b27..7c8e3b7 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt @@ -33,9 +33,9 @@ object ShadersModIntegration { * Called from transformed ShadersMod code. * @see mods.betterfoliage.loader.BetterFoliageTransformer */ - @JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long { - if (Config.blocks.leaves.matchesID(blockState.block)) return leavesEntityData - if (Config.blocks.crops.matchesID(blockState.block)) return tallGrassEntityData + @JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long { + if (Config.blocks.leavesClasses.matchesClass(blockState.block)) return leavesEntityData + if (Config.blocks.crops.matchesClass(blockState.block)) return tallGrassEntityData return original } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/AbstractRenderColumn.kt b/src/main/kotlin/mods/betterfoliage/client/render/AbstractRenderColumn.kt index 61570ef..9fb2928 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/AbstractRenderColumn.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/AbstractRenderColumn.kt @@ -1,6 +1,5 @@ package mods.betterfoliage.client.render -import mods.betterfoliage.client.config.BlockMatcher import mods.betterfoliage.client.integration.OptifineCTM import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.render.AbstractRenderColumn.BlockType.* @@ -8,6 +7,7 @@ import mods.betterfoliage.client.render.AbstractRenderColumn.QuadrantType.* import mods.octarinecore.client.render.* import mods.octarinecore.client.resource.BlockTextureInspector import mods.octarinecore.common.* +import mods.octarinecore.common.config.BlockMatcher import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.VertexBuffer diff --git a/src/main/kotlin/mods/betterfoliage/client/render/EntityFallingLeavesFX.kt b/src/main/kotlin/mods/betterfoliage/client/render/EntityFallingLeavesFX.kt index 60e2910..c6b65c0 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/EntityFallingLeavesFX.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/EntityFallingLeavesFX.kt @@ -2,6 +2,7 @@ package mods.betterfoliage.client.render import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.texture.LeafRegistry +import mods.betterfoliage.client.texture.defaultLeafColor import mods.octarinecore.PI2 import mods.octarinecore.client.render.AbstractEntityFX import mods.octarinecore.client.render.HSB @@ -42,9 +43,14 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble particleScale = Config.fallingLeaves.size.toFloat() * 0.1f val state = world.getBlockState(pos) - LeafRegistry[state, world, pos, DOWN]?.let { - particleTexture = it.particleTextures[rand.nextInt(1024)] - calculateParticleColor(it.averageColor, Minecraft.getMinecraft().blockColors.colorMultiplier(state, world, pos, 0)) + val blockColor = Minecraft.getMinecraft().blockColors.colorMultiplier(state, world, pos, 0) + val leafInfo = LeafRegistry.get(state, world, pos, DOWN) + if (leafInfo != null) { + particleTexture = leafInfo.particleTextures?.get(rand.nextInt(1024)) + calculateParticleColor(leafInfo.averageColor, blockColor) + } else { + particleTexture = LeafRegistry.particles["default"]?.get(rand.nextInt(1024)) + setColor(blockColor) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt index a964fd7..ce9c3d6 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt @@ -29,7 +29,7 @@ class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { ctx.cameraDistance < Config.algae.distance && ctx.blockState(up2).material == Material.WATER && ctx.blockState(up1).material == Material.WATER && - Config.blocks.dirt.matchesID(ctx.block) && + Config.blocks.dirt.matchesClass(ctx.block) && ctx.biomeId in Config.algae.biomes && noise[ctx.pos] < Config.algae.population diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt index c1d2fda..f20174b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt @@ -60,7 +60,7 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { override fun isEligible(ctx: BlockContext): Boolean = Config.enabled && Config.cactus.enabled && ctx.cameraDistance < Config.cactus.distance && - Config.blocks.cactus.matchesID(ctx.block) + Config.blocks.cactus.matchesClass(ctx.block) override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { // get AO data diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt index 62eae9f..b755c67 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt @@ -13,8 +13,8 @@ import net.minecraft.util.BlockRenderLayer class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { override fun isEligible(ctx: BlockContext) = Config.enabled && Config.connectedGrass.enabled && - Config.blocks.dirt.matchesID(ctx.block) && - Config.blocks.grass.matchesID(ctx.block(up1)) && + Config.blocks.dirt.matchesClass(ctx.block) && + Config.blocks.grassClasses.matchesClass(ctx.block(up1)) && (Config.connectedGrass.snowEnabled || !ctx.blockState(up2).isSnow) override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrassLog.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrassLog.kt index f7852cb..cc7b3e0 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrassLog.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrassLog.kt @@ -18,12 +18,12 @@ class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliageMod.M override fun isEligible(ctx: BlockContext) = Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass && - Config.blocks.dirt.matchesID(ctx.block) && - Config.blocks.logs.matchesID(ctx.block(up1)) + Config.blocks.dirt.matchesClass(ctx.block) && + Config.blocks.logs.matchesClass(ctx.block(up1)) override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { val grassDir = grassCheckDirs.find { - Config.blocks.grass.matchesID(ctx.block(it.offset)) + Config.blocks.grassClasses.matchesClass(ctx.block(it.offset)) } return if (grassDir != null) { diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt index 02f0a68..b399396 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt @@ -47,7 +47,7 @@ class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { ctx.cameraDistance < Config.coral.distance && (ctx.blockState(up2).material == Material.WATER || Config.coral.shallowWater) && ctx.blockState(up1).material == Material.WATER && - Config.blocks.sand.matchesID(ctx.block) && + Config.blocks.sand.matchesClass(ctx.block) && ctx.biomeId in Config.coral.biomes && noise[ctx.pos] < Config.coral.population diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt index 93a8223..edb84b6 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt @@ -3,7 +3,6 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config -import mods.betterfoliage.client.integration.OptifineCTM import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.texture.GrassRegistry import mods.octarinecore.client.render.* @@ -47,16 +46,17 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { Config.enabled && ctx.cameraDistance < Config.shortGrass.distance && (Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) && - Config.blocks.grass.matchesID(ctx.block) + GrassRegistry[ctx, UP] != null override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { - val isConnected = ctx.block(down1).let { Config.blocks.dirt.matchesID(it) || Config.blocks.grass.matchesID(it) } + val isConnected = ctx.block(down1).let { + Config.blocks.dirt.matchesClass(it) || + Config.blocks.grassClasses.matchesClass(it) + } val isSnowed = ctx.blockState(up1).isSnow val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled) - val grassInfo = GrassRegistry[ctx.blockState(Int3.zero)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, layer) - val grassTopTexture = OptifineCTM.override(grassInfo.grassTopTexture, ctx, UP) - + val grassInfo = GrassRegistry[ctx, UP]!! val blockColor = ctx.blockData(Int3.zero).color if (connectedGrass) { @@ -70,7 +70,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { fullCube, Rotation.identity, ctx.blockCenter, - icon = { ctx, qi, q -> grassTopTexture }, + icon = { ctx, qi, q -> grassInfo.grassTopTexture }, postProcess = { ctx, qi, q, vi, v -> rotateUV(2) if (isSnowed) { diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt index cc9f4f1..45e28cf 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt @@ -41,14 +41,14 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { Config.enabled && Config.leaves.enabled && ctx.cameraDistance < Config.leaves.distance && - Config.blocks.leaves.matchesID(ctx.block) + LeafRegistry[ctx, DOWN] != null override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { val isSnowed = ctx.blockState(up1).material.let { it == Material.SNOW || it == Material.CRAFTED_SNOW } renderWorldBlockBase(ctx, dispatcher, renderer, null) - val leafInfo = LeafRegistry[ctx, DOWN] ?: return false + val leafInfo = LeafRegistry[ctx, DOWN]!! val blockColor = ctx.blockData(Int3.zero).color modelRenderer.updateShading(Int3.zero, allFaces) diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt index eed4f08..570d848 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt @@ -39,7 +39,7 @@ class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { override fun isEligible(ctx: BlockContext): Boolean = Config.enabled && Config.lilypad.enabled && ctx.cameraDistance < Config.lilypad.distance && - Config.blocks.lilypad.matchesID(ctx.block) + Config.blocks.lilypad.matchesClass(ctx.block) override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { renderWorldBlockBase(ctx, dispatcher, renderer, null) diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt index 02ee7df..fd4f64b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt @@ -2,17 +2,12 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.config.Config -import mods.betterfoliage.client.integration.OptifineCTM import mods.octarinecore.client.render.BlockContext -import mods.octarinecore.client.render.Quad -import mods.octarinecore.client.render.ShadingContext -import mods.octarinecore.client.render.blockContext import mods.octarinecore.common.Int3 -import mods.octarinecore.common.rotate import mods.octarinecore.tryDefault import net.minecraft.block.BlockLog import net.minecraft.block.state.IBlockState -import net.minecraft.util.EnumFacing.* +import net.minecraft.util.EnumFacing.Axis class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) { @@ -21,7 +16,7 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) { override fun isEligible(ctx: BlockContext) = Config.enabled && Config.roundLogs.enabled && ctx.cameraDistance < Config.roundLogs.distance && - Config.blocks.logs.matchesID(ctx.block) + Config.blocks.logs.matchesClass(ctx.block) override var axisFunc = { state: IBlockState -> val axis = tryDefault(null) { state.getValue(BlockLog.LOG_AXIS).toString() } ?: @@ -44,8 +39,8 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) { override fun resolver(ctx: BlockContext): ColumnTextureResolver? = columnTextures[ctx.blockState(Int3.zero)] - override val blockPredicate = { state: IBlockState -> Config.blocks.logs.matchesID(state.block) } - override val surroundPredicate = { state: IBlockState -> state.isOpaqueCube && !Config.blocks.logs.matchesID(state.block) } + override val blockPredicate = { state: IBlockState -> Config.blocks.logs.matchesClass(state.block) } + override val surroundPredicate = { state: IBlockState -> state.isOpaqueCube && !Config.blocks.logs.matchesClass(state.block) } override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular override val connectSolids: Boolean get() = Config.roundLogs.connectSolids diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt index 9368a27..978b9fd 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt @@ -46,7 +46,7 @@ class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) { ctx.cameraDistance < Config.reed.distance && ctx.blockState(up2).material == Material.AIR && ctx.blockState(up1).material == Material.WATER && - Config.blocks.dirt.matchesID(ctx.block) && + Config.blocks.dirt.matchesClass(ctx.block) && ctx.biomeId in Config.reed.biomes && noise[ctx.pos] < Config.reed.population diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt index d380776..0211fc6 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt @@ -1,16 +1,27 @@ package mods.betterfoliage.client.texture -import mods.betterfoliage.client.Client +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.config.Config +import mods.betterfoliage.client.integration.OptifineCTM +import mods.octarinecore.client.render.BlockContext import mods.octarinecore.client.render.HSB -import mods.octarinecore.client.resource.BlockTextureInspector +import mods.octarinecore.client.resource.TextureListModelProcessor +import mods.octarinecore.client.resource.TextureMediatedRegistry import mods.octarinecore.client.resource.averageColor +import mods.octarinecore.client.resource.get +import mods.octarinecore.common.Int3 +import mods.octarinecore.common.config.BlockMatcher +import mods.octarinecore.common.config.ModelTextureList +import mods.octarinecore.findFirst import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureMap +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.IBlockAccess +import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly -import org.apache.logging.log4j.Level.INFO const val defaultGrassColor = 0 @@ -23,28 +34,66 @@ class GrassInfo( * Color to use for Short Grass rendering instead of the biome color. * * Value is null if the texture is mostly grey (the saturation of its average color is under a configurable limit), - * the average color of the texture (significantly brightened) otherwise. + * the average color of the texture (significantly ) otherwise. */ val overrideColor: Int? ) +interface IGrassRegistry { + fun get(state: IBlockState): GrassInfo? + fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): GrassInfo? +} + /** Collects and manages rendering-related information for grass blocks. */ @SideOnly(Side.CLIENT) -object GrassRegistry : BlockTextureInspector() { +object GrassRegistry : IGrassRegistry { + val subRegistries = mutableListOf(StandardGrassSupport) - init { - matchClassAndModel(Config.blocks.grass, "block/grass", listOf("top")) - matchClassAndModel(Config.blocks.grass, "block/cube_bottom_top", listOf("top")) + override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) = + subRegistries.findFirst { it.get(state, world, pos, face) } + + operator fun get(ctx: BlockContext, face: EnumFacing) = get(ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, face) + + override fun get(state: IBlockState) = subRegistries.findFirst { it.get(state) } +} + +object StandardGrassSupport : + TextureListModelProcessor, + TextureMediatedRegistry, GrassInfo>, + IGrassRegistry +{ + init { MinecraftForge.EVENT_BUS.register(this) } + + override var stateToKey = mutableMapOf>() + override var stateToValue = mapOf() + override var textureToValue = mutableMapOf() + + override val logger = BetterFoliageMod.logDetail + override val logName = "StandardGrassSupport" + override val matchClasses: BlockMatcher get() = Config.blocks.grassClasses + override val modelTextures: List get() = Config.blocks.grassModels.list + + override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): GrassInfo? { + val baseTexture = stateToValue[state] ?: return null + return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture] } - override fun onAfterModelLoad() { - super.onAfterModelLoad() - Client.log(INFO, "Inspecting grass textures") + override fun get(state: IBlockState) = StandardLeafSupport.stateToValue[state].let { + if (it == null) null else textureToValue[it] } - override fun processTextures(state: IBlockState, textures: List, atlas: TextureMap): GrassInfo { - val hsb = HSB.fromColor(textures[0].averageColor ?: defaultGrassColor) + override fun processStitch(state: IBlockState, key: List, atlas: TextureMap) = atlas[key[0]] + + override fun processTexture(states: List, texture: TextureAtlasSprite, atlas: TextureMap) { + registerGrass(texture, atlas) + OptifineCTM.getAllCTM(states, texture).forEach { + registerGrass(it, atlas) + } + } + + fun registerGrass(texture: TextureAtlasSprite, atlas: TextureMap) { + val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor) val overrideColor = if (hsb.saturation > Config.shortGrass.saturationThreshold) hsb.copy(brightness = 0.8f).asColor else null - return GrassInfo(textures[0], overrideColor) + textureToValue[texture] = GrassInfo(texture, overrideColor) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt index 4f66445..35377a8 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt @@ -1,24 +1,29 @@ package mods.betterfoliage.client.texture +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.OptifineCTM import mods.octarinecore.client.render.BlockContext -import mods.octarinecore.client.resource.BlockTextureInspector -import mods.octarinecore.client.resource.IconSet -import mods.octarinecore.client.resource.averageColor +import mods.octarinecore.client.resource.* import mods.octarinecore.common.Int3 +import mods.octarinecore.common.config.BlockMatcher +import mods.octarinecore.common.config.ModelTextureList +import mods.octarinecore.findFirst import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureMap import net.minecraft.util.EnumFacing -import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockAccess +import net.minecraftforge.client.event.TextureStitchEvent +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly -import org.apache.logging.log4j.Level.INFO -import org.apache.logging.log4j.Level.WARN +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.Logger const val defaultLeafColor = 0 @@ -34,61 +39,94 @@ class LeafInfo( val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor ) { /** [IconSet] of the textures to use for leaf particles emitted from this block. */ - val particleTextures: IconSet get() = LeafRegistry.particles[leafType]!! + val particleTextures: IconSet? get() = LeafRegistry.particles[leafType] } -/** Collects and manages rendering-related information for leaf blocks. */ -@SideOnly(Side.CLIENT) -object LeafRegistry : BlockTextureInspector() { +interface ILeafRegistry { + operator fun get(state: IBlockState): LeafInfo? + operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? +} - val leaves: MutableMap = hashMapOf() - val particles: MutableMap = hashMapOf() +/** Collects and manages rendering-related information for grass blocks. */ +object LeafRegistry : ILeafRegistry { + val subRegistries: MutableList = mutableListOf(StandardLeafSupport) val typeMappings = TextureMatcher() + val particles = hashMapOf() - init { - matchClassAndModel(Config.blocks.leaves, "minecraft:block/leaves", listOf("all")) - matchClassAndModel(Config.blocks.leaves, "minecraft:block/cube_all", listOf("all")) - matchClassAndModel(Config.blocks.leaves, "biomesoplenty:block/leaves_overlay", listOf("under")) + init { MinecraftForge.EVENT_BUS.register(this) } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + fun handlePreStitch(event: TextureStitchEvent.Pre) { + particles.clear() } - operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? { - val baseTexture = get(state) ?: return null - return leaves[OptifineCTM.override(baseTexture, world, pos, face)] ?: leaves[baseTexture] - } + override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) = + subRegistries.findFirst { it.get(state, world, pos, face) } operator fun get(ctx: BlockContext, face: EnumFacing) = get(ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, face) - override fun onAfterModelLoad() { - super.onAfterModelLoad() - Client.log(INFO, "Inspecting leaf textures") - particles.clear() - typeMappings.loadMappings(ResourceLocation("betterfoliage", "leafTextureMappings.cfg")) - } + override fun get(state: IBlockState) = subRegistries.findFirst { it.get(state) } - override fun processTextures(state: IBlockState, textures: List, atlas: TextureMap): TextureAtlasSprite { - val texture = textures[0] - registerLeaf(texture, atlas) - OptifineCTM.getAllCTM(state, texture).forEach { registerLeaf(it, atlas) } - return texture - } - - fun registerLeaf(texture: TextureAtlasSprite, atlas: TextureMap) { + fun getParticleType(texture: TextureAtlasSprite, atlas: TextureMap): String { var leafType = typeMappings.getType(texture) ?: "default" - val generated = atlas.registerSprite( - Client.genLeaves.generatedResource(texture.iconName, "type" to leafType) - ) - if (leafType !in particles.keys) { val particleSet = IconSet("betterfoliage", "blocks/falling_leaf_${leafType}_%d") particleSet.onStitch(atlas) if (particleSet.num == 0) { - Client.log(WARN, "Leaf particle textures not found for leaf type: $leafType") + Client.log(Level.WARN, "Leaf particle textures not found for leaf type: $leafType") leafType == "default" } else { particles.put(leafType, particleSet) } } - - leaves[texture] = LeafInfo(generated, leafType) + return leafType } -} \ No newline at end of file +} + + +@SideOnly(Side.CLIENT) +object StandardLeafSupport : + TextureListModelProcessor, + TextureMediatedRegistry, LeafInfo>, + ILeafRegistry +{ + + init { MinecraftForge.EVENT_BUS.register(this) } + + override val logName = "StandardLeafSupport" + override val matchClasses: BlockMatcher get() = Config.blocks.leavesClasses + override val modelTextures: List get() = Config.blocks.leavesModels.list + override val logger: Logger? get() = BetterFoliageMod.logDetail + + override var stateToKey = mutableMapOf>() + override var stateToValue = mapOf() + override var textureToValue = mutableMapOf() + + override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? { + val baseTexture = stateToValue[state] ?: return null + return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture] + } + + override fun get(state: IBlockState) = stateToValue[state].let { + if (it == null) null else textureToValue[it] + } + + override fun processStitch(state: IBlockState, key: List, atlas: TextureMap) = atlas[key[0]] + + override fun processTexture(states: List, texture: TextureAtlasSprite, atlas: TextureMap) { + registerLeaf(texture, atlas) + OptifineCTM.getAllCTM(states, texture).forEach { + registerLeaf(it, atlas) + } + } + + fun registerLeaf(texture: TextureAtlasSprite, atlas: TextureMap) { + var leafType = LeafRegistry.typeMappings.getType(texture) ?: "default" + val generated = atlas.registerSprite( + Client.genLeaves.generatedResource(texture.iconName, "type" to leafType) + ) + textureToValue[texture] = LeafInfo(generated, LeafRegistry.getParticleType(texture, atlas)) + } + +} + diff --git a/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt b/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt index 6d9c9eb..f322001 100644 --- a/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt +++ b/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt @@ -5,7 +5,6 @@ import mods.octarinecore.metaprog.Transformer import mods.octarinecore.metaprog.allAvailable import net.minecraftforge.fml.relauncher.FMLLaunchHandler import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin -import org.objectweb.asm.Opcodes import org.objectweb.asm.Opcodes.* @IFMLLoadingPlugin.TransformerExclusions( diff --git a/src/main/kotlin/mods/betterfoliage/loader/Refs.kt b/src/main/kotlin/mods/betterfoliage/loader/Refs.kt index c0b54c0..2375d2d 100644 --- a/src/main/kotlin/mods/betterfoliage/loader/Refs.kt +++ b/src/main/kotlin/mods/betterfoliage/loader/Refs.kt @@ -61,12 +61,15 @@ object Refs { val VanillaModelWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$VanillaModelWrapper") val model_VMW = FieldRef(VanillaModelWrapper, "model", ModelBlock) val location_VMW = FieldRef(VanillaModelWrapper, "location", ModelBlock) - val WeightedPartWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedPartWrapper") - val model_WPW = FieldRef(WeightedPartWrapper, "model", IModel) +// val WeightedPartWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedPartWrapper") +// val model_WPW = FieldRef(WeightedPartWrapper, "model", IModel) val WeightedRandomModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedRandomModel") val models_WRM = FieldRef(WeightedRandomModel, "models", List) val MultiModel = ClassRef("net.minecraftforge.client.model.MultiModel") val base_MM = FieldRef(MultiModel, "base", IModel) + val WeightedBakedModel = ClassRef("net.minecraft.client.renderer.block.model.WeightedBakedModel") + val models_WBM = FieldRef(WeightedBakedModel, "models", List) + // Better Foliage val BetterFoliageHooks = ClassRef("mods.betterfoliage.client.Hooks") @@ -76,6 +79,7 @@ object Refs { val isOpaqueCubeOverride = MethodRef(BetterFoliageHooks, "isOpaqueCubeOverride", ClassRef.boolean, ClassRef.boolean, IBlockState) val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, World, IBlockState, BlockPos) val onAfterLoadModelDefinitions = MethodRef(BetterFoliageHooks, "onAfterLoadModelDefinitions", ClassRef.void, ModelLoader) + val onAfterBakeModels = MethodRef(BetterFoliageHooks, "onAfterBakeModels", ClassRef.void, Map) val renderWorldBlock = MethodRef(BetterFoliageHooks, "renderWorldBlock", ClassRef.boolean, BlockRendererDispatcher, IBlockState, BlockPos, IBlockAccess, VertexBuffer, BlockRenderLayer) val canRenderBlockInLayer = MethodRef(BetterFoliageHooks, "canRenderBlockInLayer", ClassRef.boolean, Block, IBlockState, BlockRenderLayer) diff --git a/src/main/kotlin/mods/octarinecore/Utils.kt b/src/main/kotlin/mods/octarinecore/Utils.kt index b24f067..5365bf2 100644 --- a/src/main/kotlin/mods/octarinecore/Utils.kt +++ b/src/main/kotlin/mods/octarinecore/Utils.kt @@ -33,6 +33,14 @@ inline fun forEachNested(list1: Iterable, list2: Iterable, func } } +@Suppress("UNCHECKED_CAST") +inline fun Map.filterValuesNotNull() = filterValues { it != null } as Map + +inline fun Iterable.findFirst(func: (T)->R?): R? { + forEach { func(it)?.let { return it } } + return null +} + /** * Property-level delegate backed by a [ThreadLocal]. * diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ModelDataInspector.kt b/src/main/kotlin/mods/octarinecore/client/resource/ModelDataInspector.kt index c7c22a6..2763ce8 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/ModelDataInspector.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/ModelDataInspector.kt @@ -1,11 +1,9 @@ package mods.octarinecore.client.resource -import mods.betterfoliage.client.config.BlockMatcher import mods.betterfoliage.loader.Refs -import mods.octarinecore.stripStart +import mods.octarinecore.common.config.BlockMatcher import net.minecraft.block.Block import net.minecraft.block.state.IBlockState -import net.minecraft.client.renderer.block.model.ModelBlock import net.minecraft.client.renderer.block.model.ModelResourceLocation import net.minecraft.client.renderer.block.statemap.DefaultStateMapper import net.minecraft.client.renderer.block.statemap.IStateMapper @@ -34,8 +32,8 @@ abstract class ModelDataInspector { @SubscribeEvent fun handleLoadModelData(event: LoadModelDataEvent) { val stateMappings = Block.REGISTRY.flatMap { block -> - ((event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()) - .putStateModelLocations(block as Block) as Map).entries + val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper() + (mapper.putStateModelLocations(block as Block) as Map).entries } val stateModels = Refs.stateModels.get(event.loader) as Map @@ -96,31 +94,4 @@ abstract class BlockTextureInspector : ModelDataInspector() { } abstract fun processTextures(state: IBlockState, textures: List, atlas: TextureMap): T -} - -@Suppress("UNCHECKED_CAST") -val IModel.modelBlockAndLoc: Pair? get() { - if (Refs.VanillaModelWrapper.isInstance(this)) - return Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation) - else if (Refs.WeightedPartWrapper.isInstance(this)) Refs.model_WPW.get(this)?.let { - return (it as IModel).modelBlockAndLoc - } - else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let { - (it as List).forEach { - it.modelBlockAndLoc.let { if (it != null) return it } - } - } - else if (Refs.MultiModel.isInstance(this)) Refs.base_MM.get(this)?.let { - return (it as IModel).modelBlockAndLoc - } - return null -} - -fun Pair.derivesFrom(targetLocation: String): Boolean { - if (second.stripStart("models/") == ResourceLocation(targetLocation)) return true - if (first.parent != null && first.parentLocation != null) - return Pair(first.parent, first.parentLocation!!).derivesFrom(targetLocation) - return false -} - -fun IModel.derivesFromModel(modelLocation: String) = modelBlockAndLoc?.derivesFrom(modelLocation) ?: false \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt b/src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt new file mode 100644 index 0000000..d85787d --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt @@ -0,0 +1,104 @@ +package mods.octarinecore.client.resource + +import com.google.common.base.Joiner +import mods.betterfoliage.loader.Refs +import mods.octarinecore.common.config.BlockMatcher +import mods.octarinecore.common.config.ModelTextureList +import mods.octarinecore.filterValuesNotNull +import net.minecraft.block.Block +import net.minecraft.block.state.IBlockState +import net.minecraft.client.renderer.block.model.ModelResourceLocation +import net.minecraft.client.renderer.block.statemap.DefaultStateMapper +import net.minecraft.client.renderer.block.statemap.IStateMapper +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.client.renderer.texture.TextureMap +import net.minecraftforge.client.event.TextureStitchEvent +import net.minecraftforge.client.model.IModel +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.Logger + +interface ModelProcessor { + val logger: Logger? + var stateToKey: MutableMap + var stateToValue: Map + + fun onPostLoad() { } + fun onPreStitch() { } + + fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): T1? + fun processStitch(state: IBlockState, key: T1, atlas: TextureMap): T2? + + @SubscribeEvent + fun handleLoadModelData(event: LoadModelDataEvent) { + stateToKey.clear() + onPostLoad() + + val stateMappings = Block.REGISTRY.flatMap { block -> + val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper() + (mapper.putStateModelLocations(block as Block) as Map).entries + } + val stateModels = Refs.stateModels.get(event.loader) as Map + + stateMappings.forEach { mapping -> + if (mapping.key.block != null) stateModels[mapping.value]?.let { model -> + processModelLoad(mapping.key, mapping.value, model)?.let { key -> stateToKey.put(mapping.key, key) } + } + } + } + + @Suppress("UNCHECKED_CAST") + @SubscribeEvent(priority = EventPriority.LOW) + fun handlePreStitch(event: TextureStitchEvent.Pre) { + onPreStitch() + stateToValue = stateToKey.mapValues { processStitch(it.key, it.value, event.map) }.filterValuesNotNull() + } +} + +interface TextureListModelProcessor : ModelProcessor, T2> { + val logName: String + val matchClasses: BlockMatcher + val modelTextures: List + + override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): List? { + val matchClass = matchClasses.matchingClass(state.block) ?: return null + logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}") + logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}") + + val blockLoc = model.modelBlockAndLoc + if (blockLoc == null) { + logger?.log(Level.DEBUG, "$logName: no models found") + return null + } + val modelMatch = modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) } + if (modelMatch == null) { + logger?.log(Level.DEBUG, "$logName: no matching models found") + return null + } + logger?.log(Level.DEBUG, "$logName: model ${blockLoc.second} matches ${modelMatch.modelLocation.toString()}") + + val textures = modelMatch.textureNames.map { it to blockLoc.first.resolveTextureName(it) } + val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" }) + logger?.log(Level.DEBUG, "$logName: textures [$texMapString]") + + return if (textures.all { it.second != "missingno" }) textures.map { it.second } else null + } +} + +interface TextureMediatedRegistry : ModelProcessor { + + var textureToValue: MutableMap + + @Suppress("UNCHECKED_CAST") + // @SubscribeEvent(priority = EventPriority.LOW) + override fun handlePreStitch(event: TextureStitchEvent.Pre) { + textureToValue.clear() + super.handlePreStitch(event) + + val textureToStates = stateToValue.entries.groupBy(keySelector = { it.value }, valueTransform = { it.key }) + stateToValue.values.toSet().forEach { processTexture(textureToStates[it]!!, it, event.map) } + } + + fun processTexture(states: List, texture: TextureAtlasSprite, atlas: TextureMap) +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt index 48b8e1c..512c769 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt @@ -1,15 +1,20 @@ @file:JvmName("Utils") package mods.octarinecore.client.resource +import mods.betterfoliage.loader.Refs import mods.octarinecore.PI2 import mods.octarinecore.client.render.HSB +import mods.octarinecore.stripStart import mods.octarinecore.tryDefault import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.block.model.ModelBlock import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.client.renderer.texture.TextureMap import net.minecraft.client.resources.IResource import net.minecraft.client.resources.IResourceManager import net.minecraft.client.resources.SimpleReloadableResourceManager import net.minecraft.util.ResourceLocation +import net.minecraftforge.client.model.IModel import java.awt.image.BufferedImage import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -29,6 +34,9 @@ operator fun IResourceManager.get(domain: String, path: String): IResource? = ge /** Index operator to get a resource. */ operator fun IResourceManager.get(location: ResourceLocation): IResource? = tryDefault(null) { getResource(location) } +/** Index operator to get a texture sprite. */ +operator fun TextureMap.get(name: String): TextureAtlasSprite? = getTextureExtry(name) + /** Load an image resource. */ fun IResource.loadImage() = ImageIO.read(this.inputStream) @@ -89,4 +97,29 @@ val TextureAtlasSprite.averageColor: Int? get() { fun textureLocation(iconName: String) = ResourceLocation(iconName).let { if (it.resourcePath.startsWith("mcpatcher")) it else ResourceLocation(it.resourceDomain, "textures/${it.resourcePath}") -} \ No newline at end of file +} + +@Suppress("UNCHECKED_CAST") +val IModel.modelBlockAndLoc: Pair? get() { + if (Refs.VanillaModelWrapper.isInstance(this)) + return Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation) + else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let { + (it as List).forEach { + it.modelBlockAndLoc.let { if (it != null) return it } + } + } + else if (Refs.MultiModel.isInstance(this)) Refs.base_MM.get(this)?.let { + return (it as IModel).modelBlockAndLoc + } + // TODO support net.minecraftforge.client.model.ModelLoader.MultipartModel + return null +} + +fun Pair.derivesFrom(targetLocation: ResourceLocation): Boolean { + if (second.stripStart("models/") == targetLocation) return true + if (first.parent != null && first.parentLocation != null) + return Pair(first.parent, first.parentLocation!!).derivesFrom(targetLocation) + return false +} + +fun IModel.derivesFromModel(modelLoc: String) = modelBlockAndLoc?.derivesFrom(ResourceLocation(modelLoc)) ?: false diff --git a/src/main/kotlin/mods/betterfoliage/client/config/BlockMatcher.kt b/src/main/kotlin/mods/octarinecore/common/config/BlackWhiteListConfigOption.kt similarity index 50% rename from src/main/kotlin/mods/betterfoliage/client/config/BlockMatcher.kt rename to src/main/kotlin/mods/octarinecore/common/config/BlackWhiteListConfigOption.kt index fb5fe3a..8b60d06 100644 --- a/src/main/kotlin/mods/betterfoliage/client/config/BlockMatcher.kt +++ b/src/main/kotlin/mods/octarinecore/common/config/BlackWhiteListConfigOption.kt @@ -1,41 +1,23 @@ -package mods.betterfoliage.client.config +package mods.octarinecore.common.config import mods.octarinecore.client.gui.NonVerboseArrayEntry import mods.octarinecore.client.resource.get import mods.octarinecore.client.resource.getLines import mods.octarinecore.client.resource.resourceManager -import mods.octarinecore.common.config.ConfigPropertyBase -import mods.octarinecore.metaprog.getJavaClass -import net.minecraft.block.Block -import net.minecraft.client.multiplayer.WorldClient -import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.config.Configuration import net.minecraftforge.common.config.Property -import net.minecraftforge.event.world.WorldEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -/** - * Match blocks based on their class names. Caches block IDs for faster lookup. - * - * @param[domain] resource domain for defaults file - * @param[path] resource path for defaults file - */ -class BlockMatcher(val domain: String, val path: String) : ConfigPropertyBase() { +abstract class BlackWhiteListConfigOption(val domain: String, val path: String) : ConfigPropertyBase() { - val blackList = mutableListOf>() - val whiteList = mutableListOf>() - val blockIDs = hashSetOf() + val blackList = mutableListOf() + val whiteList = mutableListOf() var blacklistProperty: Property? = null var whitelistProperty: Property? = null - fun matchesClass(block: Block): Boolean { - val blockClass = block.javaClass - blackList.forEach { if (it.isAssignableFrom(blockClass)) return false } - whiteList.forEach { if (it.isAssignableFrom(blockClass)) return true } - return false - } - fun matchesID(block: Block) = blockIDs.contains(Block.REGISTRY.getIDForObject(block)) - fun matchesID(blockId: Int) = blockIDs.contains(blockId) + override val hasChanged: Boolean + get() = blacklistProperty?.hasChanged() ?: false || whitelistProperty?.hasChanged() ?: false + + override val guiProperties: List get() = listOf(whitelistProperty!!, blacklistProperty!!) override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) { lang = null @@ -43,32 +25,24 @@ class BlockMatcher(val domain: String, val path: String) : ConfigPropertyBase() blacklistProperty = target.get(categoryName, "${propertyName}Blacklist", defaults.first) whitelistProperty = target.get(categoryName, "${propertyName}Whitelist", defaults.second) listOf(blacklistProperty!!, whitelistProperty!!).forEach { - it.setConfigEntryClass(NonVerboseArrayEntry::class.java) - it.setLanguageKey("$langPrefix.$categoryName.${it.name}") + it.configEntryClass = NonVerboseArrayEntry::class.java + it.languageKey = "$langPrefix.$categoryName.${it.name}" } read() } + abstract fun convertValue(line: String): VALUE? + override fun read() { listOf(Pair(blackList, blacklistProperty!!), Pair(whiteList, whitelistProperty!!)).forEach { it.first.clear() - it.first.addAll(it.second.stringList.map { getJavaClass(it) }.filterNotNull()) - } - updateIDs() - } - - fun updateIDs() { - blockIDs.clear() - Block.REGISTRY.forEach { - if (matchesClass(it as Block)) blockIDs.add(Block.REGISTRY.getIDForObject(it)) + it.second.stringList.forEach { line -> + val value = convertValue(line) + if (value != null) it.first.add(value) + } } } - override val hasChanged: Boolean - get() = blacklistProperty?.hasChanged() ?: false || whitelistProperty?.hasChanged() ?: false - - override val guiProperties: List get() = listOf(whitelistProperty!!, blacklistProperty!!) - fun readDefaults(domain: String, path: String): Pair, Array> { val blackList = arrayListOf() val whiteList = arrayListOf() @@ -79,10 +53,4 @@ class BlockMatcher(val domain: String, val path: String) : ConfigPropertyBase() } return (blackList.toTypedArray() to whiteList.toTypedArray()) } - - @SubscribeEvent - fun onWorldLoad(event: WorldEvent.Load) { if (event.world is WorldClient) updateIDs() } - - init { MinecraftForge.EVENT_BUS.register(this) } - } \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/common/config/Matchers.kt b/src/main/kotlin/mods/octarinecore/common/config/Matchers.kt new file mode 100644 index 0000000..eea8f64 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/common/config/Matchers.kt @@ -0,0 +1,33 @@ +package mods.octarinecore.common.config + +import mods.octarinecore.metaprog.getJavaClass +import net.minecraft.block.Block +import net.minecraft.util.ResourceLocation + +class BlockMatcher(domain: String, path: String) : BlackWhiteListConfigOption>(domain, path) { + override fun convertValue(line: String) = getJavaClass(line) + + fun matchesClass(block: Block): Boolean { + val blockClass = block.javaClass + blackList.forEach { if (it.isAssignableFrom(blockClass)) return false } + whiteList.forEach { if (it.isAssignableFrom(blockClass)) return true } + return false + } + + fun matchingClass(block: Block): Class<*>? { + val blockClass = block.javaClass + blackList.forEach { if (it.isAssignableFrom(blockClass)) return null } + whiteList.forEach { if (it.isAssignableFrom(blockClass)) return it } + return null + } +} + +data class ModelTextureList(val modelLocation: ResourceLocation, val textureNames: List) + +class ModelTextureListConfigOption(domain: String, path: String, val minTextures: Int) : StringListConfigOption(domain, path) { + override fun convertValue(line: String): ModelTextureList? { + val elements = line.split(",") + if (elements.size < minTextures + 1) return null + return ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/common/config/StringListConfigOption.kt b/src/main/kotlin/mods/octarinecore/common/config/StringListConfigOption.kt new file mode 100644 index 0000000..fa76780 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/common/config/StringListConfigOption.kt @@ -0,0 +1,43 @@ +package mods.octarinecore.common.config + +import mods.octarinecore.client.gui.NonVerboseArrayEntry +import mods.octarinecore.client.resource.get +import mods.octarinecore.client.resource.getLines +import mods.octarinecore.client.resource.resourceManager +import net.minecraftforge.common.config.Configuration +import net.minecraftforge.common.config.Property + +abstract class StringListConfigOption(val domain: String, val path: String) : ConfigPropertyBase() { + + val list = mutableListOf() + lateinit var listProperty: Property + + override val hasChanged: Boolean get() = listProperty.hasChanged() ?: false + override val guiProperties: List get() = listOf(listProperty) + + override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) { + lang = null + val defaults = readDefaults(domain, path) + listProperty = target.get(categoryName, "${propertyName}", defaults) + listProperty.configEntryClass = NonVerboseArrayEntry::class.java + listProperty.languageKey = "$langPrefix.$categoryName.${listProperty.name}" + read() + } + + abstract fun convertValue(line: String): VALUE? + + override fun read() { + list.clear() + listProperty.stringList.forEach { line -> + val value = convertValue(line) + if (value != null) list.add(value) + } + } + + fun readDefaults(domain: String, path: String): Array { + val list = arrayListOf() + val defaults = resourceManager[domain, path]?.getLines() + defaults?.map { it.trim() }?.filter { !it.startsWith("//") && it.isNotEmpty() }?.forEach { list.add(it) } + return list.toTypedArray() + } +} \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/GrassBlocksDefault.cfg b/src/main/resources/assets/betterfoliage/GrassBlocksDefault.cfg new file mode 100644 index 0000000..2f44802 --- /dev/null +++ b/src/main/resources/assets/betterfoliage/GrassBlocksDefault.cfg @@ -0,0 +1,19 @@ +// Vanilla +net.minecraft.block.BlockGrass + +// Biomes O'Plenty +biomesoplenty.common.blocks.BlockOriginGrass +biomesoplenty.common.blocks.BlockLongGrass +biomesoplenty.common.blocks.BlockNewGrass + +// Tinker's Construct +tconstruct.blocks.slime.SlimeGrass + +// Enhanced Biomes +enhancedbiomes.blocks.BlockGrassEB + +// TerraFirmaCraft +com.bioxx.tfc.Blocks.Terrain.BlockGrass + +// Aether +net.aetherteam.aether.blocks.natural.BlockAetherGrass \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/GrassDefault.cfg b/src/main/resources/assets/betterfoliage/GrassDefault.cfg deleted file mode 100644 index bd92538..0000000 --- a/src/main/resources/assets/betterfoliage/GrassDefault.cfg +++ /dev/null @@ -1,14 +0,0 @@ -// Vanilla -net.minecraft.block.BlockGrass - -// Biomes O'Plenty -biomesoplenty.common.block.BlockBOPGrass - -// Enhanced Biomes -enhancedbiomes.blocks.BlockGrassEB - -// TerraFirmaCraft -com.bioxx.tfc.Blocks.Terrain.BlockGrass - -// Random Things -lumien.randomthings.block.BlockColoredGrass \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/GrassModelsDefault.cfg b/src/main/resources/assets/betterfoliage/GrassModelsDefault.cfg new file mode 100644 index 0000000..b40a924 --- /dev/null +++ b/src/main/resources/assets/betterfoliage/GrassModelsDefault.cfg @@ -0,0 +1,2 @@ +block/grass,top +block/cube_bottom_top,top \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/LeavesBlocksDefault.cfg b/src/main/resources/assets/betterfoliage/LeavesBlocksDefault.cfg new file mode 100644 index 0000000..70d8e10 --- /dev/null +++ b/src/main/resources/assets/betterfoliage/LeavesBlocksDefault.cfg @@ -0,0 +1,8 @@ +// Vanilla +net.minecraft.block.BlockLeaves + +// Forestry +forestry.arboriculture.gadgets.BlockLeaves + +// Thaumcraft +thaumcraft.common.blocks.BlockMagicalLeaves \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/LeavesDefault.cfg b/src/main/resources/assets/betterfoliage/LeavesDefault.cfg deleted file mode 100644 index 54b785f..0000000 --- a/src/main/resources/assets/betterfoliage/LeavesDefault.cfg +++ /dev/null @@ -1,20 +0,0 @@ -// Vanilla -net.minecraft.block.BlockLeaves - -// Biomes O'Plenty -biomesoplenty.common.block.BlockBOPLeaves - -// Forestry -forestry.arboriculture.blocks.BlockForestryLeaves - -// Tinker's Construct -slimeknights.tconstruct.world.block.BlockSlimeLeaves - -// Thaumcraft -thaumcraft.common.blocks.world.plants.BlockLeavesTC - -// TechReborn -//techreborn.blocks.BlockRubberLeaves - -// Random Things -lumien.randomthings.block.spectretree.BlockSpectreLeaf \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/LeavesModelsDefault.cfg b/src/main/resources/assets/betterfoliage/LeavesModelsDefault.cfg new file mode 100644 index 0000000..01167f7 --- /dev/null +++ b/src/main/resources/assets/betterfoliage/LeavesModelsDefault.cfg @@ -0,0 +1,2 @@ +minecraft:block/leaves,all +minecraft:block/cube_all,all \ No newline at end of file diff --git a/src/main/resources/assets/betterfoliage/lang/en_US.lang b/src/main/resources/assets/betterfoliage/lang/en_US.lang index 9c0d0ea..6dc6ba0 100644 --- a/src/main/resources/assets/betterfoliage/lang/en_US.lang +++ b/src/main/resources/assets/betterfoliage/lang/en_US.lang @@ -30,15 +30,19 @@ betterfoliage.blocks.dirtBlacklist=Dirt Blacklist betterfoliage.blocks.dirtWhitelist.arrayEntry=%d entries betterfoliage.blocks.dirtBlacklist.arrayEntry=%d entries -betterfoliage.blocks.grassWhitelist=Grass Whitelist -betterfoliage.blocks.grassBlacklist=Grass Blacklist -betterfoliage.blocks.grassWhitelist.arrayEntry=%d entries -betterfoliage.blocks.grassBlacklist.arrayEntry=%d entries +betterfoliage.blocks.grassClassesWhitelist=Grass Whitelist +betterfoliage.blocks.grassClassesBlacklist=Grass Blacklist +betterfoliage.blocks.grassClassesWhitelist.arrayEntry=%d entries +betterfoliage.blocks.grassClassesBlacklist.arrayEntry=%d entries +betterfoliage.blocks.grassModels=Grass Models +betterfoliage.blocks.grassModels.arrayEntry=%d entries -betterfoliage.blocks.leavesWhitelist=Leaves Whitelist -betterfoliage.blocks.leavesBlacklist=Leaves Blacklist -betterfoliage.blocks.leavesWhitelist.arrayEntry=%d entries -betterfoliage.blocks.leavesBlacklist.arrayEntry=%d entries +betterfoliage.blocks.leavesClassesWhitelist=Leaves Whitelist +betterfoliage.blocks.leavesClassesBlacklist=Leaves Blacklist +betterfoliage.blocks.leavesClassesWhitelist.arrayEntry=%d entries +betterfoliage.blocks.leavesClassesBlacklist.arrayEntry=%d entries +betterfoliage.blocks.leavesModels=Leaves Models +betterfoliage.blocks.leavesModels.arrayEntry=%d entries betterfoliage.blocks.cropsWhitelist=Crop Whitelist betterfoliage.blocks.cropsBlacklist=Crop Blacklist diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 60cd829..3fab3ef 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -4,5 +4,6 @@ "version": "$version", "mcversion": "$mcversion", "description": "Leafier leaves and grassier grass", - "authorList" : ["octarine-noise (code)", "Meringue (textures)"] + "authorList" : ["octarine-noise (code)", "Meringue (textures)"], + "modLanguageAdapter": "" }] \ No newline at end of file