rewrite model and texture detection

expose in mod configuration
This commit is contained in:
octarine-noise
2016-08-09 16:53:29 +02:00
parent 1bd353577f
commit 488078b50f
37 changed files with 526 additions and 236 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -61,6 +61,9 @@ object OptifineCTM {
return result
}
fun getAllCTM(states: List<IBlockState>, icon: TextureAtlasSprite): Collection<TextureAtlasSprite> =
states.flatMap { getAllCTM(it, icon) }.toSet()
fun override(texture: TextureAtlasSprite, ctx: BlockContext, face: EnumFacing) =
override(texture, ctx.world!!, ctx.pos, face)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<GrassInfo>() {
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<TextureAtlasSprite>,
TextureMediatedRegistry<List<String>, GrassInfo>,
IGrassRegistry
{
init { MinecraftForge.EVENT_BUS.register(this) }
override var stateToKey = mutableMapOf<IBlockState, List<String>>()
override var stateToValue = mapOf<IBlockState, TextureAtlasSprite>()
override var textureToValue = mutableMapOf<TextureAtlasSprite, GrassInfo>()
override val logger = BetterFoliageMod.logDetail
override val logName = "StandardGrassSupport"
override val matchClasses: BlockMatcher get() = Config.blocks.grassClasses
override val modelTextures: List<ModelTextureList> 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<TextureAtlasSprite>, atlas: TextureMap): GrassInfo {
val hsb = HSB.fromColor(textures[0].averageColor ?: defaultGrassColor)
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap) = atlas[key[0]]
override fun processTexture(states: List<IBlockState>, 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)
}
}

View File

@@ -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<TextureAtlasSprite>() {
interface ILeafRegistry {
operator fun get(state: IBlockState): LeafInfo?
operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo?
}
val leaves: MutableMap<TextureAtlasSprite, LeafInfo> = hashMapOf()
val particles: MutableMap<String, IconSet> = hashMapOf()
/** Collects and manages rendering-related information for grass blocks. */
object LeafRegistry : ILeafRegistry {
val subRegistries: MutableList<ILeafRegistry> = mutableListOf(StandardLeafSupport)
val typeMappings = TextureMatcher()
val particles = hashMapOf<String, IconSet>()
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<TextureAtlasSprite>, 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
}
}
}
@SideOnly(Side.CLIENT)
object StandardLeafSupport :
TextureListModelProcessor<TextureAtlasSprite>,
TextureMediatedRegistry<List<String>, LeafInfo>,
ILeafRegistry
{
init { MinecraftForge.EVENT_BUS.register(this) }
override val logName = "StandardLeafSupport"
override val matchClasses: BlockMatcher get() = Config.blocks.leavesClasses
override val modelTextures: List<ModelTextureList> get() = Config.blocks.leavesModels.list
override val logger: Logger? get() = BetterFoliageMod.logDetail
override var stateToKey = mutableMapOf<IBlockState, List<String>>()
override var stateToValue = mapOf<IBlockState, TextureAtlasSprite>()
override var textureToValue = mutableMapOf<TextureAtlasSprite, LeafInfo>()
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<String>, atlas: TextureMap) = atlas[key[0]]
override fun processTexture(states: List<IBlockState>, 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))
}
}

View File

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

View File

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

View File

@@ -33,6 +33,14 @@ inline fun <T1, T2> forEachNested(list1: Iterable<T1>, list2: Iterable<T2>, func
}
}
@Suppress("UNCHECKED_CAST")
inline fun <K, V> Map<K, V?>.filterValuesNotNull() = filterValues { it != null } as Map<K, V>
inline fun <reified T, R> Iterable<T>.findFirst(func: (T)->R?): R? {
forEach { func(it)?.let { return it } }
return null
}
/**
* Property-level delegate backed by a [ThreadLocal].
*

View File

@@ -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<IBlockState, ModelResourceLocation>).entries
val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()
(mapper.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
}
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
@@ -96,31 +94,4 @@ abstract class BlockTextureInspector<T> : ModelDataInspector() {
}
abstract fun processTextures(state: IBlockState, textures: List<TextureAtlasSprite>, atlas: TextureMap): T
}
@Suppress("UNCHECKED_CAST")
val IModel.modelBlockAndLoc: Pair<ModelBlock, ResourceLocation>? 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<IModel>).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<ModelBlock, ResourceLocation>.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
}

View File

@@ -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<T1, T2> {
val logger: Logger?
var stateToKey: MutableMap<IBlockState, T1>
var stateToValue: Map<IBlockState, T2>
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<IBlockState, ModelResourceLocation>).entries
}
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
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<T2> : ModelProcessor<List<String>, T2> {
val logName: String
val matchClasses: BlockMatcher
val modelTextures: List<ModelTextureList>
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): List<String>? {
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<T1, T3> : ModelProcessor<T1, TextureAtlasSprite> {
var textureToValue: MutableMap<TextureAtlasSprite, T3>
@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<IBlockState>, texture: TextureAtlasSprite, atlas: TextureMap)
}

View File

@@ -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}")
}
}
@Suppress("UNCHECKED_CAST")
val IModel.modelBlockAndLoc: Pair<ModelBlock, ResourceLocation>? 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<IModel>).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<ModelBlock, ResourceLocation>.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

View File

@@ -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<VALUE>(val domain: String, val path: String) : ConfigPropertyBase() {
val blackList = mutableListOf<Class<*>>()
val whiteList = mutableListOf<Class<*>>()
val blockIDs = hashSetOf<Int>()
val blackList = mutableListOf<VALUE>()
val whiteList = mutableListOf<VALUE>()
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<Property> 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<Property> get() = listOf(whitelistProperty!!, blacklistProperty!!)
fun readDefaults(domain: String, path: String): Pair<Array<String>, Array<String>> {
val blackList = arrayListOf<String>()
val whiteList = arrayListOf<String>()
@@ -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) }
}

View File

@@ -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<Class<*>>(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<String>)
class ModelTextureListConfigOption(domain: String, path: String, val minTextures: Int) : StringListConfigOption<ModelTextureList>(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))
}
}

View File

@@ -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<VALUE>(val domain: String, val path: String) : ConfigPropertyBase() {
val list = mutableListOf<VALUE>()
lateinit var listProperty: Property
override val hasChanged: Boolean get() = listProperty.hasChanged() ?: false
override val guiProperties: List<Property> 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<String> {
val list = arrayListOf<String>()
val defaults = resourceManager[domain, path]?.getLines()
defaults?.map { it.trim() }?.filter { !it.startsWith("//") && it.isNotEmpty() }?.forEach { list.add(it) }
return list.toTypedArray()
}
}

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
block/grass,top
block/cube_bottom_top,top

View File

@@ -0,0 +1,8 @@
// Vanilla
net.minecraft.block.BlockLeaves
// Forestry
forestry.arboriculture.gadgets.BlockLeaves
// Thaumcraft
thaumcraft.common.blocks.BlockMagicalLeaves

View File

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

View File

@@ -0,0 +1,2 @@
minecraft:block/leaves,all
minecraft:block/cube_all,all

View File

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

View File

@@ -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": ""
}]