first Kotlin version
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
import mods.octarinecore.client.resource.*
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
/**
|
||||
* Generate Short Grass textures from [Blocks.tallgrass] block textures.
|
||||
* The bottom 3/8 of the base texture is chopped off.
|
||||
*
|
||||
* @param[domain] Resource domain of generator
|
||||
*/
|
||||
class GrassGenerator(domain: String) : TextureGenerator(domain) {
|
||||
|
||||
override fun generate(params: ParameterList): BufferedImage? {
|
||||
val target = targetResource(params)!!
|
||||
val isSnowed = params["snowed"]?.toBoolean() ?: false
|
||||
|
||||
val baseTexture = resourceManager[target.second]?.loadImage() ?: return null
|
||||
|
||||
// draw bottom half of texture
|
||||
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
val graphics = result.createGraphics()
|
||||
graphics.drawImage(baseTexture, 0, 3 * baseTexture.height / 8, null)
|
||||
|
||||
// blend with white if snowed
|
||||
if (isSnowed && target.first == ResourceType.COLOR) {
|
||||
for (x in 0..result.width - 1) for (y in 0..result.height - 1) {
|
||||
result[x, y] = blendRGB(result[x, y], 16777215, 2, 3)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent
|
||||
import cpw.mods.fml.relauncher.Side
|
||||
import cpw.mods.fml.relauncher.SideOnly
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.octarinecore.client.render.HSB
|
||||
import mods.octarinecore.client.resource.averageColor
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.client.renderer.texture.TextureMap
|
||||
import net.minecraft.util.IIcon
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import org.apache.logging.log4j.Level.DEBUG
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
|
||||
const val defaultGrassColor = 0
|
||||
|
||||
/** Rendering-related information for a grass block. */
|
||||
class GrassInfo(
|
||||
/** Top texture of the grass block. */
|
||||
val grassTopTexture: TextureAtlasSprite,
|
||||
|
||||
/**
|
||||
* Color to use for Short Grass rendering instead of the biome color.
|
||||
*
|
||||
* Value is null if the texture is mostly grey (the saturation of its average color is under a configurable limit),
|
||||
* the average color of the texture (significantly brightened) otherwise.
|
||||
*/
|
||||
val overrideColor: Int?
|
||||
)
|
||||
|
||||
/** Collects and manages rendering-related information for grass blocks. */
|
||||
@SideOnly(Side.CLIENT)
|
||||
object GrassRegistry {
|
||||
|
||||
val grass: MutableMap<IIcon, GrassInfo> = hashMapOf()
|
||||
|
||||
init {
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleTextureReload(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureType != 0) return
|
||||
grass.clear()
|
||||
Client.log(INFO, "Inspecting grass textures")
|
||||
|
||||
Block.blockRegistry.forEach { block ->
|
||||
if (Config.blocks.grass.matchesClass(block as Block)) {
|
||||
block.registerBlockIcons { location ->
|
||||
val original = event.map.getTextureExtry(location)
|
||||
Client.log(DEBUG, "Found grass texture: $location")
|
||||
registerGrass(event.map, original)
|
||||
return@registerBlockIcons original
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun registerGrass(atlas: TextureMap, icon: TextureAtlasSprite) {
|
||||
val hsb = HSB.fromColor(icon.averageColor ?: defaultGrassColor)
|
||||
val overrideColor = if (hsb.saturation > Config.shortGrass.saturationThreshold) hsb.copy(brightness = 0.8f).asColor else null
|
||||
grass.put(icon, GrassInfo(icon, overrideColor))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.stripStart
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
/**
|
||||
* Generate round leaf textures from leaf block textures.
|
||||
* The base texture is tiled 2x2, then parts of it are made transparent by applying a mask to the alpha channel.
|
||||
*
|
||||
* Generator parameter _type_: Leaf type (configurable by user). Different leaf types may have their own alpha mask.
|
||||
*
|
||||
* @param[domain] Resource domain of generator
|
||||
*/
|
||||
class LeafGenerator(domain: String) : TextureGenerator(domain) {
|
||||
|
||||
override fun generate(params: ParameterList): BufferedImage? {
|
||||
val target = targetResource(params)!!
|
||||
val leafType = params["type"] ?: "default"
|
||||
|
||||
val handDrawnLoc = target.second.stripStart("textures/").stripStart("blocks/").let {
|
||||
ResourceLocation(BetterFoliageMod.DOMAIN, "textures/blocks/${it.resourceDomain}/${it.resourcePath}")
|
||||
}
|
||||
resourceManager[handDrawnLoc]?.loadImage()?.let { return it }
|
||||
|
||||
val baseTexture = resourceManager[target.second]?.loadImage() ?: return null
|
||||
val size = baseTexture.width
|
||||
val frames = baseTexture.height / size
|
||||
|
||||
val maskTexture = (getLeafMask(leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage()
|
||||
fun scale(i: Int) = i * maskTexture!!.width / (size * 2)
|
||||
|
||||
val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
val graphics = leafTexture.createGraphics()
|
||||
|
||||
// iterate all frames
|
||||
for (frame in 0 .. frames - 1) {
|
||||
val baseFrame = baseTexture.getSubimage(0, size * frame, size, size)
|
||||
val leafFrame = BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
|
||||
// tile leaf texture 2x2
|
||||
leafFrame.createGraphics().apply {
|
||||
drawImage(baseFrame, 0, 0, null)
|
||||
drawImage(baseFrame, 0, size, null)
|
||||
drawImage(baseFrame, size, 0, null)
|
||||
drawImage(baseFrame, size, size, null)
|
||||
}
|
||||
|
||||
// overlay alpha mask
|
||||
if (target.first == ResourceType.COLOR && maskTexture != null) {
|
||||
for (x in 0 .. size * 2 - 1) for (y in 0 .. size * 2 - 1) {
|
||||
val basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL
|
||||
val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL
|
||||
leafFrame[x, y] = (basePixel and maskPixel).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
// add to animated png
|
||||
graphics.drawImage(leafFrame, 0, size * frame * 2, null)
|
||||
}
|
||||
|
||||
return leafTexture
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alpha mask to use
|
||||
*
|
||||
* @param[type] Alpha mask type.
|
||||
* @param[maxSize] Preferred mask size.
|
||||
*/
|
||||
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size ->
|
||||
ResourceLocation(BetterFoliageMod.DOMAIN, "textures/blocks/leafmask_${size}_${type}.png")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent
|
||||
import cpw.mods.fml.relauncher.Side
|
||||
import cpw.mods.fml.relauncher.SideOnly
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.octarinecore.client.resource.IconSet
|
||||
import mods.octarinecore.client.resource.averageColor
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.client.renderer.texture.TextureMap
|
||||
import net.minecraft.util.IIcon
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import org.apache.logging.log4j.Level.*
|
||||
|
||||
const val defaultLeafColor = 0
|
||||
|
||||
/** Rendering-related information for a leaf block. */
|
||||
class LeafInfo(
|
||||
/** The generated round leaf texture. */
|
||||
val roundLeafTexture: TextureAtlasSprite,
|
||||
|
||||
/** Type of the leaf block (configurable by user). */
|
||||
val leafType: String,
|
||||
|
||||
/** Average color of the round leaf texture. */
|
||||
val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor
|
||||
) {
|
||||
/** [IconSet] of the textures to use for leaf particles emitted from this block. */
|
||||
val particleTextures: IconSet get() = LeafRegistry.particles[leafType]!!
|
||||
}
|
||||
|
||||
/** Collects and manages rendering-related information for leaf blocks. */
|
||||
@SideOnly(Side.CLIENT)
|
||||
object LeafRegistry {
|
||||
|
||||
val leaves: MutableMap<IIcon, LeafInfo> = hashMapOf()
|
||||
val particles: MutableMap<String, IconSet> = hashMapOf()
|
||||
val typeMappings = TextureMatcher()
|
||||
|
||||
init {
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleTextureReload(event: TextureStitchEvent.Pre) {
|
||||
if (event.map.textureType != 0) return
|
||||
leaves.clear()
|
||||
particles.clear()
|
||||
typeMappings.loadMappings(ResourceLocation("betterfoliage", "leafTypeMappings.cfg"))
|
||||
Client.log(INFO, "Generating leaf textures")
|
||||
|
||||
IconSet("betterfoliage", "falling_leaf_default_%d").let {
|
||||
it.onStitch(event.map)
|
||||
particles.put("default", it)
|
||||
}
|
||||
|
||||
Block.blockRegistry.forEach { block ->
|
||||
if (Config.blocks.leaves.matchesClass(block as Block)) {
|
||||
block.registerBlockIcons { location ->
|
||||
val original = event.map.getTextureExtry(location)
|
||||
Client.log(DEBUG, "Found leaf texture: $location")
|
||||
registerLeaf(event.map, original)
|
||||
return@registerBlockIcons original
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun registerLeaf(atlas: TextureMap, icon: TextureAtlasSprite) {
|
||||
var leafType = typeMappings.getType(icon) ?: "default"
|
||||
val generated = atlas.registerIcon(
|
||||
Client.genLeaves.generatedResource(icon.iconName, "type" to leafType).toString()
|
||||
)
|
||||
|
||||
if (leafType !in particles.keys) {
|
||||
val particleSet = IconSet("betterfoliage", "falling_leaf_${leafType}_%d")
|
||||
particleSet.onStitch(atlas)
|
||||
if (particleSet.num == 0) {
|
||||
Client.log(WARN, "Leaf particle textures not found for leaf type: $leafType")
|
||||
leafType == "default"
|
||||
} else {
|
||||
particles.put(leafType, particleSet)
|
||||
}
|
||||
}
|
||||
|
||||
val leafInfo = LeafInfo(generated as TextureAtlasSprite, leafType)
|
||||
leaves.put(icon, leafInfo)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
import mods.octarinecore.client.resource.resourceManager
|
||||
import mods.octarinecore.client.resource.get
|
||||
import mods.octarinecore.client.resource.getLines
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.ResourceLocation
|
||||
|
||||
class TextureMatcher() {
|
||||
|
||||
data class Mapping(val domain: String?, val path: String, val type: String) {
|
||||
fun matches(icon: TextureAtlasSprite): Boolean {
|
||||
val iconLocation = ResourceLocation(icon.iconName)
|
||||
return (domain == null || domain == iconLocation.resourceDomain) && iconLocation.resourcePath.contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
val mappings: MutableList<Mapping> = linkedListOf()
|
||||
|
||||
fun getType(icon: TextureAtlasSprite): String? = mappings.filter { it.matches(icon) }.map { it.type }.firstOrNull()
|
||||
|
||||
fun loadMappings(mappingLocation: ResourceLocation) {
|
||||
mappings.clear()
|
||||
resourceManager[mappingLocation]?.getLines()?.let { lines ->
|
||||
lines.filter { !it.startsWith("//") }.filter { !it.isEmpty() }.forEach { line ->
|
||||
val line2 = line.trim().split('=')
|
||||
if (line2.size == 2) {
|
||||
val mapping = line2[0].trim().split(':')
|
||||
if (mapping.size == 1) mappings.add(Mapping(null, mapping[0].trim(), line2[1].trim()))
|
||||
else if (mapping.size == 2) mappings.add(Mapping(mapping[1].trim(), mapping[0].trim(), line2[1].trim()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt
Normal file
11
src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
@file:JvmName("Utils")
|
||||
package mods.betterfoliage.client.texture
|
||||
|
||||
fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
||||
val r = (((rgb1 shr 16) and 255) * weight1 + ((rgb2 shr 16) and 255) * weight2) / (weight1 + weight2)
|
||||
val g = (((rgb1 shr 8) and 255) * weight1 + ((rgb2 shr 8) and 255) * weight2) / (weight1 + weight2)
|
||||
val b = ((rgb1 and 255) * weight1 + (rgb2 and 255) * weight2) / (weight1 + weight2)
|
||||
val a = (rgb1 shr 24) and 255
|
||||
val result = ((a shl 24) or (r shl 16) or (g shl 8) or b).toInt()
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user