[WIP] major rewrite, grass and leaves working already
This commit is contained in:
9
src/main/kotlin/mods/betterfoliage/util/Blocks.kt
Normal file
9
src/main/kotlin/mods/betterfoliage/util/Blocks.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.material.Material
|
||||
|
||||
val BlockState.isSnow: Boolean get() = material.let { it == Material.SNOW }
|
||||
|
||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT)
|
||||
@@ -17,6 +17,10 @@ interface Invalidator {
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleInvalidator : Invalidator {
|
||||
override val callbacks = mutableListOf<WeakReference<() -> Unit>>()
|
||||
}
|
||||
|
||||
class LazyInvalidatable<V>(invalidator: Invalidator, val valueFactory: ()->V): ReadOnlyProperty<Any, V> {
|
||||
init { invalidator.onInvalidate { value = null } }
|
||||
|
||||
@@ -31,7 +35,7 @@ class LazyInvalidatable<V>(invalidator: Invalidator, val valueFactory: ()->V): R
|
||||
}
|
||||
}
|
||||
|
||||
class LazyMap<K, V>(val invalidator: Invalidator, val valueFactory: (K)->V) {
|
||||
open class LazyMapInvalidatable<K, V>(val invalidator: Invalidator, val valueFactory: (K)->V) {
|
||||
init { invalidator.onInvalidate { values.clear() } }
|
||||
|
||||
val values = mutableMapOf<K, V>()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,8 @@ inline fun <T, C: Comparable<C>> Triple<T, T, T>.maxValueBy(func: (T)->C): C {
|
||||
return result
|
||||
}
|
||||
|
||||
inline fun <reified T, reified R> Array<T>.mapArray(func: (T)->R) = Array<R>(size) { idx -> func(get(idx)) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <K, V> Map<K, V?>.filterValuesNotNull() = filterValues { it != null } as Map<K, V>
|
||||
|
||||
@@ -61,3 +64,8 @@ inline fun <T> MutableList<T>.exchange(idx1: Int, idx2: Int) {
|
||||
|
||||
/** Return a random element from the array using the provided random generator */
|
||||
inline operator fun <T> Array<T>.get(random: Random) = get(random.nextInt(Int.MAX_VALUE) % size)
|
||||
|
||||
fun <T> Iterable<T>.toImmutableList() = ImmutableList.builder<T>().let { builder ->
|
||||
forEach { builder.add(it) }
|
||||
builder.build()
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import net.minecraft.util.Direction.AxisDirection.NEGATIVE
|
||||
import net.minecraft.util.Direction.AxisDirection.POSITIVE
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
val EPSILON = 0.05
|
||||
|
||||
// ================================
|
||||
// Axes and directions
|
||||
// ================================
|
||||
@@ -22,8 +24,19 @@ val Pair<Axis, AxisDirection>.face: Direction get() = when(this) {
|
||||
Y to POSITIVE -> UP; Y to NEGATIVE -> DOWN;
|
||||
Z to POSITIVE -> SOUTH; else -> NORTH;
|
||||
}
|
||||
val Direction.perpendiculars: List<Direction> get() =
|
||||
axes.filter { it != this.axis }.cross(axisDirs).map { it.face }
|
||||
val directionsAndNull = arrayOf(DOWN, UP, NORTH, SOUTH, WEST, EAST, null)
|
||||
|
||||
val Direction.perpendiculars: Array<Direction> get() =
|
||||
axes.filter { it != this.axis }.flatMap { listOf((it to POSITIVE).face, (it to NEGATIVE).face) }.toTypedArray()
|
||||
|
||||
val perpendiculars: Array<Array<Direction>> = Direction.values().map { dir ->
|
||||
axes.filter { it != dir.axis }
|
||||
.flatMap { listOf(
|
||||
(it to POSITIVE).face,
|
||||
(it to NEGATIVE).face
|
||||
) }.toTypedArray()
|
||||
}.toTypedArray()
|
||||
|
||||
val Direction.offset: Int3 get() = allDirOffsets[ordinal]
|
||||
|
||||
/** Old ForgeDirection rotation matrix yanked from 1.7.10 */
|
||||
@@ -172,6 +185,14 @@ class Rotation(val forward: Array<Direction>, val reverse: Array<Direction>) {
|
||||
// Forge rotation matrix is left-hand
|
||||
val rot90 = Array(6) { idx -> Rotation(allDirections[idx].opposite.rotations, allDirections[idx].rotations) }
|
||||
val identity = Rotation(allDirections, allDirections)
|
||||
val fromUp = arrayOf(
|
||||
rot90[EAST.ordinal] * 2,
|
||||
identity,
|
||||
rot90[WEST.ordinal],
|
||||
rot90[EAST.ordinal],
|
||||
rot90[SOUTH.ordinal],
|
||||
rot90[NORTH.ordinal]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
@@ -49,6 +50,11 @@ fun nextPowerOf2(x: Int): Int {
|
||||
return 1 shl (if (x == 0) 0 else 32 - Integer.numberOfLeadingZeros(x - 1))
|
||||
}
|
||||
|
||||
abstract class HasLogger {
|
||||
val logger = BetterFoliageMod.logger(this)
|
||||
val detailLogger = BetterFoliageMod.detailLogger(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Chunk containing the given [BlockPos] is loaded.
|
||||
* Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances.
|
||||
@@ -60,15 +66,6 @@ fun nextPowerOf2(x: Int): Int {
|
||||
// else -> false
|
||||
//}
|
||||
|
||||
interface HasLogger {
|
||||
val logger: Logger
|
||||
val logName: String get() = this::class.simpleName!!
|
||||
fun log(msg: String) = log(Level.INFO, msg)
|
||||
fun log(level: Level, msg: String) = logger.log(level, "[$logName] $msg")
|
||||
fun log(msg: String, e: Throwable) = log(Level.WARN, msg, e)
|
||||
fun log(level: Level, msg: String, e: Throwable) = logger.log(level, "[$logName] $msg", e)
|
||||
}
|
||||
|
||||
//fun textComponent(msg: String, color: Formatting = Formatting.GRAY): LiteralText {
|
||||
// val style = Style().apply { this.color = color }
|
||||
// return LiteralText(msg).apply { this.style = style }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.resources.IReloadableResourceManager
|
||||
import net.minecraft.resources.IResource
|
||||
import net.minecraft.resources.IResourceManager
|
||||
@@ -13,6 +15,15 @@ val resourceManager: IReloadableResourceManager
|
||||
/** Append a string to the [ResourceLocation]'s path. */
|
||||
operator fun ResourceLocation.plus(str: String) = ResourceLocation(namespace, path + str)
|
||||
|
||||
/** Prepend a string to the [ResourceLocation]'s path. */
|
||||
fun ResourceLocation.prependLocation(basePath: String) =
|
||||
ResourceLocation(namespace, basePath.stripEnd("/").let { "$it/$path" })
|
||||
|
||||
val ResourceLocation.asBlockMaterial: Material get() = Material(
|
||||
AtlasTexture.LOCATION_BLOCKS_TEXTURE,
|
||||
this
|
||||
)
|
||||
|
||||
/** Index operator to get a resource. */
|
||||
operator fun IResourceManager.get(domain: String, path: String): IResource? = get(ResourceLocation(domain, path))
|
||||
/** Index operator to get a resource. */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package mods.betterfoliage.util
|
||||
|
||||
import mods.betterfoliage.render.lighting.HSB
|
||||
import mods.betterfoliage.render.old.HSB
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.Material
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.resources.IResource
|
||||
@@ -15,29 +16,33 @@ import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
enum class Atlas(val basePath: String, val resourceId: ResourceLocation) {
|
||||
BLOCKS("textures", AtlasTexture.LOCATION_BLOCKS_TEXTURE),
|
||||
PARTICLES("textures", AtlasTexture.LOCATION_PARTICLES_TEXTURE);
|
||||
enum class Atlas(val resourceId: ResourceLocation) {
|
||||
BLOCKS(AtlasTexture.LOCATION_BLOCKS_TEXTURE),
|
||||
PARTICLES(AtlasTexture.LOCATION_PARTICLES_TEXTURE);
|
||||
|
||||
/** Get the fully-qualified resource name for sprites belonging to this atlas*/
|
||||
fun wrap(resource: ResourceLocation) = ResourceLocation(resource.namespace, "$basePath/${resource.path}.png")
|
||||
|
||||
/** Get the short resource name for sprites belonging to this atlas*/
|
||||
fun unwrap(resource: ResourceLocation) = resource.stripStart("$basePath/").stripEnd(".png")
|
||||
/** Get the fully-qualified resource name for sprites belonging to this atlas */
|
||||
fun file(resource: ResourceLocation) = ResourceLocation(resource.namespace, "textures/${resource.path}.png")
|
||||
|
||||
/** Reference to the atlas itself */
|
||||
val atlas: AtlasTexture get() = Minecraft.getInstance().textureManager.getTexture(resourceId) as AtlasTexture
|
||||
private val atlas: AtlasTexture get() = Minecraft.getInstance().textureManager.getTexture(resourceId) as AtlasTexture
|
||||
|
||||
/** Get a sprite from this atlas */
|
||||
operator fun get(location: ResourceLocation) = atlas.getSprite(location)
|
||||
}
|
||||
|
||||
val Material.atlas: Atlas get() = Atlas.values().find { it.resourceId == atlasLocation } ?: Atlas.BLOCKS
|
||||
|
||||
inline operator fun AtlasTexture.get(res: ResourceLocation): TextureAtlasSprite? = this.getSprite(res)
|
||||
inline operator fun AtlasTexture.get(name: String): TextureAtlasSprite? = get(ResourceLocation(name))
|
||||
|
||||
fun IResourceManager.loadSprite(id: ResourceLocation) = this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id")
|
||||
fun IResourceManager.loadSprite(id: ResourceLocation) =
|
||||
this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id")
|
||||
|
||||
fun IResource.loadImage(): BufferedImage? = ImageIO.read(this.inputStream)
|
||||
|
||||
/** Index operator to get the RGB value of a pixel. */
|
||||
operator fun BufferedImage.get(x: Int, y: Int) = this.getRGB(x, y)
|
||||
|
||||
/** Index operator to set the RGB value of a pixel. */
|
||||
operator fun BufferedImage.set(x: Int, y: Int, value: Int) = this.setRGB(x, y, value)
|
||||
|
||||
@@ -50,30 +55,31 @@ val BufferedImage.bytes: ByteArray get() =
|
||||
* Only non-transparent pixels are considered. Averages are taken in the HSB color space (note: Hue is a circular average),
|
||||
* and the result transformed back to the RGB color space.
|
||||
*/
|
||||
val TextureAtlasSprite.averageColor: Int get() {
|
||||
var numOpaque = 0
|
||||
var sumHueX = 0.0
|
||||
var sumHueY = 0.0
|
||||
var sumSaturation = 0.0f
|
||||
var sumBrightness = 0.0f
|
||||
for (x in 0 until width)
|
||||
for (y in 0 until height) {
|
||||
val pixel = getPixelRGBA(0, x, y)
|
||||
val alpha = (pixel shr 24) and 255
|
||||
val hsb = HSB.fromColor(pixel)
|
||||
if (alpha == 255) {
|
||||
numOpaque++
|
||||
sumHueX += cos((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumHueY += sin((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumSaturation += hsb.saturation
|
||||
sumBrightness += hsb.brightness
|
||||
val TextureAtlasSprite.averageColor: HSB
|
||||
get() {
|
||||
var numOpaque = 0
|
||||
var sumHueX = 0.0
|
||||
var sumHueY = 0.0
|
||||
var sumSaturation = 0.0f
|
||||
var sumBrightness = 0.0f
|
||||
for (x in 0 until width)
|
||||
for (y in 0 until height) {
|
||||
val pixel = getPixelRGBA(0, x, y)
|
||||
val alpha = (pixel shr 24) and 255
|
||||
val hsb = HSB.fromColor(pixel)
|
||||
if (alpha == 255) {
|
||||
numOpaque++
|
||||
sumHueX += cos((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumHueY += sin((hsb.hue.toDouble() - 0.5) * PI2)
|
||||
sumSaturation += hsb.saturation
|
||||
sumBrightness += hsb.brightness
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// circular average - transform sum vector to polar angle
|
||||
val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat()
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat()).asColor
|
||||
}
|
||||
// circular average - transform sum vector to polar angle
|
||||
val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat()
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat())
|
||||
}
|
||||
|
||||
/** Weighted blend of 2 packed RGB colors */
|
||||
fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
||||
|
||||
Reference in New Issue
Block a user