[WIP] major rewrite, grass and leaves working already
This commit is contained in:
@@ -1,95 +0,0 @@
|
||||
package mods.octarinecore.client.resource
|
||||
|
||||
import mods.betterfoliage.resource.Identifier
|
||||
import mods.betterfoliage.resource.Sprite
|
||||
import mods.betterfoliage.util.map
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||
import net.minecraft.profiler.IProfiler
|
||||
import net.minecraft.resources.IResourceManager
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Main entry point to atlas manipulation. Called from mixins that wrap [AtlasTexture.stitch] calls.
|
||||
*
|
||||
* 1. All registered providers receive an [AsyncSpriteProvider.setup] call. Providers can set up their
|
||||
* processing chain at this point, but should not do anything yet except configuration and housekeeping.
|
||||
* 2. The [CompletableFuture] of the stitch source finishes, starting the "discovery" phase. Providers
|
||||
* may register sprites in the [AtlasFuture].
|
||||
* 3. After all providers finish their discovery, the atlas is stitched.
|
||||
* 4. The [AtlasFuture] finishes, starting the "cleanup" phase. All [AtlasFuture.runAfter] and
|
||||
* [AtlasFuture.mapAfter] tasks are processed.
|
||||
* 5. After all providers finish their cleanup, we return to the original code path.
|
||||
*/
|
||||
class AsnycSpriteProviderManager<SOURCE: Any>(val profilerSection: String) {
|
||||
|
||||
val providers = mutableListOf<AsyncSpriteProvider<SOURCE>>()
|
||||
|
||||
/**
|
||||
* Needed in order to keep the actual [AtlasTexture.stitch] call in the original method, in case
|
||||
* other modders want to modify it too.
|
||||
*/
|
||||
class StitchWrapper(val idList: Iterable<Identifier>, val onComplete: (AtlasTexture.SheetData)->Unit) {
|
||||
fun complete(sheet: AtlasTexture.SheetData) = onComplete(sheet)
|
||||
}
|
||||
|
||||
var currentAtlas: AtlasFuture? = null
|
||||
var currentPhases: List<StitchPhases> = emptyList()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun prepare(sourceObj: Any, manager: IResourceManager, idList: Iterable<Identifier>, profiler: IProfiler): Set<Identifier> {
|
||||
profiler.startSection(profilerSection)
|
||||
|
||||
val source = CompletableFuture<SOURCE>()
|
||||
currentAtlas = AtlasFuture(idList)
|
||||
|
||||
currentPhases = providers.map { it.setup(manager, source, currentAtlas!!) }
|
||||
source.complete(sourceObj as SOURCE)
|
||||
currentPhases.forEach { it.discovery.get() }
|
||||
|
||||
return currentAtlas!!.idSet
|
||||
}
|
||||
|
||||
fun finish(sheetData: AtlasTexture.SheetData, profiler: IProfiler): AtlasTexture.SheetData {
|
||||
currentAtlas!!.complete(sheetData)
|
||||
currentPhases.forEach { it.cleanup.get() }
|
||||
currentAtlas = null
|
||||
currentPhases = emptyList()
|
||||
profiler.endSection()
|
||||
return sheetData
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a way for [AsyncSpriteProvider]s to register sprites to receive [CompletableFuture]s.
|
||||
* Tracks sprite ids that need to be stitched.
|
||||
*/
|
||||
class AtlasFuture(initial: Iterable<Identifier>) {
|
||||
val idSet = Collections.synchronizedSet(mutableSetOf<Identifier>().apply { addAll(initial) })
|
||||
protected val sheet = CompletableFuture<AtlasTexture.SheetData>()
|
||||
protected val finished = CompletableFuture<Void>()
|
||||
|
||||
fun complete(sheetData: AtlasTexture.SheetData) {
|
||||
sheet.complete(sheetData)
|
||||
finished.complete(null)
|
||||
}
|
||||
|
||||
fun sprite(id: String) = sprite(Identifier(id))
|
||||
fun sprite(id: Identifier): CompletableFuture<Sprite> {
|
||||
idSet.add(id)
|
||||
return sheet.map { sheetData -> sheetData.sprites.find { it.name == id } ?: throw IllegalStateException("Atlas does not contain $id") }
|
||||
}
|
||||
val missing = sheet.map { sheetData -> sheetData.sprites.find { it.name == MissingTextureSprite.getLocation() } }
|
||||
fun <T> mapAfter(supplier: ()->T): CompletableFuture<T> = finished.map{ supplier() }
|
||||
fun runAfter(action: ()->Unit): CompletableFuture<Void> = finished.thenRun(action)
|
||||
}
|
||||
|
||||
class StitchPhases(
|
||||
val discovery: CompletableFuture<Void>,
|
||||
val cleanup: CompletableFuture<Void>
|
||||
)
|
||||
|
||||
interface AsyncSpriteProvider<SOURCE: Any> {
|
||||
fun setup(manager: IResourceManager, source: CompletableFuture<SOURCE>, atlas: AtlasFuture): StitchPhases
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.ModelDefinitionsLoadedEvent
|
||||
import mods.betterfoliage.render.bakeSpecial
|
||||
import mods.betterfoliage.util.Atlas
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.Invalidator
|
||||
import mods.betterfoliage.util.SimpleInvalidator
|
||||
import mods.betterfoliage.util.asBlockMaterial
|
||||
import net.minecraft.client.renderer.model.IBakedModel
|
||||
import net.minecraft.client.renderer.model.IModelTransform
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.event.ModelBakeEvent
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import net.minecraftforge.common.ForgeConfig
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.function.Function
|
||||
|
||||
interface ModelDiscovery {
|
||||
fun onModelsLoaded(
|
||||
bakery: ModelBakery,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakeKey>
|
||||
)
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ModelBakeKey {
|
||||
fun replace(
|
||||
location: ResourceLocation,
|
||||
unbaked: IUnbakedModel,
|
||||
transform: IModelTransform,
|
||||
bakery: ModelBakery,
|
||||
spriteGetter: Function<Material, TextureAtlasSprite>
|
||||
): IBakedModel? = unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||
}
|
||||
|
||||
interface ModelWrapperKey : ModelBakeKey {
|
||||
override fun replace(
|
||||
location: ResourceLocation,
|
||||
unbaked: IUnbakedModel,
|
||||
transform: IModelTransform,
|
||||
bakery: ModelBakery,
|
||||
spriteGetter: Function<Material, TextureAtlasSprite>
|
||||
): IBakedModel? {
|
||||
val baked = super.replace(location, unbaked, transform, bakery, spriteGetter) ?: return null
|
||||
val sprites = { res: ResourceLocation -> spriteGetter.apply(res.asBlockMaterial) }
|
||||
return replace(location, baked, sprites)
|
||||
}
|
||||
|
||||
fun replace(
|
||||
location: ResourceLocation,
|
||||
wrapped: IBakedModel,
|
||||
sprites: (ResourceLocation) -> TextureAtlasSprite
|
||||
) = replace(wrapped)
|
||||
|
||||
fun replace(wrapped: IBakedModel) = wrapped
|
||||
}
|
||||
|
||||
object BakeWrapperManager : Invalidator, HasLogger() {
|
||||
val discoverers = mutableListOf<ModelDiscovery>()
|
||||
override val callbacks = mutableListOf<WeakReference<()->Unit>>()
|
||||
|
||||
val modelsValid = SimpleInvalidator()
|
||||
val spritesValid = SimpleInvalidator()
|
||||
|
||||
private val replacements = mutableMapOf<ResourceLocation, ModelBakeKey>()
|
||||
private val sprites = mutableSetOf<ResourceLocation>()
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||
modelsValid.invalidate()
|
||||
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
||||
logger.log(INFO, "starting model discovery (${discoverers.size} listeners)")
|
||||
discoverers.forEach { listener ->
|
||||
val replacementsLocal = mutableMapOf<ResourceLocation, ModelBakeKey>()
|
||||
listener.onModelsLoaded(event.bakery, sprites, replacementsLocal)
|
||||
replacements.putAll(replacementsLocal)
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleStitch(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureLocation == Atlas.BLOCKS.resourceId) {
|
||||
logger.log(INFO, "Adding ${sprites.size} sprites to block atlas")
|
||||
spritesValid.invalidate()
|
||||
sprites.forEach { event.addSprite(it) }
|
||||
sprites.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleModelBake(event: ModelBakeEvent) {
|
||||
replacements.clear()
|
||||
}
|
||||
|
||||
fun onBake(
|
||||
unbaked: IUnbakedModel,
|
||||
bakery: ModelBakery,
|
||||
spriteGetter: Function<Material, TextureAtlasSprite>,
|
||||
transform: IModelTransform,
|
||||
location: ResourceLocation
|
||||
): IBakedModel? {
|
||||
// bake replacement if available
|
||||
replacements[location]?.let { replacement ->
|
||||
detailLogger.log(INFO, "Baking replacement for [${unbaked::class.java.simpleName}] $location -> $replacement")
|
||||
return replacement.replace(location, unbaked, transform, bakery, spriteGetter)
|
||||
}
|
||||
// container model support
|
||||
if (unbaked is VariantList) unbaked.bakeSpecial(bakery, spriteGetter)?.let {
|
||||
detailLogger.log(INFO, "Wrapping container [${unbaked::class.java.simpleName}] $location")
|
||||
return it
|
||||
}
|
||||
|
||||
return unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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, ModelBakeKey>
|
||||
) {
|
||||
Client.blockTypes = BlockTypeCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import com.google.common.base.Joiner
|
||||
import mods.betterfoliage.render.old.BlockCtx
|
||||
import mods.octarinecore.client.resource.AsyncSpriteProvider
|
||||
import mods.octarinecore.client.resource.AtlasFuture
|
||||
import mods.octarinecore.client.resource.StitchPhases
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.config.IBlockMatcher
|
||||
import mods.betterfoliage.config.ModelTextureList
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.findFirst
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.sinkAsync
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.BlockModelShapes
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||
import net.minecraft.resources.IResourceManager
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IBlockReader
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
interface ModelRenderRegistry<T> {
|
||||
operator fun get(ctx: BlockCtx) = get(ctx.state, ctx.world, ctx.pos)
|
||||
operator fun get(ctx: BlockCtx, offset: Int3) = get(ctx.state(offset), ctx.world, ctx.pos + offset)
|
||||
operator fun get(state: BlockState, world: IBlockReader, pos: BlockPos): T?
|
||||
}
|
||||
|
||||
abstract class ModelRenderRegistryRoot<T> : ModelRenderRegistry<T> {
|
||||
val registries = mutableListOf<ModelRenderRegistry<T>>()
|
||||
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = registries.findFirst { it[state, world, pos] }
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about a single BlockState and all the IUnbakedModel it could render as.
|
||||
*/
|
||||
class ModelDiscoveryContext(
|
||||
bakery: ModelBakery,
|
||||
val state: BlockState,
|
||||
val modelId: ModelResourceLocation
|
||||
) {
|
||||
val models = bakery.unwrapVariants(bakery.getUnbakedModel(modelId) to modelId)
|
||||
.filter { it.second != bakery.getUnbakedModel(ModelBakery.MODEL_MISSING) }
|
||||
|
||||
fun ModelBakery.unwrapVariants(modelAndLoc: Pair<IUnbakedModel, ResourceLocation>): List<Pair<IUnbakedModel, ResourceLocation>> = when(val model = modelAndLoc.first) {
|
||||
is VariantList -> model.variantList.flatMap { variant -> unwrapVariants(getUnbakedModel(variant.modelLocation) to variant.modelLocation) }
|
||||
is BlockModel -> listOf(modelAndLoc)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ModelDiscovery<T> : HasLogger, AsyncSpriteProvider<ModelBakery>, ModelRenderRegistry<T> {
|
||||
|
||||
var modelData: Map<BlockState, T> = emptyMap()
|
||||
protected set
|
||||
|
||||
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = modelData[state]
|
||||
|
||||
abstract fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<T>?
|
||||
|
||||
override fun setup(manager: IResourceManager, bakeryF: CompletableFuture<ModelBakery>, atlas: AtlasFuture): StitchPhases {
|
||||
val modelDataTemp = mutableMapOf<BlockState, CompletableFuture<T>>()
|
||||
|
||||
return StitchPhases(
|
||||
discovery = bakeryF.sinkAsync { bakery ->
|
||||
var errors = 0
|
||||
bakery.iterateModels { ctx ->
|
||||
try {
|
||||
processModel(ctx, atlas)?.let { modelDataTemp[ctx.state] = it }
|
||||
} catch (e: Exception) {
|
||||
errors++
|
||||
}
|
||||
}
|
||||
log("${modelDataTemp.size} BlockStates discovered, $errors errors")
|
||||
},
|
||||
cleanup = atlas.runAfter {
|
||||
modelData = modelDataTemp.filterValues { !it.isCompletedExceptionally }.mapValues { it.value.get() }
|
||||
val errors = modelDataTemp.values.filter { it.isCompletedExceptionally }.size
|
||||
log("${modelData.size} BlockStates loaded, $errors errors")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun ModelBakery.iterateModels(func: (ModelDiscoveryContext)->Unit) {
|
||||
ForgeRegistries.BLOCKS.flatMap { block ->
|
||||
block.stateContainer.validStates.map { state -> state to BlockModelShapes.getModelLocation(state) }
|
||||
}.forEach { (state, stateModelResource) ->
|
||||
func(ModelDiscoveryContext(this, state, stateModelResource))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ConfigurableModelDiscovery<T> : ModelDiscovery<T>() {
|
||||
|
||||
abstract val matchClasses: IBlockMatcher
|
||||
abstract val modelTextures: List<ModelTextureList>
|
||||
|
||||
abstract fun processModel(state: BlockState, textures: List<ResourceLocation>, atlas: AtlasFuture): CompletableFuture<T>?
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<T>? {
|
||||
val matchClass = matchClasses.matchingClass(ctx.state.block) ?: return null
|
||||
log("block state ${ctx.state}")
|
||||
log(" class ${ctx.state.block.javaClass.name} matches ${matchClass.name}")
|
||||
|
||||
if (ctx.models.isEmpty()) {
|
||||
log(" no models found")
|
||||
return null
|
||||
}
|
||||
|
||||
ctx.models.filter { it.first is BlockModel }.forEach { (model, location) ->
|
||||
model as BlockModel
|
||||
val modelMatch = modelTextures.firstOrNull { (model to location).derivesFrom(it.modelLocation) }
|
||||
if (modelMatch != null) {
|
||||
log(" model ${model} matches ${modelMatch.modelLocation}")
|
||||
|
||||
val textures = modelMatch.textureNames.map { it to model.resolveTextureName(it).textureLocation }
|
||||
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
|
||||
log(" sprites [$texMapString]")
|
||||
|
||||
if (textures.all { it.second != MissingTextureSprite.getLocation() }) {
|
||||
// found a valid model (all required textures exist)
|
||||
return processModel(ctx.state, textures.map { it.second}, atlas)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun Pair<BlockModel, 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
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
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
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
abstract class ModelReplacer : HasLogger(), ModelDiscovery {
|
||||
override fun onModelsLoaded(
|
||||
bakery: ModelBakery,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakeKey>
|
||||
) {
|
||||
ForgeRegistries.BLOCKS
|
||||
.flatMap { block -> block.stateContainer.validStates }
|
||||
.forEach { state ->
|
||||
val location = BlockModelShapes.getModelLocation(state)
|
||||
try {
|
||||
val hasReplaced = processModel(bakery, state, location, sprites, replacements)
|
||||
} 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, ModelBakeKey>
|
||||
): Boolean {
|
||||
// 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
|
||||
}
|
||||
is Multipart -> model.variants.fold(false) { hasReplaced, variantList ->
|
||||
variantList.variantList.fold(false) { hasReplaced, variant ->
|
||||
processModel(bakery, state, variant.modelLocation, sprites, replacements) || hasReplaced
|
||||
} || hasReplaced
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
abstract class ConfigurableModelReplacer : ModelReplacer() {
|
||||
abstract val matchClasses: IBlockMatcher
|
||||
abstract val modelTextures: List<ModelTextureList>
|
||||
|
||||
abstract fun processModel(
|
||||
state: BlockState,
|
||||
location: ResourceLocation,
|
||||
textureMatch: List<ResourceLocation>,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakeKey>
|
||||
): Boolean
|
||||
|
||||
override fun processModel(
|
||||
bakery: ModelBakery,
|
||||
state: BlockState,
|
||||
location: ResourceLocation,
|
||||
sprites: MutableSet<ResourceLocation>,
|
||||
replacements: MutableMap<ResourceLocation, ModelBakeKey>
|
||||
): Boolean {
|
||||
val model = bakery.getUnbakedModel(location)
|
||||
if (model is BlockModel) {
|
||||
val matchClass = matchClasses.matchingClass(state.block) ?: return false
|
||||
|
||||
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}")
|
||||
|
||||
modelTextures
|
||||
.filter { matcher -> bakery.modelDerivesFrom(model, location, matcher.modelLocation) }
|
||||
.forEach { match ->
|
||||
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}" })
|
||||
detailLogger.log(Level.INFO, " sprites [$texMapString]")
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.processModel(bakery, state, location, sprites, replacements)
|
||||
}
|
||||
}
|
||||
|
||||
fun ModelBakery.modelDerivesFrom(model: BlockModel, location: ResourceLocation, target: ResourceLocation): Boolean =
|
||||
if (location == target) true
|
||||
else model.parentLocation
|
||||
?.let { getUnbakedModel(it) as? BlockModel }
|
||||
?.let { parent -> modelDerivesFrom(parent, model.parentLocation!!, target) }
|
||||
?: false
|
||||
Reference in New Issue
Block a user