rewrite model and texture detection
expose in mod configuration
This commit is contained in:
@@ -9,8 +9,15 @@ import net.minecraftforge.fml.common.event.FMLPostInitializationEvent
|
|||||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
|
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
|
||||||
import net.minecraftforge.fml.common.network.NetworkCheckHandler
|
import net.minecraftforge.fml.common.network.NetworkCheckHandler
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
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.Level.INFO
|
||||||
import org.apache.logging.log4j.Logger
|
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(
|
@Mod(
|
||||||
modid = BetterFoliageMod.MOD_ID,
|
modid = BetterFoliageMod.MOD_ID,
|
||||||
@@ -28,6 +35,8 @@ object BetterFoliageMod {
|
|||||||
const val GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory"
|
const val GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory"
|
||||||
|
|
||||||
lateinit var log: Logger
|
lateinit var log: Logger
|
||||||
|
lateinit var logDetail: Logger
|
||||||
|
|
||||||
var config: Configuration? = null
|
var config: Configuration? = null
|
||||||
var isAfterPostInit = false
|
var isAfterPostInit = false
|
||||||
|
|
||||||
@@ -39,8 +48,16 @@ object BetterFoliageMod {
|
|||||||
@Mod.EventHandler
|
@Mod.EventHandler
|
||||||
fun preInit(event: FMLPreInitializationEvent) {
|
fun preInit(event: FMLPreInitializationEvent) {
|
||||||
log = event.modLog
|
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)
|
config = Configuration(event.suggestedConfigurationFile, null, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mod.EventHandler
|
@Mod.EventHandler
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import mods.betterfoliage.client.gui.ConfigGuiFactory
|
|||||||
import mods.betterfoliage.client.integration.OptifineCTM
|
import mods.betterfoliage.client.integration.OptifineCTM
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.betterfoliage.client.render.*
|
import mods.betterfoliage.client.render.*
|
||||||
import mods.betterfoliage.client.texture.GrassGenerator
|
import mods.betterfoliage.client.texture.*
|
||||||
import mods.betterfoliage.client.texture.GrassRegistry
|
|
||||||
import mods.betterfoliage.client.texture.LeafGenerator
|
|
||||||
import mods.betterfoliage.client.texture.LeafRegistry
|
|
||||||
import mods.octarinecore.client.KeyHandler
|
import mods.octarinecore.client.KeyHandler
|
||||||
import mods.octarinecore.client.resource.CenteringTextureGenerator
|
import mods.octarinecore.client.resource.CenteringTextureGenerator
|
||||||
import mods.octarinecore.client.resource.GeneratorPack
|
import mods.octarinecore.client.resource.GeneratorPack
|
||||||
@@ -63,14 +60,21 @@ object Client {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val singletons = listOf(
|
val singletons = listOf(
|
||||||
LeafRegistry,
|
StandardLeafSupport,
|
||||||
GrassRegistry,
|
StandardGrassSupport,
|
||||||
LeafWindTracker,
|
LeafWindTracker,
|
||||||
RisingSoulTextures,
|
RisingSoulTextures,
|
||||||
ShadersModIntegration,
|
ShadersModIntegration,
|
||||||
OptifineCTM
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,35 +15,38 @@ import net.minecraft.block.Block
|
|||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.state.IBlockState
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.VertexBuffer
|
import net.minecraft.client.renderer.VertexBuffer
|
||||||
|
import net.minecraft.client.renderer.block.model.IBakedModel
|
||||||
import net.minecraft.init.Blocks
|
import net.minecraft.init.Blocks
|
||||||
import net.minecraft.util.BlockRenderLayer
|
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.EnumFacing
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockAccess
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
|
import net.minecraftforge.client.model.IModel
|
||||||
import net.minecraftforge.client.model.ModelLoader
|
import net.minecraftforge.client.model.ModelLoader
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.fml.relauncher.Side
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.fml.relauncher.SideOnly
|
||||||
|
|
||||||
fun doesSideBlockRenderingOverride(original: Boolean, blockAccess: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean {
|
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 {
|
fun isOpaqueCubeOverride(original: Boolean, state: IBlockState): Boolean {
|
||||||
// caution: blocks are initialized and the method called during startup
|
// caution: blocks are initialized and the method called during startup
|
||||||
if (!BetterFoliageMod.isAfterPostInit) return original
|
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 {
|
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;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUseNeighborBrightnessOverride(original: Boolean, state: IBlockState): Boolean {
|
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) {
|
fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) {
|
||||||
@@ -57,7 +60,7 @@ fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) {
|
|||||||
|
|
||||||
if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
Config.fallingLeaves.enabled &&
|
Config.fallingLeaves.enabled &&
|
||||||
Config.blocks.leaves.matchesID(state.block) &&
|
Config.blocks.leavesClasses.matchesClass(state.block) &&
|
||||||
world.isAirBlock(pos + down1) &&
|
world.isAirBlock(pos + down1) &&
|
||||||
Math.random() < Config.fallingLeaves.chance) {
|
Math.random() < Config.fallingLeaves.chance) {
|
||||||
EntityFallingLeavesFX(world, pos).addIfValid()
|
EntityFallingLeavesFX(world, pos).addIfValid()
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
var enabled by boolean(true)
|
var enabled by boolean(true)
|
||||||
|
|
||||||
object blocks {
|
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 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 crops = BlockMatcher(BetterFoliageMod.DOMAIN, "CropDefault.cfg")
|
||||||
val logs = BlockMatcher(BetterFoliageMod.DOMAIN, "LogDefault.cfg")
|
val logs = BlockMatcher(BetterFoliageMod.DOMAIN, "LogDefault.cfg")
|
||||||
val sand = BlockMatcher(BetterFoliageMod.DOMAIN, "SandDefault.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 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) {
|
override fun onChange(event: ConfigChangedEvent.OnConfigChangedEvent) {
|
||||||
super.onChange(event)
|
super.onChange(event)
|
||||||
if (hasChanged(blocks, shortGrass["saturationThreshold"]))
|
if (hasChanged(forceReloadOptions))
|
||||||
Minecraft.getMinecraft().refreshResources()
|
Minecraft.getMinecraft().refreshResources()
|
||||||
else
|
else
|
||||||
Minecraft.getMinecraft().renderGlobal.loadRenderers()
|
Minecraft.getMinecraft().renderGlobal.loadRenderers()
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ object OptifineCTM {
|
|||||||
return result
|
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) =
|
fun override(texture: TextureAtlasSprite, ctx: BlockContext, face: EnumFacing) =
|
||||||
override(texture, ctx.world!!, ctx.pos, face)
|
override(texture, ctx.world!!, ctx.pos, face)
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ object ShadersModIntegration {
|
|||||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
||||||
*/
|
*/
|
||||||
@JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long {
|
@JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long {
|
||||||
if (Config.blocks.leaves.matchesID(blockState.block)) return leavesEntityData
|
if (Config.blocks.leavesClasses.matchesClass(blockState.block)) return leavesEntityData
|
||||||
if (Config.blocks.crops.matchesID(blockState.block)) return tallGrassEntityData
|
if (Config.blocks.crops.matchesClass(blockState.block)) return tallGrassEntityData
|
||||||
return original
|
return original
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.client.config.BlockMatcher
|
|
||||||
import mods.betterfoliage.client.integration.OptifineCTM
|
import mods.betterfoliage.client.integration.OptifineCTM
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.betterfoliage.client.render.AbstractRenderColumn.BlockType.*
|
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.render.*
|
||||||
import mods.octarinecore.client.resource.BlockTextureInspector
|
import mods.octarinecore.client.resource.BlockTextureInspector
|
||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.*
|
||||||
|
import mods.octarinecore.common.config.BlockMatcher
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.state.IBlockState
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.VertexBuffer
|
import net.minecraft.client.renderer.VertexBuffer
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mods.betterfoliage.client.render
|
|||||||
|
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.texture.LeafRegistry
|
import mods.betterfoliage.client.texture.LeafRegistry
|
||||||
|
import mods.betterfoliage.client.texture.defaultLeafColor
|
||||||
import mods.octarinecore.PI2
|
import mods.octarinecore.PI2
|
||||||
import mods.octarinecore.client.render.AbstractEntityFX
|
import mods.octarinecore.client.render.AbstractEntityFX
|
||||||
import mods.octarinecore.client.render.HSB
|
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
|
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
||||||
|
|
||||||
val state = world.getBlockState(pos)
|
val state = world.getBlockState(pos)
|
||||||
LeafRegistry[state, world, pos, DOWN]?.let {
|
val blockColor = Minecraft.getMinecraft().blockColors.colorMultiplier(state, world, pos, 0)
|
||||||
particleTexture = it.particleTextures[rand.nextInt(1024)]
|
val leafInfo = LeafRegistry.get(state, world, pos, DOWN)
|
||||||
calculateParticleColor(it.averageColor, Minecraft.getMinecraft().blockColors.colorMultiplier(state, world, pos, 0))
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
ctx.cameraDistance < Config.algae.distance &&
|
ctx.cameraDistance < Config.algae.distance &&
|
||||||
ctx.blockState(up2).material == Material.WATER &&
|
ctx.blockState(up2).material == Material.WATER &&
|
||||||
ctx.blockState(up1).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 &&
|
ctx.biomeId in Config.algae.biomes &&
|
||||||
noise[ctx.pos] < Config.algae.population
|
noise[ctx.pos] < Config.algae.population
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
override fun isEligible(ctx: BlockContext): Boolean =
|
override fun isEligible(ctx: BlockContext): Boolean =
|
||||||
Config.enabled && Config.cactus.enabled &&
|
Config.enabled && Config.cactus.enabled &&
|
||||||
ctx.cameraDistance < Config.cactus.distance &&
|
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 {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
||||||
// get AO data
|
// get AO data
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import net.minecraft.util.BlockRenderLayer
|
|||||||
class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.connectedGrass.enabled &&
|
Config.enabled && Config.connectedGrass.enabled &&
|
||||||
Config.blocks.dirt.matchesID(ctx.block) &&
|
Config.blocks.dirt.matchesClass(ctx.block) &&
|
||||||
Config.blocks.grass.matchesID(ctx.block(up1)) &&
|
Config.blocks.grassClasses.matchesClass(ctx.block(up1)) &&
|
||||||
(Config.connectedGrass.snowEnabled || !ctx.blockState(up2).isSnow)
|
(Config.connectedGrass.snowEnabled || !ctx.blockState(up2).isSnow)
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliageMod.M
|
|||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&
|
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&
|
||||||
Config.blocks.dirt.matchesID(ctx.block) &&
|
Config.blocks.dirt.matchesClass(ctx.block) &&
|
||||||
Config.blocks.logs.matchesID(ctx.block(up1))
|
Config.blocks.logs.matchesClass(ctx.block(up1))
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
||||||
val grassDir = grassCheckDirs.find {
|
val grassDir = grassCheckDirs.find {
|
||||||
Config.blocks.grass.matchesID(ctx.block(it.offset))
|
Config.blocks.grassClasses.matchesClass(ctx.block(it.offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (grassDir != null) {
|
return if (grassDir != null) {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
ctx.cameraDistance < Config.coral.distance &&
|
ctx.cameraDistance < Config.coral.distance &&
|
||||||
(ctx.blockState(up2).material == Material.WATER || Config.coral.shallowWater) &&
|
(ctx.blockState(up2).material == Material.WATER || Config.coral.shallowWater) &&
|
||||||
ctx.blockState(up1).material == Material.WATER &&
|
ctx.blockState(up1).material == Material.WATER &&
|
||||||
Config.blocks.sand.matchesID(ctx.block) &&
|
Config.blocks.sand.matchesClass(ctx.block) &&
|
||||||
ctx.biomeId in Config.coral.biomes &&
|
ctx.biomeId in Config.coral.biomes &&
|
||||||
noise[ctx.pos] < Config.coral.population
|
noise[ctx.pos] < Config.coral.population
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package mods.betterfoliage.client.render
|
|||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.OptifineCTM
|
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.betterfoliage.client.texture.GrassRegistry
|
import mods.betterfoliage.client.texture.GrassRegistry
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
@@ -47,16 +46,17 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
Config.enabled &&
|
Config.enabled &&
|
||||||
ctx.cameraDistance < Config.shortGrass.distance &&
|
ctx.cameraDistance < Config.shortGrass.distance &&
|
||||||
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
|
(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 {
|
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 isSnowed = ctx.blockState(up1).isSnow
|
||||||
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
|
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
|
||||||
|
|
||||||
val grassInfo = GrassRegistry[ctx.blockState(Int3.zero)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
val grassInfo = GrassRegistry[ctx, UP]!!
|
||||||
val grassTopTexture = OptifineCTM.override(grassInfo.grassTopTexture, ctx, UP)
|
|
||||||
|
|
||||||
val blockColor = ctx.blockData(Int3.zero).color
|
val blockColor = ctx.blockData(Int3.zero).color
|
||||||
|
|
||||||
if (connectedGrass) {
|
if (connectedGrass) {
|
||||||
@@ -70,7 +70,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
fullCube,
|
fullCube,
|
||||||
Rotation.identity,
|
Rotation.identity,
|
||||||
ctx.blockCenter,
|
ctx.blockCenter,
|
||||||
icon = { ctx, qi, q -> grassTopTexture },
|
icon = { ctx, qi, q -> grassInfo.grassTopTexture },
|
||||||
postProcess = { ctx, qi, q, vi, v ->
|
postProcess = { ctx, qi, q, vi, v ->
|
||||||
rotateUV(2)
|
rotateUV(2)
|
||||||
if (isSnowed) {
|
if (isSnowed) {
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
Config.enabled &&
|
Config.enabled &&
|
||||||
Config.leaves.enabled &&
|
Config.leaves.enabled &&
|
||||||
ctx.cameraDistance < Config.leaves.distance &&
|
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 {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
||||||
val isSnowed = ctx.blockState(up1).material.let {
|
val isSnowed = ctx.blockState(up1).material.let {
|
||||||
it == Material.SNOW || it == Material.CRAFTED_SNOW
|
it == Material.SNOW || it == Material.CRAFTED_SNOW
|
||||||
}
|
}
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
||||||
val leafInfo = LeafRegistry[ctx, DOWN] ?: return false
|
val leafInfo = LeafRegistry[ctx, DOWN]!!
|
||||||
val blockColor = ctx.blockData(Int3.zero).color
|
val blockColor = ctx.blockData(Int3.zero).color
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
override fun isEligible(ctx: BlockContext): Boolean =
|
override fun isEligible(ctx: BlockContext): Boolean =
|
||||||
Config.enabled && Config.lilypad.enabled &&
|
Config.enabled && Config.lilypad.enabled &&
|
||||||
ctx.cameraDistance < Config.lilypad.distance &&
|
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 {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
||||||
|
|||||||
@@ -2,17 +2,12 @@ package mods.betterfoliage.client.render
|
|||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.OptifineCTM
|
|
||||||
import mods.octarinecore.client.render.BlockContext
|
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.Int3
|
||||||
import mods.octarinecore.common.rotate
|
|
||||||
import mods.octarinecore.tryDefault
|
import mods.octarinecore.tryDefault
|
||||||
import net.minecraft.block.BlockLog
|
import net.minecraft.block.BlockLog
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.state.IBlockState
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.EnumFacing.Axis
|
||||||
|
|
||||||
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
|
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
|
||||||
|
|
||||||
@@ -21,7 +16,7 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
|
|||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.roundLogs.enabled &&
|
Config.enabled && Config.roundLogs.enabled &&
|
||||||
ctx.cameraDistance < Config.roundLogs.distance &&
|
ctx.cameraDistance < Config.roundLogs.distance &&
|
||||||
Config.blocks.logs.matchesID(ctx.block)
|
Config.blocks.logs.matchesClass(ctx.block)
|
||||||
|
|
||||||
override var axisFunc = { state: IBlockState ->
|
override var axisFunc = { state: IBlockState ->
|
||||||
val axis = tryDefault(null) { state.getValue(BlockLog.LOG_AXIS).toString() } ?:
|
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 fun resolver(ctx: BlockContext): ColumnTextureResolver? = columnTextures[ctx.blockState(Int3.zero)]
|
||||||
|
|
||||||
override val blockPredicate = { state: IBlockState -> 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.matchesID(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 connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
|
||||||
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
|
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
ctx.cameraDistance < Config.reed.distance &&
|
ctx.cameraDistance < Config.reed.distance &&
|
||||||
ctx.blockState(up2).material == Material.AIR &&
|
ctx.blockState(up2).material == Material.AIR &&
|
||||||
ctx.blockState(up1).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.reed.biomes &&
|
ctx.biomeId in Config.reed.biomes &&
|
||||||
noise[ctx.pos] < Config.reed.population
|
noise[ctx.pos] < Config.reed.population
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.client.config.Config
|
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.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.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.block.state.IBlockState
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
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.Side
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.fml.relauncher.SideOnly
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
|
|
||||||
const val defaultGrassColor = 0
|
const val defaultGrassColor = 0
|
||||||
|
|
||||||
@@ -23,28 +34,66 @@ class GrassInfo(
|
|||||||
* Color to use for Short Grass rendering instead of the biome color.
|
* 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),
|
* 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?
|
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. */
|
/** Collects and manages rendering-related information for grass blocks. */
|
||||||
@SideOnly(Side.CLIENT)
|
@SideOnly(Side.CLIENT)
|
||||||
object GrassRegistry : BlockTextureInspector<GrassInfo>() {
|
object GrassRegistry : IGrassRegistry {
|
||||||
|
val subRegistries = mutableListOf(StandardGrassSupport)
|
||||||
|
|
||||||
init {
|
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) =
|
||||||
matchClassAndModel(Config.blocks.grass, "block/grass", listOf("top"))
|
subRegistries.findFirst { it.get(state, world, pos, face) }
|
||||||
matchClassAndModel(Config.blocks.grass, "block/cube_bottom_top", listOf("top"))
|
|
||||||
|
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() {
|
override fun get(state: IBlockState) = StandardLeafSupport.stateToValue[state].let {
|
||||||
super.onAfterModelLoad()
|
if (it == null) null else textureToValue[it]
|
||||||
Client.log(INFO, "Inspecting grass textures")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processTextures(state: IBlockState, textures: List<TextureAtlasSprite>, atlas: TextureMap): GrassInfo {
|
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap) = atlas[key[0]]
|
||||||
val hsb = HSB.fromColor(textures[0].averageColor ?: defaultGrassColor)
|
|
||||||
|
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
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,29 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.OptifineCTM
|
import mods.betterfoliage.client.integration.OptifineCTM
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
import mods.octarinecore.client.resource.BlockTextureInspector
|
import mods.octarinecore.client.resource.*
|
||||||
import mods.octarinecore.client.resource.IconSet
|
|
||||||
import mods.octarinecore.client.resource.averageColor
|
|
||||||
import mods.octarinecore.common.Int3
|
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.block.state.IBlockState
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
import net.minecraft.client.renderer.texture.TextureMap
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.EnumFacing
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
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.Side
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.fml.relauncher.SideOnly
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level
|
||||||
import org.apache.logging.log4j.Level.WARN
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
const val defaultLeafColor = 0
|
const val defaultLeafColor = 0
|
||||||
|
|
||||||
@@ -34,61 +39,94 @@ class LeafInfo(
|
|||||||
val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor
|
val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor
|
||||||
) {
|
) {
|
||||||
/** [IconSet] of the textures to use for leaf particles emitted from this block. */
|
/** [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. */
|
interface ILeafRegistry {
|
||||||
@SideOnly(Side.CLIENT)
|
operator fun get(state: IBlockState): LeafInfo?
|
||||||
object LeafRegistry : BlockTextureInspector<TextureAtlasSprite>() {
|
operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo?
|
||||||
|
}
|
||||||
|
|
||||||
val leaves: MutableMap<TextureAtlasSprite, LeafInfo> = hashMapOf()
|
/** Collects and manages rendering-related information for grass blocks. */
|
||||||
val particles: MutableMap<String, IconSet> = hashMapOf()
|
object LeafRegistry : ILeafRegistry {
|
||||||
|
val subRegistries: MutableList<ILeafRegistry> = mutableListOf(StandardLeafSupport)
|
||||||
val typeMappings = TextureMatcher()
|
val typeMappings = TextureMatcher()
|
||||||
|
val particles = hashMapOf<String, IconSet>()
|
||||||
|
|
||||||
init {
|
init { MinecraftForge.EVENT_BUS.register(this) }
|
||||||
matchClassAndModel(Config.blocks.leaves, "minecraft:block/leaves", listOf("all"))
|
|
||||||
matchClassAndModel(Config.blocks.leaves, "minecraft:block/cube_all", listOf("all"))
|
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||||
matchClassAndModel(Config.blocks.leaves, "biomesoplenty:block/leaves_overlay", listOf("under"))
|
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||||
|
particles.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? {
|
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) =
|
||||||
val baseTexture = get(state) ?: return null
|
subRegistries.findFirst { it.get(state, world, pos, face) }
|
||||||
return leaves[OptifineCTM.override(baseTexture, world, pos, face)] ?: leaves[baseTexture]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(ctx: BlockContext, face: EnumFacing) = get(ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, face)
|
operator fun get(ctx: BlockContext, face: EnumFacing) = get(ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, face)
|
||||||
|
|
||||||
override fun onAfterModelLoad() {
|
override fun get(state: IBlockState) = subRegistries.findFirst { it.get(state) }
|
||||||
super.onAfterModelLoad()
|
|
||||||
Client.log(INFO, "Inspecting leaf textures")
|
|
||||||
particles.clear()
|
|
||||||
typeMappings.loadMappings(ResourceLocation("betterfoliage", "leafTextureMappings.cfg"))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun processTextures(state: IBlockState, textures: List<TextureAtlasSprite>, atlas: TextureMap): TextureAtlasSprite {
|
fun getParticleType(texture: TextureAtlasSprite, atlas: TextureMap): String {
|
||||||
val texture = textures[0]
|
|
||||||
registerLeaf(texture, atlas)
|
|
||||||
OptifineCTM.getAllCTM(state, texture).forEach { registerLeaf(it, atlas) }
|
|
||||||
return texture
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerLeaf(texture: TextureAtlasSprite, atlas: TextureMap) {
|
|
||||||
var leafType = typeMappings.getType(texture) ?: "default"
|
var leafType = typeMappings.getType(texture) ?: "default"
|
||||||
val generated = atlas.registerSprite(
|
|
||||||
Client.genLeaves.generatedResource(texture.iconName, "type" to leafType)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (leafType !in particles.keys) {
|
if (leafType !in particles.keys) {
|
||||||
val particleSet = IconSet("betterfoliage", "blocks/falling_leaf_${leafType}_%d")
|
val particleSet = IconSet("betterfoliage", "blocks/falling_leaf_${leafType}_%d")
|
||||||
particleSet.onStitch(atlas)
|
particleSet.onStitch(atlas)
|
||||||
if (particleSet.num == 0) {
|
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"
|
leafType == "default"
|
||||||
} else {
|
} else {
|
||||||
particles.put(leafType, particleSet)
|
particles.put(leafType, particleSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return leafType
|
||||||
leaves[texture] = LeafInfo(generated, 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import mods.octarinecore.metaprog.Transformer
|
|||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraftforge.fml.relauncher.FMLLaunchHandler
|
import net.minecraftforge.fml.relauncher.FMLLaunchHandler
|
||||||
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin
|
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.Opcodes.*
|
import org.objectweb.asm.Opcodes.*
|
||||||
|
|
||||||
@IFMLLoadingPlugin.TransformerExclusions(
|
@IFMLLoadingPlugin.TransformerExclusions(
|
||||||
|
|||||||
@@ -61,12 +61,15 @@ object Refs {
|
|||||||
val VanillaModelWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$VanillaModelWrapper")
|
val VanillaModelWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$VanillaModelWrapper")
|
||||||
val model_VMW = FieldRef(VanillaModelWrapper, "model", ModelBlock)
|
val model_VMW = FieldRef(VanillaModelWrapper, "model", ModelBlock)
|
||||||
val location_VMW = FieldRef(VanillaModelWrapper, "location", ModelBlock)
|
val location_VMW = FieldRef(VanillaModelWrapper, "location", ModelBlock)
|
||||||
val WeightedPartWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedPartWrapper")
|
// val WeightedPartWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedPartWrapper")
|
||||||
val model_WPW = FieldRef(WeightedPartWrapper, "model", IModel)
|
// val model_WPW = FieldRef(WeightedPartWrapper, "model", IModel)
|
||||||
val WeightedRandomModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedRandomModel")
|
val WeightedRandomModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedRandomModel")
|
||||||
val models_WRM = FieldRef(WeightedRandomModel, "models", List)
|
val models_WRM = FieldRef(WeightedRandomModel, "models", List)
|
||||||
val MultiModel = ClassRef("net.minecraftforge.client.model.MultiModel")
|
val MultiModel = ClassRef("net.minecraftforge.client.model.MultiModel")
|
||||||
val base_MM = FieldRef(MultiModel, "base", IModel)
|
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
|
// Better Foliage
|
||||||
val BetterFoliageHooks = ClassRef("mods.betterfoliage.client.Hooks")
|
val BetterFoliageHooks = ClassRef("mods.betterfoliage.client.Hooks")
|
||||||
@@ -76,6 +79,7 @@ object Refs {
|
|||||||
val isOpaqueCubeOverride = MethodRef(BetterFoliageHooks, "isOpaqueCubeOverride", ClassRef.boolean, ClassRef.boolean, IBlockState)
|
val isOpaqueCubeOverride = MethodRef(BetterFoliageHooks, "isOpaqueCubeOverride", ClassRef.boolean, ClassRef.boolean, IBlockState)
|
||||||
val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, World, IBlockState, BlockPos)
|
val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, World, IBlockState, BlockPos)
|
||||||
val onAfterLoadModelDefinitions = MethodRef(BetterFoliageHooks, "onAfterLoadModelDefinitions", ClassRef.void, ModelLoader)
|
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 renderWorldBlock = MethodRef(BetterFoliageHooks, "renderWorldBlock", ClassRef.boolean, BlockRendererDispatcher, IBlockState, BlockPos, IBlockAccess, VertexBuffer, BlockRenderLayer)
|
||||||
val canRenderBlockInLayer = MethodRef(BetterFoliageHooks, "canRenderBlockInLayer", ClassRef.boolean, Block, IBlockState, BlockRenderLayer)
|
val canRenderBlockInLayer = MethodRef(BetterFoliageHooks, "canRenderBlockInLayer", ClassRef.boolean, Block, IBlockState, BlockRenderLayer)
|
||||||
|
|
||||||
|
|||||||
@@ -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].
|
* Property-level delegate backed by a [ThreadLocal].
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
import mods.betterfoliage.client.config.BlockMatcher
|
|
||||||
import mods.betterfoliage.loader.Refs
|
import mods.betterfoliage.loader.Refs
|
||||||
import mods.octarinecore.stripStart
|
import mods.octarinecore.common.config.BlockMatcher
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.state.IBlockState
|
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.model.ModelResourceLocation
|
||||||
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
|
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
|
||||||
import net.minecraft.client.renderer.block.statemap.IStateMapper
|
import net.minecraft.client.renderer.block.statemap.IStateMapper
|
||||||
@@ -34,8 +32,8 @@ abstract class ModelDataInspector {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleLoadModelData(event: LoadModelDataEvent) {
|
fun handleLoadModelData(event: LoadModelDataEvent) {
|
||||||
val stateMappings = Block.REGISTRY.flatMap { block ->
|
val stateMappings = Block.REGISTRY.flatMap { block ->
|
||||||
((event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper())
|
val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()
|
||||||
.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
|
(mapper.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
|
||||||
}
|
}
|
||||||
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
|
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
|
||||||
|
|
||||||
@@ -97,30 +95,3 @@ abstract class BlockTextureInspector<T> : ModelDataInspector() {
|
|||||||
|
|
||||||
abstract fun processTextures(state: IBlockState, textures: List<TextureAtlasSprite>, atlas: TextureMap): T
|
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
|
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
@file:JvmName("Utils")
|
@file:JvmName("Utils")
|
||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
|
import mods.betterfoliage.loader.Refs
|
||||||
import mods.octarinecore.PI2
|
import mods.octarinecore.PI2
|
||||||
import mods.octarinecore.client.render.HSB
|
import mods.octarinecore.client.render.HSB
|
||||||
|
import mods.octarinecore.stripStart
|
||||||
import mods.octarinecore.tryDefault
|
import mods.octarinecore.tryDefault
|
||||||
import net.minecraft.client.Minecraft
|
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.TextureAtlasSprite
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap
|
||||||
import net.minecraft.client.resources.IResource
|
import net.minecraft.client.resources.IResource
|
||||||
import net.minecraft.client.resources.IResourceManager
|
import net.minecraft.client.resources.IResourceManager
|
||||||
import net.minecraft.client.resources.SimpleReloadableResourceManager
|
import net.minecraft.client.resources.SimpleReloadableResourceManager
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import net.minecraftforge.client.model.IModel
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
@@ -29,6 +34,9 @@ operator fun IResourceManager.get(domain: String, path: String): IResource? = ge
|
|||||||
/** Index operator to get a resource. */
|
/** Index operator to get a resource. */
|
||||||
operator fun IResourceManager.get(location: ResourceLocation): IResource? = tryDefault(null) { getResource(location) }
|
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. */
|
/** Load an image resource. */
|
||||||
fun IResource.loadImage() = ImageIO.read(this.inputStream)
|
fun IResource.loadImage() = ImageIO.read(this.inputStream)
|
||||||
|
|
||||||
@@ -90,3 +98,28 @@ fun textureLocation(iconName: String) = ResourceLocation(iconName).let {
|
|||||||
if (it.resourcePath.startsWith("mcpatcher")) it
|
if (it.resourcePath.startsWith("mcpatcher")) it
|
||||||
else ResourceLocation(it.resourceDomain, "textures/${it.resourcePath}")
|
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
|
||||||
|
|||||||
@@ -1,41 +1,23 @@
|
|||||||
package mods.betterfoliage.client.config
|
package mods.octarinecore.common.config
|
||||||
|
|
||||||
import mods.octarinecore.client.gui.NonVerboseArrayEntry
|
import mods.octarinecore.client.gui.NonVerboseArrayEntry
|
||||||
import mods.octarinecore.client.resource.get
|
import mods.octarinecore.client.resource.get
|
||||||
import mods.octarinecore.client.resource.getLines
|
import mods.octarinecore.client.resource.getLines
|
||||||
import mods.octarinecore.client.resource.resourceManager
|
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.Configuration
|
||||||
import net.minecraftforge.common.config.Property
|
import net.minecraftforge.common.config.Property
|
||||||
import net.minecraftforge.event.world.WorldEvent
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
|
||||||
|
|
||||||
/**
|
abstract class BlackWhiteListConfigOption<VALUE>(val domain: String, val path: String) : ConfigPropertyBase() {
|
||||||
* 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() {
|
|
||||||
|
|
||||||
val blackList = mutableListOf<Class<*>>()
|
val blackList = mutableListOf<VALUE>()
|
||||||
val whiteList = mutableListOf<Class<*>>()
|
val whiteList = mutableListOf<VALUE>()
|
||||||
val blockIDs = hashSetOf<Int>()
|
|
||||||
var blacklistProperty: Property? = null
|
var blacklistProperty: Property? = null
|
||||||
var whitelistProperty: Property? = null
|
var whitelistProperty: Property? = null
|
||||||
|
|
||||||
fun matchesClass(block: Block): Boolean {
|
override val hasChanged: Boolean
|
||||||
val blockClass = block.javaClass
|
get() = blacklistProperty?.hasChanged() ?: false || whitelistProperty?.hasChanged() ?: false
|
||||||
blackList.forEach { if (it.isAssignableFrom(blockClass)) return false }
|
|
||||||
whiteList.forEach { if (it.isAssignableFrom(blockClass)) return true }
|
override val guiProperties: List<Property> get() = listOf(whitelistProperty!!, blacklistProperty!!)
|
||||||
return false
|
|
||||||
}
|
|
||||||
fun matchesID(block: Block) = blockIDs.contains(Block.REGISTRY.getIDForObject(block))
|
|
||||||
fun matchesID(blockId: Int) = blockIDs.contains(blockId)
|
|
||||||
|
|
||||||
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
||||||
lang = null
|
lang = null
|
||||||
@@ -43,31 +25,23 @@ class BlockMatcher(val domain: String, val path: String) : ConfigPropertyBase()
|
|||||||
blacklistProperty = target.get(categoryName, "${propertyName}Blacklist", defaults.first)
|
blacklistProperty = target.get(categoryName, "${propertyName}Blacklist", defaults.first)
|
||||||
whitelistProperty = target.get(categoryName, "${propertyName}Whitelist", defaults.second)
|
whitelistProperty = target.get(categoryName, "${propertyName}Whitelist", defaults.second)
|
||||||
listOf(blacklistProperty!!, whitelistProperty!!).forEach {
|
listOf(blacklistProperty!!, whitelistProperty!!).forEach {
|
||||||
it.setConfigEntryClass(NonVerboseArrayEntry::class.java)
|
it.configEntryClass = NonVerboseArrayEntry::class.java
|
||||||
it.setLanguageKey("$langPrefix.$categoryName.${it.name}")
|
it.languageKey = "$langPrefix.$categoryName.${it.name}"
|
||||||
}
|
}
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun convertValue(line: String): VALUE?
|
||||||
|
|
||||||
override fun read() {
|
override fun read() {
|
||||||
listOf(Pair(blackList, blacklistProperty!!), Pair(whiteList, whitelistProperty!!)).forEach {
|
listOf(Pair(blackList, blacklistProperty!!), Pair(whiteList, whitelistProperty!!)).forEach {
|
||||||
it.first.clear()
|
it.first.clear()
|
||||||
it.first.addAll(it.second.stringList.map { getJavaClass(it) }.filterNotNull())
|
it.second.stringList.forEach { line ->
|
||||||
}
|
val value = convertValue(line)
|
||||||
updateIDs()
|
if (value != null) it.first.add(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateIDs() {
|
|
||||||
blockIDs.clear()
|
|
||||||
Block.REGISTRY.forEach {
|
|
||||||
if (matchesClass(it as Block)) blockIDs.add(Block.REGISTRY.getIDForObject(it))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>> {
|
fun readDefaults(domain: String, path: String): Pair<Array<String>, Array<String>> {
|
||||||
val blackList = arrayListOf<String>()
|
val blackList = arrayListOf<String>()
|
||||||
@@ -79,10 +53,4 @@ class BlockMatcher(val domain: String, val path: String) : ConfigPropertyBase()
|
|||||||
}
|
}
|
||||||
return (blackList.toTypedArray() to whiteList.toTypedArray())
|
return (blackList.toTypedArray() to whiteList.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun onWorldLoad(event: WorldEvent.Load) { if (event.world is WorldClient) updateIDs() }
|
|
||||||
|
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
33
src/main/kotlin/mods/octarinecore/common/config/Matchers.kt
Normal file
33
src/main/kotlin/mods/octarinecore/common/config/Matchers.kt
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
block/grass,top
|
||||||
|
block/cube_bottom_top,top
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Vanilla
|
||||||
|
net.minecraft.block.BlockLeaves
|
||||||
|
|
||||||
|
// Forestry
|
||||||
|
forestry.arboriculture.gadgets.BlockLeaves
|
||||||
|
|
||||||
|
// Thaumcraft
|
||||||
|
thaumcraft.common.blocks.BlockMagicalLeaves
|
||||||
@@ -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
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
minecraft:block/leaves,all
|
||||||
|
minecraft:block/cube_all,all
|
||||||
@@ -30,15 +30,19 @@ betterfoliage.blocks.dirtBlacklist=Dirt Blacklist
|
|||||||
betterfoliage.blocks.dirtWhitelist.arrayEntry=%d entries
|
betterfoliage.blocks.dirtWhitelist.arrayEntry=%d entries
|
||||||
betterfoliage.blocks.dirtBlacklist.arrayEntry=%d entries
|
betterfoliage.blocks.dirtBlacklist.arrayEntry=%d entries
|
||||||
|
|
||||||
betterfoliage.blocks.grassWhitelist=Grass Whitelist
|
betterfoliage.blocks.grassClassesWhitelist=Grass Whitelist
|
||||||
betterfoliage.blocks.grassBlacklist=Grass Blacklist
|
betterfoliage.blocks.grassClassesBlacklist=Grass Blacklist
|
||||||
betterfoliage.blocks.grassWhitelist.arrayEntry=%d entries
|
betterfoliage.blocks.grassClassesWhitelist.arrayEntry=%d entries
|
||||||
betterfoliage.blocks.grassBlacklist.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.leavesClassesWhitelist=Leaves Whitelist
|
||||||
betterfoliage.blocks.leavesBlacklist=Leaves Blacklist
|
betterfoliage.blocks.leavesClassesBlacklist=Leaves Blacklist
|
||||||
betterfoliage.blocks.leavesWhitelist.arrayEntry=%d entries
|
betterfoliage.blocks.leavesClassesWhitelist.arrayEntry=%d entries
|
||||||
betterfoliage.blocks.leavesBlacklist.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.cropsWhitelist=Crop Whitelist
|
||||||
betterfoliage.blocks.cropsBlacklist=Crop Blacklist
|
betterfoliage.blocks.cropsBlacklist=Crop Blacklist
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
"version": "$version",
|
"version": "$version",
|
||||||
"mcversion": "$mcversion",
|
"mcversion": "$mcversion",
|
||||||
"description": "Leafier leaves and grassier grass",
|
"description": "Leafier leaves and grassier grass",
|
||||||
"authorList" : ["octarine-noise (code)", "Meringue (textures)"]
|
"authorList" : ["octarine-noise (code)", "Meringue (textures)"],
|
||||||
|
"modLanguageAdapter": ""
|
||||||
}]
|
}]
|
||||||
Reference in New Issue
Block a user