[WIP] Cactus, netherrack, round logs work
+ lots more cleanup + Optifine x-ray fix
This commit is contained in:
@@ -5,6 +5,7 @@ import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import mods.betterfoliage.util.SimpleInvalidator
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.IModelTransform
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
@@ -19,7 +20,9 @@ import net.minecraftforge.eventbus.api.Event
|
||||
import net.minecraftforge.eventbus.api.EventPriority
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
||||
import org.apache.logging.log4j.Level.DEBUG
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.function.Function
|
||||
|
||||
@@ -27,6 +30,21 @@ data class ModelDefinitionsLoadedEvent(
|
||||
val bakery: ModelBakery
|
||||
) : Event()
|
||||
|
||||
data class ModelDiscoveryContext(
|
||||
val bakery: ModelBakery,
|
||||
val blockState: BlockState,
|
||||
val modelLocation: ResourceLocation,
|
||||
val sprites: MutableSet<ResourceLocation>,
|
||||
val replacements: MutableMap<ResourceLocation, ModelBakingKey>,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked(location: ResourceLocation = modelLocation) = bakery.getUnbakedModel(location)
|
||||
fun addReplacement(key: ModelBakingKey) {
|
||||
replacements[modelLocation] = key
|
||||
logger.log(INFO, "Adding model replacement $modelLocation -> $key")
|
||||
}
|
||||
}
|
||||
|
||||
interface ModelDiscovery {
|
||||
fun onModelsLoaded(
|
||||
bakery: ModelBakery,
|
||||
@@ -35,14 +53,20 @@ interface ModelDiscovery {
|
||||
)
|
||||
}
|
||||
|
||||
data class ModelBakingContext(
|
||||
val bakery: ModelBakery,
|
||||
val spriteGetter: Function<Material, TextureAtlasSprite>,
|
||||
val location: ResourceLocation,
|
||||
val transform: IModelTransform,
|
||||
val logger: Logger
|
||||
) {
|
||||
fun getUnbaked() = bakery.getUnbakedModel(location)
|
||||
fun getBaked() = bakery.getBakedModel(location, transform, spriteGetter)
|
||||
}
|
||||
|
||||
interface ModelBakingKey {
|
||||
fun bake(
|
||||
location: ResourceLocation,
|
||||
unbaked: IUnbakedModel,
|
||||
transform: IModelTransform,
|
||||
bakery: ModelBakery,
|
||||
spriteGetter: Function<Material, TextureAtlasSprite>
|
||||
): IBakedModel? = unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||
fun bake(ctx: ModelBakingContext): IBakedModel? =
|
||||
ctx.getUnbaked().bakeModel(ctx.bakery, ctx.spriteGetter, ctx.transform, ctx.location)
|
||||
}
|
||||
|
||||
object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
@@ -57,14 +81,16 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
modelsValid.invalidate()
|
||||
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
||||
logger.log(INFO, "starting model discovery (${discoverers.size} listeners)")
|
||||
discoverers.forEach { listener ->
|
||||
val replacementsLocal = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||
listener.onModelsLoaded(event.bakery, sprites, replacementsLocal)
|
||||
replacements.putAll(replacementsLocal)
|
||||
listener.onModelsLoaded(event.bakery, sprites, replacements)
|
||||
}
|
||||
val elapsed = System.currentTimeMillis() - startTime
|
||||
logger.log(INFO, "finished model discovery in $elapsed ms, ${replacements.size} top-level replacements")
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -89,17 +115,12 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
transform: IModelTransform,
|
||||
location: ResourceLocation
|
||||
): IBakedModel? {
|
||||
val ctx = ModelBakingContext(bakery, spriteGetter, location, transform, detailLogger)
|
||||
// bake replacement if available
|
||||
replacements[location]?.let { replacement ->
|
||||
detailLogger.log(INFO, "Baking replacement for [${unbaked::class.java.simpleName}] $location -> $replacement")
|
||||
return replacement.bake(location, unbaked, transform, bakery, spriteGetter)
|
||||
return replacement.bake(ctx)
|
||||
}
|
||||
// container model support
|
||||
if (unbaked is VariantList) SpecialRenderVariantList.bakeIfSpecial(location, unbaked, bakery, spriteGetter)?.let {
|
||||
detailLogger.log(INFO, "Wrapping container [${unbaked::class.java.simpleName}] $location")
|
||||
return it
|
||||
}
|
||||
|
||||
return unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.Client
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.util.ResourceLocation
|
||||
|
||||
class BlockTypeCache {
|
||||
val leaf = mutableSetOf<BlockState>()
|
||||
val grass = mutableSetOf<BlockState>()
|
||||
val dirt = mutableSetOf<BlockState>()
|
||||
|
||||
companion object : ModelDiscovery {
|
||||
override fun onModelsLoaded(bakery: ModelBakery, sprites: MutableSet<ResourceLocation>, replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||
) {
|
||||
Client.blockTypes = BlockTypeCache()
|
||||
}
|
||||
}
|
||||
val stateKeys = mutableMapOf<BlockState, ModelBakingKey>()
|
||||
|
||||
inline fun <reified T> getTyped(state: BlockState) = stateKeys[state] as? T
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.util.getJavaClass
|
||||
import mods.betterfoliage.util.getLines
|
||||
import mods.betterfoliage.util.resourceManager
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
|
||||
interface IBlockMatcher {
|
||||
fun matchesClass(block: Block): Boolean
|
||||
fun matchingClass(block: Block): Class<*>?
|
||||
}
|
||||
|
||||
class SimpleBlockMatcher(vararg val classes: Class<*>) : IBlockMatcher {
|
||||
override fun matchesClass(block: Block) = matchingClass(block) != null
|
||||
|
||||
override fun matchingClass(block: Block): Class<*>? {
|
||||
val blockClass = block.javaClass
|
||||
classes.forEach { if (it.isAssignableFrom(blockClass)) return it }
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigurableBlockMatcher(val location: ResourceLocation) : IBlockMatcher {
|
||||
val logger = BetterFoliageMod.detailLogger(this)
|
||||
|
||||
val blackList = mutableListOf<Class<*>>()
|
||||
val whiteList = mutableListOf<Class<*>>()
|
||||
|
||||
override 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
|
||||
}
|
||||
|
||||
override 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
|
||||
}
|
||||
|
||||
fun readDefaults() {
|
||||
blackList.clear()
|
||||
whiteList.clear()
|
||||
resourceManager.getAllResources(location).forEach { resource ->
|
||||
logger.log(INFO, "Reading block class configuration $location from pack ${resource.packName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
if (line.startsWith("-")) getJavaClass(line.substring(1))?.let { blackList.add(it) }
|
||||
else getJavaClass(line)?.let { whiteList.add(it) }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ModelTextureList(val modelLocation: ResourceLocation, val textureNames: List<String>) {
|
||||
constructor(vararg args: String) : this(ResourceLocation(args[0]), listOf(*args).drop(1))
|
||||
}
|
||||
|
||||
class ModelTextureListConfiguration(val location: ResourceLocation) {
|
||||
val logger = BetterFoliageMod.detailLogger(this)
|
||||
|
||||
val modelList = mutableListOf<ModelTextureList>()
|
||||
fun readDefaults() {
|
||||
resourceManager.getAllResources(location).forEach { resource ->
|
||||
logger.log(INFO, "Reading model/texture configuration $location from pack ${resource.packName}")
|
||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||
val elements = line.split(",")
|
||||
modelList.add(ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import com.google.common.base.Joiner
|
||||
import mods.betterfoliage.config.IBlockMatcher
|
||||
import mods.betterfoliage.config.ModelTextureList
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.BlockModelShapes
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.client.renderer.model.multipart.Multipart
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
@@ -25,36 +21,29 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
.flatMap { block -> block.stateContainer.validStates }
|
||||
.forEach { state ->
|
||||
val location = BlockModelShapes.getModelLocation(state)
|
||||
val ctx = ModelDiscoveryContext(bakery, state, location, sprites, replacements, detailLogger)
|
||||
try {
|
||||
val hasReplaced = processModel(bakery, state, location, sprites, replacements)
|
||||
processModel(ctx)
|
||||
} catch (e: Exception) {
|
||||
logger.log(Level.WARN, "Discovery error in $location", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun processModel(
|
||||
bakery: ModelBakery,
|
||||
state: BlockState,
|
||||
location: ResourceLocation,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||
): Boolean {
|
||||
open fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
|
||||
// built-in support for container models
|
||||
return when (val model = bakery.getUnbakedModel(location)) {
|
||||
is VariantList -> {
|
||||
val hasReplaced = model.variantList.fold(false) { hasReplaced, variant ->
|
||||
processModel(bakery, state, variant.modelLocation, sprites, replacements) || hasReplaced
|
||||
}
|
||||
if (hasReplaced) replacements[location]
|
||||
hasReplaced
|
||||
if (model is VariantList) {
|
||||
// per-location replacements need to be scoped to the variant list, as replacement models
|
||||
// may need information from the BlockState which is not available at baking time
|
||||
val scopedReplacements = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||
model.variantList.forEach { variant ->
|
||||
processModel(ctx.copy(modelLocation = variant.modelLocation, replacements = scopedReplacements))
|
||||
}
|
||||
is Multipart -> model.variants.fold(false) { hasReplaced, variantList ->
|
||||
variantList.variantList.fold(false) { hasReplaced, variant ->
|
||||
processModel(bakery, state, variant.modelLocation, sprites, replacements) || hasReplaced
|
||||
} || hasReplaced
|
||||
if (scopedReplacements.isNotEmpty()) {
|
||||
ctx.addReplacement(SpecialRenderVariantList(scopedReplacements))
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,37 +53,23 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
||||
abstract val modelTextures: List<ModelTextureList>
|
||||
|
||||
abstract fun processModel(
|
||||
state: BlockState,
|
||||
location: ResourceLocation,
|
||||
textureMatch: List<ResourceLocation>,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||
): Boolean
|
||||
ctx: ModelDiscoveryContext,
|
||||
textureMatch: List<ResourceLocation>
|
||||
)
|
||||
|
||||
override fun processModel(
|
||||
bakery: ModelBakery,
|
||||
state: BlockState,
|
||||
location: ResourceLocation,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||
): Boolean {
|
||||
val model = bakery.getUnbakedModel(location)
|
||||
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
if (model is BlockModel) {
|
||||
val matchClass = matchClasses.matchingClass(state.block) ?: return false
|
||||
val matchClass = matchClasses.matchingClass(ctx.blockState.block) ?: return
|
||||
|
||||
detailLogger.log(Level.INFO, "block state $state")
|
||||
detailLogger.log(Level.INFO, " model $location")
|
||||
replacements[location]?.let { existing ->
|
||||
detailLogger.log(Level.INFO, " already processed as $existing")
|
||||
return true
|
||||
}
|
||||
|
||||
detailLogger.log(Level.INFO, " class ${state.block.javaClass.name} matches ${matchClass.name}")
|
||||
detailLogger.log(Level.INFO, "block state ${ctx.blockState}")
|
||||
detailLogger.log(Level.INFO, " model ${ctx.modelLocation}")
|
||||
detailLogger.log(Level.INFO, " class ${ctx.blockState.block.javaClass.name} matches ${matchClass.name}")
|
||||
|
||||
modelTextures
|
||||
.filter { matcher -> bakery.modelDerivesFrom(model, location, matcher.modelLocation) }
|
||||
.filter { matcher -> ctx.bakery.modelDerivesFrom(model, ctx.modelLocation, matcher.modelLocation) }
|
||||
.forEach { match ->
|
||||
detailLogger.log(Level.INFO, " model ${model} matches ${match.modelLocation}")
|
||||
detailLogger.log(Level.INFO, " model $model matches ${match.modelLocation}")
|
||||
|
||||
val materials = match.textureNames.map { it to model.resolveTextureName(it) }
|
||||
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.textureLocation}" })
|
||||
@@ -102,12 +77,11 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
||||
|
||||
if (materials.all { it.second.textureLocation != MissingTextureSprite.getLocation() }) {
|
||||
// found a valid model (all required textures exist)
|
||||
if (processModel(state, location, materials.map { it.second.textureLocation }, sprites, replacements))
|
||||
return true
|
||||
processModel(ctx, materials.map { it.second.textureLocation })
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.processModel(bakery, state, location, sprites, replacements)
|
||||
return super.processModel(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.model.HalfBakedSimpleModelWrapper
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.model.SpecialRenderVariantList
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.SimpleBakedModel
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.Level.WARN
|
||||
|
||||
class SpecialRenderVariantList(
|
||||
val replacements: Map<ResourceLocation, ModelBakingKey>
|
||||
) : ModelBakingKey {
|
||||
|
||||
override fun bake(ctx: ModelBakingContext): IBakedModel? {
|
||||
val unbaked = ctx.getUnbaked()
|
||||
if (unbaked !is VariantList) return super.bake(ctx)
|
||||
|
||||
// bake all variants, replace as needed
|
||||
val bakedModels = unbaked.variantList.mapNotNull {
|
||||
val variantCtx = ctx.copy(location = it.modelLocation, transform = it)
|
||||
val replacement = replacements[it.modelLocation]
|
||||
val baked = replacement?.let { replacement ->
|
||||
ctx.logger.log(INFO, "Baking replacement for variant [${variantCtx.getUnbaked()::class.java.simpleName}] ${variantCtx.location} -> $replacement")
|
||||
replacement.bake(variantCtx)
|
||||
} ?: variantCtx.getBaked()
|
||||
when(baked) {
|
||||
is SpecialRenderModel -> it to baked
|
||||
is SimpleBakedModel -> it to HalfBakedSimpleModelWrapper(baked)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
// something fishy going on, possibly unknown model type
|
||||
// let it through unchanged
|
||||
if (bakedModels.isEmpty()) return super.bake(ctx)
|
||||
|
||||
if (bakedModels.size < unbaked.variantList.size) {
|
||||
SpecialRenderVariantList.detailLogger.log(
|
||||
WARN,
|
||||
"Dropped ${unbaked.variantList.size - bakedModels.size} variants from model ${ctx.location}"
|
||||
)
|
||||
}
|
||||
val weightedSpecials = bakedModels.map { (variant, model) ->
|
||||
SpecialRenderVariantList.WeightedModel(model, variant.weight)
|
||||
}
|
||||
return SpecialRenderVariantList(weightedSpecials, weightedSpecials[0].model)
|
||||
}
|
||||
|
||||
override fun toString() = "[SpecialRenderVariantList, ${replacements.size} replacements]"
|
||||
}
|
||||
Reference in New Issue
Block a user