rewrite model and texture detection

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

View File

@@ -1,11 +1,9 @@
package mods.octarinecore.client.resource
import mods.betterfoliage.client.config.BlockMatcher
import mods.betterfoliage.loader.Refs
import mods.octarinecore.stripStart
import mods.octarinecore.common.config.BlockMatcher
import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.block.model.ModelBlock
import net.minecraft.client.renderer.block.model.ModelResourceLocation
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
import net.minecraft.client.renderer.block.statemap.IStateMapper
@@ -34,8 +32,8 @@ abstract class ModelDataInspector {
@SubscribeEvent
fun handleLoadModelData(event: LoadModelDataEvent) {
val stateMappings = Block.REGISTRY.flatMap { block ->
((event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper())
.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()
(mapper.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
}
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
@@ -96,31 +94,4 @@ abstract class BlockTextureInspector<T> : ModelDataInspector() {
}
abstract fun processTextures(state: IBlockState, textures: List<TextureAtlasSprite>, atlas: TextureMap): T
}
@Suppress("UNCHECKED_CAST")
val IModel.modelBlockAndLoc: Pair<ModelBlock, ResourceLocation>? get() {
if (Refs.VanillaModelWrapper.isInstance(this))
return Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation)
else if (Refs.WeightedPartWrapper.isInstance(this)) Refs.model_WPW.get(this)?.let {
return (it as IModel).modelBlockAndLoc
}
else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let {
(it as List<IModel>).forEach {
it.modelBlockAndLoc.let { if (it != null) return it }
}
}
else if (Refs.MultiModel.isInstance(this)) Refs.base_MM.get(this)?.let {
return (it as IModel).modelBlockAndLoc
}
return null
}
fun Pair<ModelBlock, ResourceLocation>.derivesFrom(targetLocation: String): Boolean {
if (second.stripStart("models/") == ResourceLocation(targetLocation)) return true
if (first.parent != null && first.parentLocation != null)
return Pair(first.parent, first.parentLocation!!).derivesFrom(targetLocation)
return false
}
fun IModel.derivesFromModel(modelLocation: String) = modelBlockAndLoc?.derivesFrom(modelLocation) ?: false
}

View File

@@ -0,0 +1,104 @@
package mods.octarinecore.client.resource
import com.google.common.base.Joiner
import mods.betterfoliage.loader.Refs
import mods.octarinecore.common.config.BlockMatcher
import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.filterValuesNotNull
import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.block.model.ModelResourceLocation
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
import net.minecraft.client.renderer.block.statemap.IStateMapper
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.client.model.IModel
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
interface ModelProcessor<T1, T2> {
val logger: Logger?
var stateToKey: MutableMap<IBlockState, T1>
var stateToValue: Map<IBlockState, T2>
fun onPostLoad() { }
fun onPreStitch() { }
fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): T1?
fun processStitch(state: IBlockState, key: T1, atlas: TextureMap): T2?
@SubscribeEvent
fun handleLoadModelData(event: LoadModelDataEvent) {
stateToKey.clear()
onPostLoad()
val stateMappings = Block.REGISTRY.flatMap { block ->
val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()
(mapper.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
}
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
stateMappings.forEach { mapping ->
if (mapping.key.block != null) stateModels[mapping.value]?.let { model ->
processModelLoad(mapping.key, mapping.value, model)?.let { key -> stateToKey.put(mapping.key, key) }
}
}
}
@Suppress("UNCHECKED_CAST")
@SubscribeEvent(priority = EventPriority.LOW)
fun handlePreStitch(event: TextureStitchEvent.Pre) {
onPreStitch()
stateToValue = stateToKey.mapValues { processStitch(it.key, it.value, event.map) }.filterValuesNotNull()
}
}
interface TextureListModelProcessor<T2> : ModelProcessor<List<String>, T2> {
val logName: String
val matchClasses: BlockMatcher
val modelTextures: List<ModelTextureList>
override fun processModelLoad(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): List<String>? {
val matchClass = matchClasses.matchingClass(state.block) ?: return null
logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}")
logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}")
val blockLoc = model.modelBlockAndLoc
if (blockLoc == null) {
logger?.log(Level.DEBUG, "$logName: no models found")
return null
}
val modelMatch = modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) }
if (modelMatch == null) {
logger?.log(Level.DEBUG, "$logName: no matching models found")
return null
}
logger?.log(Level.DEBUG, "$logName: model ${blockLoc.second} matches ${modelMatch.modelLocation.toString()}")
val textures = modelMatch.textureNames.map { it to blockLoc.first.resolveTextureName(it) }
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
logger?.log(Level.DEBUG, "$logName: textures [$texMapString]")
return if (textures.all { it.second != "missingno" }) textures.map { it.second } else null
}
}
interface TextureMediatedRegistry<T1, T3> : ModelProcessor<T1, TextureAtlasSprite> {
var textureToValue: MutableMap<TextureAtlasSprite, T3>
@Suppress("UNCHECKED_CAST")
// @SubscribeEvent(priority = EventPriority.LOW)
override fun handlePreStitch(event: TextureStitchEvent.Pre) {
textureToValue.clear()
super.handlePreStitch(event)
val textureToStates = stateToValue.entries.groupBy(keySelector = { it.value }, valueTransform = { it.key })
stateToValue.values.toSet().forEach { processTexture(textureToStates[it]!!, it, event.map) }
}
fun processTexture(states: List<IBlockState>, texture: TextureAtlasSprite, atlas: TextureMap)
}

View File

@@ -1,15 +1,20 @@
@file:JvmName("Utils")
package mods.octarinecore.client.resource
import mods.betterfoliage.loader.Refs
import mods.octarinecore.PI2
import mods.octarinecore.client.render.HSB
import mods.octarinecore.stripStart
import mods.octarinecore.tryDefault
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.block.model.ModelBlock
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.client.resources.IResource
import net.minecraft.client.resources.IResourceManager
import net.minecraft.client.resources.SimpleReloadableResourceManager
import net.minecraft.util.ResourceLocation
import net.minecraftforge.client.model.IModel
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -29,6 +34,9 @@ operator fun IResourceManager.get(domain: String, path: String): IResource? = ge
/** Index operator to get a resource. */
operator fun IResourceManager.get(location: ResourceLocation): IResource? = tryDefault(null) { getResource(location) }
/** Index operator to get a texture sprite. */
operator fun TextureMap.get(name: String): TextureAtlasSprite? = getTextureExtry(name)
/** Load an image resource. */
fun IResource.loadImage() = ImageIO.read(this.inputStream)
@@ -89,4 +97,29 @@ val TextureAtlasSprite.averageColor: Int? get() {
fun textureLocation(iconName: String) = ResourceLocation(iconName).let {
if (it.resourcePath.startsWith("mcpatcher")) it
else ResourceLocation(it.resourceDomain, "textures/${it.resourcePath}")
}
}
@Suppress("UNCHECKED_CAST")
val IModel.modelBlockAndLoc: Pair<ModelBlock, ResourceLocation>? get() {
if (Refs.VanillaModelWrapper.isInstance(this))
return Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation)
else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let {
(it as List<IModel>).forEach {
it.modelBlockAndLoc.let { if (it != null) return it }
}
}
else if (Refs.MultiModel.isInstance(this)) Refs.base_MM.get(this)?.let {
return (it as IModel).modelBlockAndLoc
}
// TODO support net.minecraftforge.client.model.ModelLoader.MultipartModel
return null
}
fun Pair<ModelBlock, ResourceLocation>.derivesFrom(targetLocation: ResourceLocation): Boolean {
if (second.stripStart("models/") == targetLocation) return true
if (first.parent != null && first.parentLocation != null)
return Pair(first.parent, first.parentLocation!!).derivesFrom(targetLocation)
return false
}
fun IModel.derivesFromModel(modelLoc: String) = modelBlockAndLoc?.derivesFrom(ResourceLocation(modelLoc)) ?: false