[WIP] major rewrite, grass and leaves working already

This commit is contained in:
octarine-noise
2021-05-06 22:40:32 +02:00
parent 09ccb83e8b
commit f44d2a7a50
96 changed files with 2348 additions and 3531 deletions

View 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)

View File

@@ -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>()

View File

@@ -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()
}

View File

@@ -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]
)
}
}

View File

@@ -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 }

View File

@@ -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. */

View File

@@ -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 {