add support for block variants and multiple wood log bark textures

This commit is contained in:
octarine-noise
2017-08-12 15:59:06 +02:00
parent 25c302ecb6
commit 4761eb266f
11 changed files with 203 additions and 133 deletions

View File

@@ -7,9 +7,9 @@ import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.texture.ILeafRegistry import mods.betterfoliage.client.texture.ILeafRegistry
import mods.betterfoliage.client.texture.LeafInfo import mods.betterfoliage.client.texture.LeafInfo
import mods.betterfoliage.client.texture.LeafRegistry import mods.betterfoliage.client.texture.LeafRegistry
import mods.betterfoliage.client.texture.StandardLeafSupport
import mods.betterfoliage.loader.Refs import mods.betterfoliage.loader.Refs
import mods.octarinecore.client.resource.ModelProcessor import mods.octarinecore.client.resource.ModelProcessor
import mods.octarinecore.client.resource.ModelVariant
import mods.octarinecore.client.resource.get import mods.octarinecore.client.resource.get
import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.FieldRef import mods.octarinecore.metaprog.FieldRef
@@ -19,7 +19,6 @@ import mods.octarinecore.tryDefault
import net.minecraft.block.state.IBlockState import net.minecraft.block.state.IBlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.block.model.ModelResourceLocation import net.minecraft.client.renderer.block.model.ModelResourceLocation
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.ResourceLocation
@@ -103,9 +102,9 @@ object ForestryLeavesSupport : ILeafRegistry {
textureToValue[textureLocation] = LeafInfo(generated, LeafRegistry.getParticleType(texture, atlas)) textureToValue[textureLocation] = LeafInfo(generated, LeafRegistry.getParticleType(texture, atlas))
} }
override fun get(state: IBlockState) = null override fun get(state: IBlockState, rand: Int) = null
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? { override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int): LeafInfo? {
// check variant property (used in decorative leaves) // check variant property (used in decorative leaves)
state.properties.entries.find { state.properties.entries.find {
ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value) ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value)
@@ -127,21 +126,22 @@ object ForestryLeavesSupport : ILeafRegistry {
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
object ForestryLogSupport : ModelProcessor<List<String>, IColumnTextureInfo>, IColumnRegistry { object ForestryLogSupport : ModelProcessor<List<String>, IColumnTextureInfo>, IColumnRegistry {
override var stateToKey = mutableMapOf<IBlockState, List<String>>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, IColumnTextureInfo>() override var variantToKey = mutableMapOf<ModelVariant, List<String>>()
override var variantToValue = mapOf<ModelVariant, IColumnTextureInfo>()
override val logger = BetterFoliageMod.logDetail override val logger = BetterFoliageMod.logDetail
init { MinecraftForge.EVENT_BUS.register(this) } init { MinecraftForge.EVENT_BUS.register(this) }
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): List<String>? { override fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel) {
// respect class list to avoid triggering on fences, stairs, etc. // respect class list to avoid triggering on fences, stairs, etc.
if (!Config.blocks.logClasses.matchesClass(state.block)) return null if (!Config.blocks.logClasses.matchesClass(state.block)) return
// find wood type property // find wood type property
val woodType = state.properties.entries.find { val woodType = state.properties.entries.find {
ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value) ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value)
} ?: return null } ?: return
logger.log(Level.DEBUG, "ForestryLogSupport: block state ${state.toString()}") logger.log(Level.DEBUG, "ForestryLogSupport: block state ${state.toString()}")
logger.log(Level.DEBUG, "ForestryLogSupport: variant ${woodType.value.toString()}") logger.log(Level.DEBUG, "ForestryLogSupport: variant ${woodType.value.toString()}")
@@ -151,14 +151,14 @@ object ForestryLogSupport : ModelProcessor<List<String>, IColumnTextureInfo>, IC
val heart = ForestryIntegration.heartTex.invoke(woodType.value) as String? val heart = ForestryIntegration.heartTex.invoke(woodType.value) as String?
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]") logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
return if (bark != null && heart != null) listOf(heart, bark) else null if (bark != null && heart != null) putKeySingle(state, listOf(heart, bark))
} }
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap): IColumnTextureInfo? { override fun processStitch(variant: ModelVariant, key: List<String>, atlas: TextureMap): IColumnTextureInfo? {
val heart = atlas[key[0]] ?: return null val heart = atlas[key[0]] ?: return null
val bark = atlas[key[1]] ?: return null val bark = atlas[key[1]] ?: return null
return StaticColumnInfo(StandardLogSupport.getAxis(state), heart, heart, bark) return StaticColumnInfo(StandardLogSupport.getAxis(variant.state), heart, heart, listOf(bark))
} }
override fun get(state: IBlockState) = stateToValue[state] override fun get(state: IBlockState, rand: Int) = variants[state]?.let { variantToValue[it[0]] }
} }

View File

@@ -7,22 +7,16 @@ import mods.betterfoliage.client.render.IColumnRegistry
import mods.betterfoliage.client.render.IColumnTextureInfo import mods.betterfoliage.client.render.IColumnTextureInfo
import mods.betterfoliage.client.render.LogRegistry import mods.betterfoliage.client.render.LogRegistry
import mods.betterfoliage.client.render.StaticColumnInfo import mods.betterfoliage.client.render.StaticColumnInfo
import mods.betterfoliage.client.texture.LeafRegistry
import mods.betterfoliage.client.texture.StandardLeafSupport import mods.betterfoliage.client.texture.StandardLeafSupport
import mods.betterfoliage.loader.Refs import mods.betterfoliage.loader.Refs
import mods.octarinecore.client.render.Quad import mods.octarinecore.client.render.Quad
import mods.octarinecore.client.render.ShadingContext import mods.octarinecore.client.render.ShadingContext
import mods.octarinecore.client.render.blockContext import mods.octarinecore.client.render.blockContext
import mods.octarinecore.client.resource.ModelProcessor import mods.octarinecore.client.resource.*
import mods.octarinecore.client.resource.derivesFrom
import mods.octarinecore.client.resource.get
import mods.octarinecore.client.resource.modelBlockAndLoc
import mods.octarinecore.common.rotate import mods.octarinecore.common.rotate
import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.MethodRef import mods.octarinecore.metaprog.MethodRef
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import mods.octarinecore.tryDefault
import net.minecraft.block.properties.PropertyDirection
import net.minecraft.block.state.IBlockState import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.block.model.ModelResourceLocation import net.minecraft.client.renderer.block.model.ModelResourceLocation
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
@@ -106,36 +100,40 @@ data class RubberLogColumnInfo(override val axis: EnumFacing.Axis?,
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
abstract class RubberLogSupportBase : ModelProcessor<RubberLogModelInfo, IColumnTextureInfo>, IColumnRegistry { abstract class RubberLogSupportBase : ModelProcessor<RubberLogModelInfo, IColumnTextureInfo>, IColumnRegistry {
override var stateToKey = mutableMapOf<IBlockState, RubberLogModelInfo>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, IColumnTextureInfo>() override var variantToKey = mutableMapOf<ModelVariant, RubberLogModelInfo>()
override var variantToValue = mapOf<ModelVariant, IColumnTextureInfo>()
override val logger = BetterFoliageMod.logDetail override val logger = BetterFoliageMod.logDetail
init { MinecraftForge.EVENT_BUS.register(this) } init { MinecraftForge.EVENT_BUS.register(this) }
override fun processStitch(state: IBlockState, key: RubberLogModelInfo, atlas: TextureMap): IColumnTextureInfo? { override fun processStitch(variant: ModelVariant, key: RubberLogModelInfo, atlas: TextureMap): IColumnTextureInfo? {
val topTex = atlas[key.textures[0]] ?: return null val topTex = atlas[key.textures[0]] ?: return null
val bottomTex = atlas[key.textures[1]] ?: return null val bottomTex = atlas[key.textures[1]] ?: return null
val sideTex = atlas[key.textures[2]] ?: return null val sideTex = atlas[key.textures[2]] ?: return null
if (key.spotDir == null) if (key.spotDir == null)
return StaticColumnInfo(key.axis, topTex, bottomTex, sideTex) return StaticColumnInfo(key.axis, topTex, bottomTex, listOf(sideTex))
else { else {
val spotTex = atlas[key.textures[3]] ?: return null val spotTex = atlas[key.textures[3]] ?: return null
return RubberLogColumnInfo(key.axis, key.spotDir, topTex, bottomTex, sideTex, spotTex) return RubberLogColumnInfo(key.axis, key.spotDir, topTex, bottomTex, sideTex, spotTex)
} }
} }
override fun get(state: IBlockState) = stateToValue[state] override fun get(state: IBlockState, rand: Int): IColumnTextureInfo? {
val variant = getVariant(state, rand) ?: return null
return variantToValue[variant]
}
} }
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
object IC2LogSupport : RubberLogSupportBase() { object IC2LogSupport : RubberLogSupportBase() {
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): RubberLogModelInfo? { override fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel) {
// check for proper block class, existence of ModelBlock, and "state" blockstate property // check for proper block class, existence of ModelBlock, and "state" blockstate property
if (!IC2Integration.BlockRubWood.isInstance(state.block)) return null if (!IC2Integration.BlockRubWood.isInstance(state.block)) return
val blockLoc = model.modelBlockAndLoc.firstOrNull() ?: return null val blockLoc = model.modelBlockAndLoc.firstOrNull() ?: return
val type = state.properties.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null val type = state.properties.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return
// logs with no rubber spot // logs with no rubber spot
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) { if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
@@ -148,7 +146,10 @@ object IC2LogSupport : RubberLogSupportBase() {
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) } val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}") logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}")
logger.log(Level.DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}") logger.log(Level.DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}")
return if (textureNames.all { it != "missingno" }) RubberLogModelInfo(axis, null, textureNames) else null if (textureNames.all { it != "missingno" }) {
putKeySingle(state, RubberLogModelInfo(axis, null, textureNames))
}
return
} }
// logs with rubber spot // logs with rubber spot
@@ -162,19 +163,21 @@ object IC2LogSupport : RubberLogSupportBase() {
val textureNames = listOf("up", "down", "south", "north").map { blockLoc.first.resolveTextureName(it) } val textureNames = listOf("up", "down", "south", "north").map { blockLoc.first.resolveTextureName(it) }
logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}") logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}")
logger.log(Level.DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}") logger.log(Level.DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
return if (textureNames.all { it != "missingno" }) RubberLogModelInfo(EnumFacing.Axis.Y, spotDir, textureNames) else null if (textureNames.all { it != "missingno" }) {
putKeySingle(state, RubberLogModelInfo(EnumFacing.Axis.Y, spotDir, textureNames))
}
} }
} }
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
object TechRebornLogSupport : RubberLogSupportBase() { object TechRebornLogSupport : RubberLogSupportBase() {
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): RubberLogModelInfo? { override fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel) {
// check for proper block class, existence of ModelBlock // check for proper block class, existence of ModelBlock
if (!TechRebornIntegration.BlockRubberLog.isInstance(state.block)) return null if (!TechRebornIntegration.BlockRubberLog.isInstance(state.block)) return
val hasSap = state.properties.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null val hasSap = state.properties.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return
val sapSide = state.properties.entries.find { it.key.getName() == "sapside" }?.value as? EnumFacing ?: return null val sapSide = state.properties.entries.find { it.key.getName() == "sapside" }?.value as? EnumFacing ?: return
logger.log(Level.DEBUG, "TechRebornLogSupport: block state ${state.toString()}") logger.log(Level.DEBUG, "TechRebornLogSupport: block state ${state.toString()}")
if (hasSap) { if (hasSap) {
@@ -182,13 +185,17 @@ object TechRebornLogSupport : RubberLogSupportBase() {
TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, it) as String TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, it) as String
} }
logger.log(Level.DEBUG, "TechRebornLogSupport: spotDir=$sapSide, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}") logger.log(Level.DEBUG, "TechRebornLogSupport: spotDir=$sapSide, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
return if (textureNames.all { it != "missingno" }) RubberLogModelInfo(EnumFacing.Axis.Y, sapSide, textureNames) else null if (textureNames.all { it != "missingno" }) {
putKeySingle(state, RubberLogModelInfo(EnumFacing.Axis.Y, sapSide, textureNames))
}
} else { } else {
val textureNames = listOf(EnumFacing.UP, EnumFacing.DOWN, sapSide).map { val textureNames = listOf(EnumFacing.UP, EnumFacing.DOWN, sapSide).map {
TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, it) as String TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, it) as String
} }
logger.log(Level.DEBUG, "TechRebornLogSupport: up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}") logger.log(Level.DEBUG, "TechRebornLogSupport: up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}")
return if (textureNames.all { it != "missingno" }) RubberLogModelInfo(EnumFacing.Axis.Y, null, textureNames) else null if (textureNames.all { it != "missingno" }) {
putKeySingle(state, RubberLogModelInfo(EnumFacing.Axis.Y, null, textureNames))
}
} }
} }
} }
@@ -198,21 +205,21 @@ object TechRebornLeafSupport : ModelProcessor<Nothing, Nothing> {
init { MinecraftForge.EVENT_BUS.register(this) } init { MinecraftForge.EVENT_BUS.register(this) }
override var stateToKey = mutableMapOf<IBlockState, Nothing>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, Nothing>() override var variantToKey = mutableMapOf<ModelVariant, Nothing>()
override var variantToValue = mapOf<ModelVariant, Nothing>()
override val logger: Logger get() = BetterFoliageMod.logDetail override val logger: Logger get() = BetterFoliageMod.logDetail
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): Nothing? { override fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel) {
if (Config.blocks.leavesClasses.matchesClass(state.block) && TechRebornIntegration.ITexturedBlock.isInstance(state.block)) { if (Config.blocks.leavesClasses.matchesClass(state.block) && TechRebornIntegration.ITexturedBlock.isInstance(state.block)) {
val textureName = TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, EnumFacing.UP) as String val textureName = TechRebornIntegration.getTextureNameFromState.invoke(state.block, state, EnumFacing.UP) as String
logger.log(Level.DEBUG, "TechRebornLeafSupport: block state ${state.toString()}") logger.log(Level.DEBUG, "TechRebornLeafSupport: block state ${state.toString()}")
logger.log(Level.DEBUG, "TechRebornLeafSupport: texture=$textureName") logger.log(Level.DEBUG, "TechRebornLeafSupport: texture=$textureName")
// register directly into StandardLeafSupport for the sake of simplicity // register directly into StandardLeafSupport for the sake of simplicity
StandardLeafSupport.stateToKey[state] = listOf(textureName) StandardLeafSupport.putKeySingle(state, listOf(textureName))
} }
return null
} }
// no-op // no-op
override fun processStitch(state: IBlockState, key: Nothing, atlas: TextureMap) = null override fun processStitch(variant: ModelVariant, key: Nothing, atlas: TextureMap) = null
} }

View File

@@ -13,7 +13,6 @@ import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.VertexBuffer import net.minecraft.client.renderer.VertexBuffer
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.util.BlockRenderLayer import net.minecraft.util.BlockRenderLayer
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.* import net.minecraft.util.EnumFacing.*
import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly import net.minecraftforge.fml.relauncher.SideOnly
@@ -28,14 +27,18 @@ interface IColumnTextureInfo {
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
interface IColumnRegistry { interface IColumnRegistry {
operator fun get(state: IBlockState): IColumnTextureInfo? operator fun get(state: IBlockState, rand: Int): IColumnTextureInfo?
} }
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
data class StaticColumnInfo(override val axis: Axis?, data class StaticColumnInfo(override val axis: Axis?,
val topTexture: TextureAtlasSprite, val topTexture: TextureAtlasSprite,
val bottomTexture: TextureAtlasSprite, val bottomTexture: TextureAtlasSprite,
val sideTexture: TextureAtlasSprite) : IColumnTextureInfo { val sideTextures: List<TextureAtlasSprite>) : IColumnTextureInfo {
// index offsets for EnumFacings, to make it less likely for neighboring faces to get the same bark texture
val dirToIdx = arrayOf(0, 1, 2, 4, 3, 5)
override val top: QuadIconResolver = { ctx, _, _ -> override val top: QuadIconResolver = { ctx, _, _ ->
OptifineCTM.override(topTexture, blockContext, UP.rotate(ctx.rotation)) OptifineCTM.override(topTexture, blockContext, UP.rotate(ctx.rotation))
} }
@@ -43,7 +46,9 @@ data class StaticColumnInfo(override val axis: Axis?,
OptifineCTM.override(bottomTexture, blockContext, DOWN.rotate(ctx.rotation)) OptifineCTM.override(bottomTexture, blockContext, DOWN.rotate(ctx.rotation))
} }
override val side: QuadIconResolver = { ctx, idx, _ -> override val side: QuadIconResolver = { ctx, idx, _ ->
OptifineCTM.override(sideTexture, blockContext, (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.rotation)) val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.rotation)
val baseTexture = sideTextures[(blockContext.random(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size]
OptifineCTM.override(baseTexture, blockContext, worldFace)
} }
} }
@@ -141,7 +146,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean { override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: VertexBuffer, layer: BlockRenderLayer): Boolean {
if (ctx.isSurroundedBy(surroundPredicate) ) return false if (ctx.isSurroundedBy(surroundPredicate) ) return false
val columnTextures = registry[ctx.blockState(Int3.zero)] val columnTextures = registry[ctx.blockState(Int3.zero), ctx.random(0)]
if (columnTextures == null) { if (columnTextures == null) {
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos) Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
return renderWorldBlockBase(ctx, dispatcher, renderer, null) return renderWorldBlockBase(ctx, dispatcher, renderer, null)
@@ -346,7 +351,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
return if (!blockPredicate(state)) { return if (!blockPredicate(state)) {
if (state.isOpaqueCube) SOLID else NONSOLID if (state.isOpaqueCube) SOLID else NONSOLID
} else { } else {
(registry[state]?.axis ?: if (Config.roundLogs.defaultY) Axis.Y else null)?.let { (registry[state, random(0)]?.axis ?: if (Config.roundLogs.defaultY) Axis.Y else null)?.let {
if (it == axis) PARALLEL else PERPENDICULAR if (it == axis) PARALLEL else PERPENDICULAR
} ?: SOLID } ?: SOLID
} }

View File

@@ -9,6 +9,7 @@ import mods.octarinecore.client.render.HSB
import mods.octarinecore.common.Double3 import mods.octarinecore.common.Double3
import mods.octarinecore.minmax import mods.octarinecore.minmax
import mods.octarinecore.random import mods.octarinecore.random
import mods.octarinecore.semiRandom
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.VertexBuffer import net.minecraft.client.renderer.VertexBuffer
import net.minecraft.util.EnumFacing.DOWN import net.minecraft.util.EnumFacing.DOWN
@@ -45,7 +46,7 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble
val state = world.getBlockState(pos) val state = world.getBlockState(pos)
val blockColor = 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) val leafInfo = LeafRegistry.get(state, world, pos, DOWN, semiRandom(pos.x, pos.y, pos.z, 0))
if (leafInfo != null) { if (leafInfo != null) {
particleTexture = leafInfo.particleTextures?.get(rand.nextInt(1024)) particleTexture = leafInfo.particleTextures?.get(rand.nextInt(1024))
calculateParticleColor(leafInfo.averageColor, blockColor) calculateParticleColor(leafInfo.averageColor, blockColor)

View File

@@ -4,12 +4,11 @@ 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.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.resource.ModelVariant
import mods.octarinecore.client.resource.TextureListModelProcessor import mods.octarinecore.client.resource.TextureListModelProcessor
import mods.octarinecore.client.resource.get import mods.octarinecore.client.resource.get
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation import mods.octarinecore.common.Rotation
import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.common.config.SimpleBlockMatcher import mods.octarinecore.common.config.SimpleBlockMatcher
import mods.octarinecore.common.config.modelTextures import mods.octarinecore.common.config.modelTextures
import net.minecraft.block.BlockCactus import net.minecraft.block.BlockCactus
@@ -37,8 +36,9 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
init { MinecraftForge.EVENT_BUS.register(this) } init { MinecraftForge.EVENT_BUS.register(this) }
override var stateToKey = mutableMapOf<IBlockState, List<String>>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, IColumnTextureInfo>() override var variantToKey = mutableMapOf<ModelVariant, List<String>>()
override var variantToValue = mapOf<ModelVariant, IColumnTextureInfo>()
override val logger = BetterFoliageMod.logDetail override val logger = BetterFoliageMod.logDetail
override val logName = "CactusTextures" override val logName = "CactusTextures"
@@ -47,14 +47,17 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
modelTextures("block/cactus", "top", "bottom", "side") modelTextures("block/cactus", "top", "bottom", "side")
) )
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap): IColumnTextureInfo? { override fun processStitch(variant: ModelVariant, key: List<String>, atlas: TextureMap): IColumnTextureInfo? {
val topTex = atlas[key[0]] ?: return null val topTex = atlas[key[0]] ?: return null
val bottomTex = atlas[key[1]] ?: return null val bottomTex = atlas[key[1]] ?: return null
val sideTex = atlas[key[2]] ?: return null val sideTex = atlas[key[2]] ?: return null
return StaticColumnInfo(Axis.Y, topTex, bottomTex, sideTex) return StaticColumnInfo(Axis.Y, topTex, bottomTex, listOf(sideTex))
} }
override fun get(state: IBlockState) = stateToValue[state] override fun get(state: IBlockState, rand: Int): IColumnTextureInfo? {
val variant = getVariant(state, rand) ?: return null
return variantToValue[variant]
}
} }
val modelStem = model { val modelStem = model {
@@ -100,7 +103,7 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
// get AO data // get AO data
modelRenderer.updateShading(Int3.zero, allFaces) modelRenderer.updateShading(Int3.zero, allFaces)
val icons = cactusTextures[ctx.blockState(Int3.zero)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, null) val icons = cactusTextures[ctx.blockState(Int3.zero), ctx.random(0)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.render( modelRenderer.render(
renderer, renderer,

View File

@@ -2,27 +2,21 @@ 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.texture.GrassRegistry
import mods.betterfoliage.client.texture.IGrassRegistry
import mods.betterfoliage.client.texture.StandardGrassSupport
import mods.octarinecore.client.render.BlockContext import mods.octarinecore.client.render.BlockContext
import mods.octarinecore.client.resource.ModelVariant
import mods.octarinecore.client.resource.TextureListModelProcessor import mods.octarinecore.client.resource.TextureListModelProcessor
import mods.octarinecore.client.resource.get import mods.octarinecore.client.resource.get
import mods.octarinecore.common.Int3
import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.findFirst import mods.octarinecore.findFirst
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.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.EnumFacing.Axis import net.minecraft.util.EnumFacing.Axis
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
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) { class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
@@ -50,7 +44,7 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
object LogRegistry : IColumnRegistry { object LogRegistry : IColumnRegistry {
val subRegistries: MutableList<IColumnRegistry> = mutableListOf() val subRegistries: MutableList<IColumnRegistry> = mutableListOf()
override fun get(state: IBlockState) = subRegistries.findFirst { it[state] } override fun get(state: IBlockState, rand: Int) = subRegistries.findFirst { it[state, rand] }
} }
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
@@ -61,22 +55,27 @@ object StandardLogSupport : TextureListModelProcessor<IColumnTextureInfo>, IColu
MinecraftForge.EVENT_BUS.register(this) MinecraftForge.EVENT_BUS.register(this)
} }
override var stateToKey = mutableMapOf<IBlockState, List<String>>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, IColumnTextureInfo>() override var variantToKey = mutableMapOf<ModelVariant, List<String>>()
override var variantToValue = mapOf<ModelVariant, IColumnTextureInfo>()
override val logger = BetterFoliageMod.logDetail override val logger = BetterFoliageMod.logDetail
override val logName = "StandardLogSupport" override val logName = "StandardLogSupport"
override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.logClasses override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.logClasses
override val modelTextures: List<ModelTextureList> get() = Config.blocks.logModels.list override val modelTextures: List<ModelTextureList> get() = Config.blocks.logModels.list
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap): IColumnTextureInfo? { override fun processStitch(variant: ModelVariant, key: List<String>, atlas: TextureMap): IColumnTextureInfo? {
val topTex = atlas[key[0]] ?: return null val topTex = atlas[key[0]] ?: return null
val bottomTex = atlas[key[1]] ?: return null val bottomTex = atlas[key[1]] ?: return null
val sideTex = atlas[key[2]] ?: return null val sideTexList = key.drop(2).map { atlas[it] }.filterNotNull()
return StaticColumnInfo(getAxis(state), topTex, bottomTex, sideTex) if (sideTexList.isEmpty()) return null
return StaticColumnInfo(getAxis(variant.state), topTex, bottomTex, sideTexList)
} }
override fun get(state: IBlockState) = stateToValue[state] override fun get(state: IBlockState, rand: Int): IColumnTextureInfo? {
val variant = getVariant(state, rand) ?: return null
return variantToValue[variant]
}
fun getAxis(state: IBlockState): Axis? { fun getAxis(state: IBlockState): Axis? {
val axis = tryDefault(null) { state.getValue(BlockLog.LOG_AXIS).toString() } ?: val axis = tryDefault(null) { state.getValue(BlockLog.LOG_AXIS).toString() } ?:

View File

@@ -5,10 +5,7 @@ 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.render.HSB import mods.octarinecore.client.render.HSB
import mods.octarinecore.client.resource.TextureListModelProcessor import mods.octarinecore.client.resource.*
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.Int3
import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
@@ -42,8 +39,8 @@ class GrassInfo(
) )
interface IGrassRegistry { interface IGrassRegistry {
fun get(state: IBlockState): GrassInfo? operator fun get(state: IBlockState, rand: Int): GrassInfo?
fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): GrassInfo? operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int): GrassInfo?
} }
/** Collects and manages rendering-related information for grass blocks. */ /** Collects and manages rendering-related information for grass blocks. */
@@ -51,12 +48,12 @@ interface IGrassRegistry {
object GrassRegistry : IGrassRegistry { object GrassRegistry : IGrassRegistry {
val subRegistries: MutableList<IGrassRegistry> = mutableListOf(StandardGrassSupport) val subRegistries: MutableList<IGrassRegistry> = mutableListOf(StandardGrassSupport)
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) = override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int) =
subRegistries.findFirst { it.get(state, world, pos, face) } subRegistries.findFirst { it.get(state, world, pos, face, rand) }
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, ctx.random(0))
override fun get(state: IBlockState) = subRegistries.findFirst { it.get(state) } override fun get(state: IBlockState, rand: Int) = subRegistries.findFirst { it[state, rand] }
} }
object StandardGrassSupport : object StandardGrassSupport :
@@ -66,8 +63,9 @@ object StandardGrassSupport :
{ {
init { MinecraftForge.EVENT_BUS.register(this) } init { MinecraftForge.EVENT_BUS.register(this) }
override var stateToKey = mutableMapOf<IBlockState, List<String>>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, TextureAtlasSprite>() override var variantToKey = mutableMapOf<ModelVariant, List<String>>()
override var variantToValue = mapOf<ModelVariant, TextureAtlasSprite>()
override var textureToValue = mutableMapOf<TextureAtlasSprite, GrassInfo>() override var textureToValue = mutableMapOf<TextureAtlasSprite, GrassInfo>()
override val logger = BetterFoliageMod.logDetail override val logger = BetterFoliageMod.logDetail
@@ -75,20 +73,22 @@ object StandardGrassSupport :
override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.grassClasses override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.grassClasses
override val modelTextures: List<ModelTextureList> get() = Config.blocks.grassModels.list override val modelTextures: List<ModelTextureList> get() = Config.blocks.grassModels.list
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): GrassInfo? { override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int): GrassInfo? {
val baseTexture = stateToValue[state] ?: return null val variant = getVariant(state, rand) ?: return null
val baseTexture = variantToValue[variant] ?: return null
return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture] return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture]
} }
override fun get(state: IBlockState) = StandardLeafSupport.stateToValue[state].let { override fun get(state: IBlockState, rand: Int): GrassInfo? {
if (it == null) null else textureToValue[it] val variant = getVariant(state, rand) ?: return null
return variantToValue[variant].let { if (it == null) null else textureToValue[it] }
} }
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap) = atlas[key[0]] override fun processStitch(variant: ModelVariant, key: List<String>, atlas: TextureMap) = atlas[key[0]]
override fun processTexture(states: List<IBlockState>, texture: TextureAtlasSprite, atlas: TextureMap) { override fun processTexture(variants: List<ModelVariant>, texture: TextureAtlasSprite, atlas: TextureMap) {
registerGrass(texture, atlas) registerGrass(texture, atlas)
OptifineCTM.getAllCTM(states, texture).forEach { OptifineCTM.getAllCTM(variants.map { it.state }, texture).forEach {
registerGrass(it, atlas) registerGrass(it, atlas)
} }
} }

View File

@@ -44,8 +44,8 @@ class LeafInfo(
} }
interface ILeafRegistry { interface ILeafRegistry {
operator fun get(state: IBlockState): LeafInfo? operator fun get(state: IBlockState, rand: Int): LeafInfo?
operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int): LeafInfo?
} }
/** Collects and manages rendering-related information for grass blocks. */ /** Collects and manages rendering-related information for grass blocks. */
@@ -62,12 +62,12 @@ object LeafRegistry : ILeafRegistry {
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.DOMAIN, "leafTextureMappings.cfg")) typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.DOMAIN, "leafTextureMappings.cfg"))
} }
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing) = override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int) =
subRegistries.findFirst { it.get(state, world, pos, face) } subRegistries.findFirst { it.get(state, world, pos, face, rand) }
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, ctx.random(0))
override fun get(state: IBlockState) = subRegistries.findFirst { it.get(state) } override fun get(state: IBlockState, rand: Int) = subRegistries.findFirst { it[state, rand] }
fun getParticleType(texture: TextureAtlasSprite, atlas: TextureMap): String { fun getParticleType(texture: TextureAtlasSprite, atlas: TextureMap): String {
var leafType = typeMappings.getType(texture) ?: "default" var leafType = typeMappings.getType(texture) ?: "default"
@@ -100,26 +100,30 @@ object StandardLeafSupport :
override val modelTextures: List<ModelTextureList> get() = Config.blocks.leavesModels.list override val modelTextures: List<ModelTextureList> get() = Config.blocks.leavesModels.list
override val logger: Logger? get() = BetterFoliageMod.logDetail override val logger: Logger? get() = BetterFoliageMod.logDetail
override var stateToKey = mutableMapOf<IBlockState, List<String>>() override var variants = mutableMapOf<IBlockState, MutableList<ModelVariant>>()
override var stateToValue = mapOf<IBlockState, TextureAtlasSprite>() override var variantToKey = mutableMapOf<ModelVariant, List<String>>()
override var variantToValue = mapOf<ModelVariant, TextureAtlasSprite>()
override var textureToValue = mutableMapOf<TextureAtlasSprite, LeafInfo>() override var textureToValue = mutableMapOf<TextureAtlasSprite, LeafInfo>()
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing): LeafInfo? { override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos, face: EnumFacing, rand: Int): LeafInfo? {
val baseTexture = stateToValue[state] ?: return null val variant = getVariant(state, rand) ?: return null
val baseTexture = variantToValue[variant] ?: return null
return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture] return textureToValue[OptifineCTM.override(baseTexture, world, pos, face)] ?: textureToValue[baseTexture]
} }
override fun get(state: IBlockState) = stateToValue[state].let { override fun get(state: IBlockState, rand: Int): LeafInfo? {
if (it == null) null else textureToValue[it] val variant = getVariant(state, rand) ?: return null
return variantToValue[variant].let { if (it == null) null else textureToValue[it] }
} }
override fun processStitch(state: IBlockState, key: List<String>, atlas: TextureMap) = atlas[key[0]] override fun processStitch(variant: ModelVariant, key: List<String>, atlas: TextureMap) = atlas[key[0]]
override fun processTexture(states: List<IBlockState>, texture: TextureAtlasSprite, atlas: TextureMap) { override fun processTexture(variants: List<ModelVariant>, texture: TextureAtlasSprite, atlas: TextureMap) {
logger?.log(Level.DEBUG, "$logName: leaf texture ${texture.iconName}") logger?.log(Level.DEBUG, "$logName: leaf texture ${texture.iconName}")
logger?.log(Level.DEBUG, "$logName: #states ${states.size}") logger?.log(Level.DEBUG, "$logName: #variants ${variants.size}")
logger?.log(Level.DEBUG, "$logName: #states ${variants.distinctBy { it.state }.size}")
registerLeaf(texture, atlas) registerLeaf(texture, atlas)
OptifineCTM.getAllCTM(states, texture).forEach { OptifineCTM.getAllCTM(variants.map { it.state }, texture).forEach {
logger?.log(Level.DEBUG, "$logName: CTM ${texture.iconName}") logger?.log(Level.DEBUG, "$logName: CTM ${texture.iconName}")
registerLeaf(it, atlas) registerLeaf(it, atlas)
} }

View File

@@ -73,6 +73,11 @@ fun <T> tryDefault(default: T, work: ()->T) = try { work() } catch (e: Throwable
/** Return a random [Double] value between the given two limits (inclusive min, exclusive max). */ /** Return a random [Double] value between the given two limits (inclusive min, exclusive max). */
fun random(min: Double, max: Double) = Math.random().let { min + (max - min) * it } fun random(min: Double, max: Double) = Math.random().let { min + (max - min) * it }
fun semiRandom(x: Int, y: Int, z: Int, seed: Int): Int {
var value = (x * x + y * y + z * z + x * y + y * z + z * x + (seed * seed)) and 63
value = (3 * x * value + 5 * y * value + 7 * z * value + (11 * seed)) and 63
return value
}
/** /**
* Return this [Double] value if it lies between the two limits. If outside, return the * Return this [Double] value if it lies between the two limits. If outside, return the
* minimum/maximum value correspondingly. * minimum/maximum value correspondingly.

View File

@@ -10,6 +10,7 @@ import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.common.forgeDirOffsets import mods.octarinecore.common.forgeDirOffsets
import mods.octarinecore.common.plus import mods.octarinecore.common.plus
import mods.octarinecore.semiRandom
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.Minecraft import net.minecraft.client.Minecraft
@@ -105,11 +106,7 @@ class BlockContext {
fun isSurroundedBy(predicate: (IBlockState)->Boolean) = forgeDirOffsets.all { predicate(blockState(it)) } fun isSurroundedBy(predicate: (IBlockState)->Boolean) = forgeDirOffsets.all { predicate(blockState(it)) }
/** Get a semi-random value based on the block coordinate and the given seed. */ /** Get a semi-random value based on the block coordinate and the given seed. */
fun random(seed: Int): Int { fun random(seed: Int) = semiRandom(pos.x, pos.y, pos.z, seed)
var value = (pos.x * pos.x + pos.y * pos.y + pos.z * pos.z + pos.x * pos.y + pos.y * pos.z + pos.z * pos.x + (seed * seed)) and 63
value = (3 * pos.x * value + 5 * pos.y * value + 7 * pos.z * value + (11 * seed)) and 63
return value
}
/** Get an array of semi-random values based on the block coordinate. */ /** Get an array of semi-random values based on the block coordinate. */
fun semiRandomArray(num: Int): Array<Int> = Array(num) { random(it) } fun semiRandomArray(num: Int): Array<Int> = Array(num) { random(it) }

View File

@@ -2,17 +2,18 @@ package mods.octarinecore.client.resource
import com.google.common.base.Joiner import com.google.common.base.Joiner
import mods.betterfoliage.loader.Refs import mods.betterfoliage.loader.Refs
import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.IBlockMatcher import mods.octarinecore.common.config.IBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.filterValuesNotNull import mods.octarinecore.filterValuesNotNull
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
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.ResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.client.model.IModel import net.minecraftforge.client.model.IModel
import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.client.model.ModelLoader
@@ -24,20 +25,38 @@ import org.apache.logging.log4j.Logger
class LoadModelDataEvent(val loader: ModelLoader) : Event() class LoadModelDataEvent(val loader: ModelLoader) : Event()
data class ModelVariant(
val state: IBlockState,
var modelBlock: ModelBlock?,
val modelLocation: ResourceLocation?,
val weight: Int
)
interface ModelProcessor<T1, T2> { interface ModelProcessor<T1, T2> {
val logger: Logger? val logger: Logger?
var stateToKey: MutableMap<IBlockState, T1> var variants: MutableMap<IBlockState, MutableList<ModelVariant>>
var stateToValue: Map<IBlockState, T2> var variantToKey: MutableMap<ModelVariant, T1>
var variantToValue: Map<ModelVariant, T2>
fun addVariant(state: IBlockState, variant: ModelVariant) { variants.getOrPut(state) { mutableListOf() }.add(variant) }
fun getVariant(state: IBlockState, rand: Int) = variants[state]?.let { it[rand % it.size] }
fun putKeySingle(state: IBlockState, key: T1) {
val variant = ModelVariant(state, null, null, 1)
variants[state] = mutableListOf(variant)
variantToKey[variant] = key
}
fun onPostLoad() { } fun onPostLoad() { }
fun onPreStitch() { } fun onPreStitch() { }
fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): T1? fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel)
fun processStitch(state: IBlockState, key: T1, atlas: TextureMap): T2?
fun processStitch(variant: ModelVariant, key: T1, atlas: TextureMap): T2?
@SubscribeEvent(priority = EventPriority.HIGHEST) @SubscribeEvent(priority = EventPriority.HIGHEST)
fun clearBeforeLoadModelData(event: LoadModelDataEvent) { fun clearBeforeLoadModelData(event: LoadModelDataEvent) {
stateToKey.clear() variants.clear()
variantToKey.clear()
} }
@SubscribeEvent @SubscribeEvent
@@ -52,7 +71,7 @@ interface ModelProcessor<T1, T2> {
stateMappings.forEach { mapping -> stateMappings.forEach { mapping ->
if (mapping.key.block != null) stateModels[mapping.value]?.let { model -> if (mapping.key.block != null) stateModels[mapping.value]?.let { model ->
processModelLoad(mapping.key, mapping.value, model)?.let { key -> stateToKey.put(mapping.key, key) } processModelLoad1(mapping.key, mapping.value, model)
} }
} }
} }
@@ -61,7 +80,7 @@ interface ModelProcessor<T1, T2> {
@SubscribeEvent(priority = EventPriority.LOW) @SubscribeEvent(priority = EventPriority.LOW)
fun handlePreStitch(event: TextureStitchEvent.Pre) { fun handlePreStitch(event: TextureStitchEvent.Pre) {
onPreStitch() onPreStitch()
stateToValue = stateToKey.mapValues { processStitch(it.key, it.value, event.map) }.filterValuesNotNull() variantToValue = variantToKey.mapValues { processStitch(it.key, it.value, event.map) }.filterValuesNotNull()
} }
} }
@@ -70,30 +89,60 @@ interface TextureListModelProcessor<T2> : ModelProcessor<List<String>, T2> {
val matchClasses: IBlockMatcher val matchClasses: IBlockMatcher
val modelTextures: List<ModelTextureList> val modelTextures: List<ModelTextureList>
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): List<String>? { override fun processModelLoad1(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel) {
val matchClass = matchClasses.matchingClass(state.block) ?: return null val matchClass = matchClasses.matchingClass(state.block) ?: return
logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}") logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}")
logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}") logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}")
val allModels = model.modelBlockAndLoc val allModels = model.modelBlockAndLoc.distinctBy { it.second }
if (allModels.isEmpty()) { if (allModels.isEmpty()) {
logger?.log(Level.DEBUG, "$logName: no models found") logger?.log(Level.DEBUG, "$logName: no models found")
return null return
} }
allModels.forEach { blockLoc -> allModels.forEach { blockLoc ->
modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) }?.let{ modelMatch -> val modelMatch = modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) }
logger?.log(Level.DEBUG, "$logName: model ${blockLoc.second} matches ${modelMatch.modelLocation.toString()}") if (modelMatch != null) {
logger?.log(Level.DEBUG, "$logName: model ${blockLoc.second} matches ${modelMatch.modelLocation}")
val textures = modelMatch.textureNames.map { it to blockLoc.first.resolveTextureName(it) } val textures = modelMatch.textureNames.map { it to blockLoc.first.resolveTextureName(it) }
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" }) val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
logger?.log(Level.DEBUG, "$logName: textures [$texMapString]") logger?.log(Level.DEBUG, "$logName: textures [$texMapString]")
return if (textures.all { it.second != "missingno" }) textures.map { it.second } else null if (textures.all { it.second != "missingno" }) {
// found a valid variant (all required textures exist)
val variant = ModelVariant(state, blockLoc.first, blockLoc.second, 1)
addVariant(state, variant)
variantToKey[variant] = textures.map { it.second }
} }
} }
logger?.log(Level.DEBUG, "$logName: no matching models found")
return null
} }
}
// 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 allModels = model.modelBlockAndLoc
// if (allModels.isEmpty()) {
// logger?.log(Level.DEBUG, "$logName: no models found")
// return null
// }
// allModels.forEach { blockLoc ->
// modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) }?.let{ modelMatch ->
// 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
// }
// }
// logger?.log(Level.DEBUG, "$logName: no matching models found")
// return null
// }
} }
interface TextureMediatedRegistry<T1, T3> : ModelProcessor<T1, TextureAtlasSprite> { interface TextureMediatedRegistry<T1, T3> : ModelProcessor<T1, TextureAtlasSprite> {
@@ -105,9 +154,9 @@ interface TextureMediatedRegistry<T1, T3> : ModelProcessor<T1, TextureAtlasSprit
textureToValue.clear() textureToValue.clear()
super.handlePreStitch(event) super.handlePreStitch(event)
val textureToStates = stateToValue.entries.groupBy(keySelector = { it.value }, valueTransform = { it.key }) val textureToVariants = variantToValue.entries.groupBy(keySelector = { it.value }, valueTransform = { it.key })
stateToValue.values.toSet().forEach { processTexture(textureToStates[it]!!, it, event.map) } variantToValue.values.toSet().forEach { processTexture(textureToVariants[it]!!, it, event.map) }
} }
fun processTexture(states: List<IBlockState>, texture: TextureAtlasSprite, atlas: TextureMap) fun processTexture(states: List<ModelVariant>, texture: TextureAtlasSprite, atlas: TextureMap)
} }