[WIP] start 1.15 port
reorganize packages to match Fabric version use util classes from Fabric version
This commit is contained in:
158
src/main/kotlin/mods/betterfoliage/render/lighting/Lighting.kt
Normal file
158
src/main/kotlin/mods/betterfoliage/render/lighting/Lighting.kt
Normal file
@@ -0,0 +1,158 @@
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
import mods.betterfoliage.render.old.Quad
|
||||
import mods.betterfoliage.render.old.Vertex
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.axes
|
||||
import mods.betterfoliage.util.boxEdges
|
||||
import mods.betterfoliage.util.boxFaces
|
||||
import mods.betterfoliage.util.face
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.nearestAngle
|
||||
import mods.betterfoliage.util.nearestPosition
|
||||
import mods.betterfoliage.util.perpendiculars
|
||||
import mods.betterfoliage.util.vec
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.Direction.*
|
||||
import java.lang.Math.min
|
||||
|
||||
typealias EdgeShaderFactory = (Direction, Direction) -> ModelLighter
|
||||
typealias CornerShaderFactory = (Direction, Direction, Direction) -> ModelLighter
|
||||
typealias ShaderFactory = (Quad, Vertex) -> ModelLighter
|
||||
|
||||
/** Holds lighting values for block corners as calculated by vanilla Minecraft rendering. */
|
||||
class CornerLightData {
|
||||
var valid = false
|
||||
var brightness = 0
|
||||
var red: Float = 0.0f
|
||||
var green: Float = 0.0f
|
||||
var blue: Float = 0.0f
|
||||
|
||||
fun reset() { valid = false }
|
||||
|
||||
fun set(brightness: Int, red: Float, green: Float, blue: Float) {
|
||||
if (valid) return
|
||||
this.valid = true
|
||||
this.brightness = brightness
|
||||
this.red = red
|
||||
this.green = green
|
||||
this.blue = blue
|
||||
}
|
||||
|
||||
fun set(brightness: Int, colorMultiplier: Float) {
|
||||
this.valid = true
|
||||
this.brightness = brightness
|
||||
this.red = colorMultiplier
|
||||
this.green = colorMultiplier
|
||||
this.blue = colorMultiplier
|
||||
}
|
||||
|
||||
companion object {
|
||||
val black: CornerLightData get() = CornerLightData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this interface are associated with [Model] vertices, and used to apply brightness and color
|
||||
* values to a [RenderVertex].
|
||||
*/
|
||||
interface ModelLighter {
|
||||
/**
|
||||
* Set shading values of a [RenderVertex]
|
||||
*
|
||||
* @param[context] context that can be queried for lighting data in a [Model]-relative frame of reference
|
||||
* @param[vertex] the [RenderVertex] to manipulate
|
||||
*/
|
||||
fun shade(context: LightingCtx, vertex: RenderVertex)
|
||||
|
||||
/**
|
||||
* Return a new rotated version of this [ModelLighter]. Used during [Model] setup when rotating the model itself.
|
||||
*/
|
||||
fun rotate(rot: Rotation): ModelLighter
|
||||
|
||||
/** Set all lighting values on the [RenderVertex] to match the given [CornerLightData]. */
|
||||
fun RenderVertex.shade(shading: CornerLightData) {
|
||||
brightness = shading.brightness; red = shading.red; green = shading.green; blue = shading.blue
|
||||
}
|
||||
|
||||
/** Set the lighting values on the [RenderVertex] to a weighted average of the two [CornerLightData] instances. */
|
||||
fun RenderVertex.shade(shading1: CornerLightData, shading2: CornerLightData, weight1: Float = 0.5f, weight2: Float = 0.5f) {
|
||||
red = min(shading1.red * weight1 + shading2.red * weight2, 1.0f)
|
||||
green = min(shading1.green * weight1 + shading2.green * weight2, 1.0f)
|
||||
blue = min(shading1.blue * weight1 + shading2.blue * weight2, 1.0f)
|
||||
brightness = brWeighted(shading1.brightness, weight1, shading2.brightness, weight2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lighting values on the [RenderVertex] directly.
|
||||
*
|
||||
* @param[brightness] packed brightness value
|
||||
* @param[color] packed color value
|
||||
*/
|
||||
fun RenderVertex.shade(brightness: Int, color: Int) {
|
||||
this.brightness = brightness; setColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [ModelLighter] resolver for quads that point towards one of the 6 block faces.
|
||||
* The resolver works the following way:
|
||||
* - determines which face the _quad_ normal points towards (if not overridden)
|
||||
* - determines the distance of the _vertex_ to the corners and edge midpoints on that block face
|
||||
* - if _corner_ is given, and the _vertex_ is closest to a block corner, returns the [ModelLighter] created by _corner_
|
||||
* - if _edge_ is given, and the _vertex_ is closest to an edge midpoint, returns the [ModelLighter] created by _edge_
|
||||
*
|
||||
* @param[overrideFace] assume the given face instead of going by the _quad_ normal
|
||||
* @param[corner] [ModelLighter] instantiation lambda for corner vertices
|
||||
* @param[edge] [ModelLighter] instantiation lambda for edge midpoint vertices
|
||||
*/
|
||||
fun faceOrientedAuto(overrideFace: Direction? = null,
|
||||
corner: CornerShaderFactory? = null,
|
||||
edge: EdgeShaderFactory? = null) =
|
||||
fun(quad: Quad, vertex: Vertex): ModelLighter {
|
||||
val quadFace = overrideFace ?: quad.normal.nearestCardinal
|
||||
val nearestCorner = nearestPosition(vertex.xyz, boxFaces[quadFace].allCorners) {
|
||||
(quadFace.vec + it.first.vec + it.second.vec) * 0.5
|
||||
}
|
||||
val nearestEdge = nearestPosition(vertex.xyz, quadFace.perpendiculars) {
|
||||
(quadFace.vec + it.vec) * 0.5
|
||||
}
|
||||
if (edge != null && (nearestEdge.second < nearestCorner.second || corner == null))
|
||||
return edge(quadFace, nearestEdge.first)
|
||||
else return corner!!(quadFace, nearestCorner.first.first, nearestCorner.first.second)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModelLighter resolver for quads that point towards one of the 12 block edges.
|
||||
* The resolver works the following way:
|
||||
* - determines which edge the _quad_ normal points towards (if not overridden)
|
||||
* - determines which face midpoint the _vertex_ is closest to, of the 2 block faces that share this edge
|
||||
* - determines which block corner _of this face_ the _vertex_ is closest to
|
||||
* - returns the [ModelLighter] created by _corner_
|
||||
*
|
||||
* @param[overrideEdge] assume the given edge instead of going by the _quad_ normal
|
||||
* @param[corner] ModelLighter instantiation lambda
|
||||
*/
|
||||
fun edgeOrientedAuto(overrideEdge: Pair<Direction, Direction>? = null,
|
||||
corner: CornerShaderFactory
|
||||
) =
|
||||
fun(quad: Quad, vertex: Vertex): ModelLighter {
|
||||
val edgeDir = overrideEdge ?: nearestAngle(quad.normal, boxEdges) { it.first.vec + it.second.vec }.first
|
||||
val nearestFace = nearestPosition(vertex.xyz, edgeDir.toList()) { it.vec }.first
|
||||
val nearestCorner = nearestPosition(vertex.xyz, boxFaces[nearestFace].allCorners) {
|
||||
(nearestFace.vec + it.first.vec + it.second.vec) * 0.5
|
||||
}.first
|
||||
return corner(nearestFace, nearestCorner.first, nearestCorner.second)
|
||||
}
|
||||
|
||||
fun faceOrientedInterpolate(overrideFace: Direction? = null) =
|
||||
fun(quad: Quad, vertex: Vertex): ModelLighter {
|
||||
val resolver = faceOrientedAuto(overrideFace, edge = { face, edgeDir ->
|
||||
val axis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
||||
val vec = Double3((axis to AxisDirection.POSITIVE).face)
|
||||
val pos = vertex.xyz.dot(vec)
|
||||
EdgeInterpolateFallback(face, edgeDir, pos)
|
||||
})
|
||||
return resolver(quad, vertex)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
import mods.betterfoliage.render.old.BlockCtx
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.boxFaces
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.plus
|
||||
import mods.betterfoliage.util.rotate
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.BlockModelRenderer
|
||||
import net.minecraft.client.renderer.WorldRenderer
|
||||
import net.minecraft.util.Direction
|
||||
import java.util.*
|
||||
|
||||
val Direction.aoMultiplier: Float get() = when(this) {
|
||||
Direction.UP -> 1.0f
|
||||
Direction.DOWN -> 0.5f
|
||||
Direction.NORTH, Direction.SOUTH -> 0.8f
|
||||
Direction.EAST, Direction.WEST -> 0.6f
|
||||
}
|
||||
|
||||
interface LightingCtx {
|
||||
val modelRotation: Rotation
|
||||
val blockContext: BlockCtx
|
||||
val aoEnabled: Boolean
|
||||
|
||||
val brightness get() = brightness(Int3.zero)
|
||||
val color get() = color(Int3.zero)
|
||||
fun brightness(face: Direction) = brightness(face.offset)
|
||||
fun color(face: Direction) = color(face.offset)
|
||||
|
||||
fun brightness(offset: Int3) = offset.rotate(modelRotation).let {
|
||||
WorldRenderer.getCombinedLight(blockContext.world, blockContext.pos + it)
|
||||
}
|
||||
fun color(offset: Int3) = blockContext.offset(offset.rotate(modelRotation)).let { Minecraft.getInstance().blockColors.getColor(it.state, it.world, it.pos, 0) }
|
||||
|
||||
fun lighting(face: Direction, corner1: Direction, corner2: Direction): CornerLightData
|
||||
}
|
||||
|
||||
class DefaultLightingCtx(blockContext: BlockCtx) : LightingCtx {
|
||||
override var modelRotation = Rotation.identity
|
||||
|
||||
override var aoEnabled = false
|
||||
protected set
|
||||
override var blockContext: BlockCtx = blockContext
|
||||
protected set
|
||||
override var brightness = brightness(Int3.zero)
|
||||
protected set
|
||||
override var color = color(Int3.zero)
|
||||
protected set
|
||||
|
||||
override fun brightness(face: Direction) = brightness(face.offset)
|
||||
override fun color(face: Direction) = color(face.offset)
|
||||
|
||||
// smooth lighting stuff
|
||||
val lightingData = Array(6) { FaceLightData(allDirections[it]) }
|
||||
override fun lighting(face: Direction, corner1: Direction, corner2: Direction): CornerLightData = lightingData[face.rotate(modelRotation)].let { faceData ->
|
||||
if (!faceData.isValid) faceData.update(blockContext, faceData.face.aoMultiplier)
|
||||
return faceData[corner1.rotate(modelRotation), corner2.rotate(modelRotation)]
|
||||
}
|
||||
|
||||
fun reset(blockContext: BlockCtx) {
|
||||
this.blockContext = blockContext
|
||||
brightness = brightness(Int3.zero)
|
||||
color = color(Int3.zero)
|
||||
modelRotation = Rotation.identity
|
||||
lightingData.forEach { it.isValid = false }
|
||||
aoEnabled = Minecraft.isAmbientOcclusionEnabled()
|
||||
// allDirections.forEach { lightingData[it].update(blockContext, it.aoMultiplier) }
|
||||
}
|
||||
}
|
||||
|
||||
private val vanillaAOFactory = BlockModelRenderer.AmbientOcclusionFace::class.java.let {
|
||||
it.getDeclaredConstructor(BlockModelRenderer::class.java).apply { isAccessible = true }
|
||||
}.let { ctor -> { ctor.newInstance(Minecraft.getInstance().blockRendererDispatcher.blockModelRenderer) } }
|
||||
|
||||
class FaceLightData(val face: Direction) {
|
||||
val topDir = boxFaces[face].top
|
||||
val leftDir = boxFaces[face].left
|
||||
|
||||
val topLeft = CornerLightData()
|
||||
val topRight = CornerLightData()
|
||||
val bottomLeft = CornerLightData()
|
||||
val bottomRight = CornerLightData()
|
||||
|
||||
val vanillaOrdered = when(face) {
|
||||
Direction.DOWN -> listOf(topLeft, bottomLeft, bottomRight, topRight)
|
||||
Direction.UP -> listOf(bottomRight, topRight, topLeft, bottomLeft)
|
||||
Direction.NORTH -> listOf(bottomLeft, bottomRight, topRight, topLeft)
|
||||
Direction.SOUTH -> listOf(topLeft, bottomLeft, bottomRight, topRight)
|
||||
Direction.WEST -> listOf(bottomLeft, bottomRight, topRight, topLeft)
|
||||
Direction.EAST -> listOf(topRight, topLeft, bottomLeft, bottomRight)
|
||||
}
|
||||
|
||||
val delegate = vanillaAOFactory()
|
||||
var isValid = false
|
||||
|
||||
fun update(blockCtx: BlockCtx, multiplier: Float) {
|
||||
val quadBounds = FloatArray(12)
|
||||
val flags = BitSet(3).apply { set(0) }
|
||||
// delegate.updateVertexBrightness(blockCtx.world, blockCtx.state, blockCtx.pos, face, quadBounds, flags)
|
||||
vanillaOrdered.forEachIndexed { idx, corner -> corner.set(delegate.vertexBrightness[idx], delegate.vertexColorMultiplier[idx] * multiplier) }
|
||||
isValid = true
|
||||
}
|
||||
|
||||
operator fun get(dir1: Direction, dir2: Direction): CornerLightData {
|
||||
val isTop = topDir == dir1 || topDir == dir2
|
||||
val isLeft = leftDir == dir1 || leftDir == dir2
|
||||
return if (isTop) {
|
||||
if (isLeft) topLeft else topRight
|
||||
} else {
|
||||
if (isLeft) bottomLeft else bottomRight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
import mods.betterfoliage.util.Int3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.axes
|
||||
import mods.betterfoliage.util.boxFaces
|
||||
import mods.betterfoliage.util.face
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.offset
|
||||
import mods.betterfoliage.util.rotate
|
||||
import net.minecraft.util.Direction
|
||||
|
||||
|
||||
const val defaultCornerDimming = 0.5f
|
||||
const val defaultEdgeDimming = 0.8f
|
||||
|
||||
// ================================
|
||||
// Shader instantiation lambdas
|
||||
// ================================
|
||||
fun cornerAo(fallbackAxis: Direction.Axis): CornerShaderFactory = { face, dir1, dir2 ->
|
||||
val fallbackDir = listOf(face, dir1, dir2).find { it.axis == fallbackAxis }!!
|
||||
CornerSingleFallback(face, dir1, dir2, fallbackDir)
|
||||
}
|
||||
val cornerFlat = { face: Direction, dir1: Direction, dir2: Direction -> FaceFlat(face) }
|
||||
fun cornerAoTri(func: (CornerLightData, CornerLightData)-> CornerLightData) = { face: Direction, dir1: Direction, dir2: Direction ->
|
||||
CornerTri(face, dir1, dir2, func)
|
||||
}
|
||||
val cornerAoMaxGreen = cornerAoTri { s1, s2 -> if (s1.green > s2.green) s1 else s2 }
|
||||
|
||||
fun cornerInterpolate(edgeAxis: Direction.Axis, weight: Float, dimming: Float): CornerShaderFactory = { dir1, dir2, dir3 ->
|
||||
val edgeDir = listOf(dir1, dir2, dir3).find { it.axis == edgeAxis }!!
|
||||
val faceDirs = listOf(dir1, dir2, dir3).filter { it.axis != edgeAxis }
|
||||
CornerInterpolateDimming(faceDirs[0], faceDirs[1], edgeDir, weight, dimming)
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Shaders
|
||||
// ================================
|
||||
object NoLighting : ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) = vertex.shade(CornerLightData.black)
|
||||
override fun rotate(rot: Rotation) = this
|
||||
}
|
||||
|
||||
class CornerSingleFallback(val face: Direction, val dir1: Direction, val dir2: Direction, val fallbackDir: Direction, val fallbackDimming: Float = defaultCornerDimming) :
|
||||
ModelLighter {
|
||||
val offset = Int3(fallbackDir)
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
val shading = context.lighting(face, dir1, dir2)
|
||||
if (shading.valid)
|
||||
vertex.shade(shading)
|
||||
else {
|
||||
vertex.shade(context.brightness(offset) brMul fallbackDimming, context.color(offset) colorMul fallbackDimming)
|
||||
}
|
||||
}
|
||||
override fun rotate(rot: Rotation) = CornerSingleFallback(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), fallbackDir.rotate(rot), fallbackDimming)
|
||||
}
|
||||
|
||||
inline fun accumulate(v1: CornerLightData?, v2: CornerLightData?, func: ((CornerLightData, CornerLightData)-> CornerLightData)): CornerLightData? {
|
||||
val v1ok = v1 != null && v1.valid
|
||||
val v2ok = v2 != null && v2.valid
|
||||
if (v1ok && v2ok) return func(v1!!, v2!!)
|
||||
if (v1ok) return v1
|
||||
if (v2ok) return v2
|
||||
return null
|
||||
}
|
||||
|
||||
class CornerTri(val face: Direction, val dir1: Direction, val dir2: Direction,
|
||||
val func: ((CornerLightData, CornerLightData)-> CornerLightData)) : ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
var acc = accumulate(
|
||||
context.lighting(face, dir1, dir2),
|
||||
context.lighting(dir1, face, dir2),
|
||||
func)
|
||||
acc = accumulate(
|
||||
acc,
|
||||
context.lighting(dir2, face, dir1),
|
||||
func)
|
||||
vertex.shade(acc ?: CornerLightData.black)
|
||||
}
|
||||
override fun rotate(rot: Rotation) = CornerTri(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), func)
|
||||
}
|
||||
|
||||
class EdgeInterpolateFallback(val face: Direction, val edgeDir: Direction, val pos: Double, val fallbackDimming: Float = defaultEdgeDimming):
|
||||
ModelLighter {
|
||||
val offset = Int3(edgeDir)
|
||||
val edgeAxis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
||||
val weightN = (0.5 - pos).toFloat()
|
||||
val weightP = (0.5 + pos).toFloat()
|
||||
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
val shadingP = context.lighting(face, edgeDir, (edgeAxis to Direction.AxisDirection.POSITIVE).face)
|
||||
val shadingN = context.lighting(face, edgeDir, (edgeAxis to Direction.AxisDirection.NEGATIVE).face)
|
||||
if (!shadingP.valid && !shadingN.valid) {
|
||||
return vertex.shade(context.brightness(offset) brMul fallbackDimming, context.color(offset) colorMul fallbackDimming)
|
||||
}
|
||||
if (!shadingP.valid) return vertex.shade(shadingN)
|
||||
if (!shadingN.valid) return vertex.shade(shadingP)
|
||||
vertex.shade(shadingP, shadingN, weightP, weightN)
|
||||
}
|
||||
override fun rotate(rot: Rotation) = EdgeInterpolateFallback(face.rotate(rot), edgeDir.rotate(rot), pos)
|
||||
}
|
||||
|
||||
class CornerInterpolateDimming(val face1: Direction, val face2: Direction, val edgeDir: Direction,
|
||||
val weight: Float, val dimming: Float, val fallbackDimming: Float = defaultCornerDimming
|
||||
) : ModelLighter {
|
||||
val offset = Int3(edgeDir)
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
var shading1 = context.lighting(face1, edgeDir, face2)
|
||||
var shading2 = context.lighting(face2, edgeDir, face1)
|
||||
var weight1 = weight
|
||||
var weight2 = 1.0f - weight
|
||||
if (!shading1.valid && !shading2.valid) {
|
||||
return vertex.shade(context.brightness(offset) brMul fallbackDimming, context.color(offset) colorMul fallbackDimming)
|
||||
}
|
||||
if (!shading1.valid) { shading1 = shading2; weight1 *= dimming }
|
||||
if (!shading2.valid) { shading2 = shading1; weight2 *= dimming }
|
||||
vertex.shade(shading1, shading2, weight1, weight2)
|
||||
}
|
||||
|
||||
override fun rotate(rot: Rotation) =
|
||||
CornerInterpolateDimming(face1.rotate(rot), face2.rotate(rot), edgeDir.rotate(rot), weight, dimming, fallbackDimming)
|
||||
}
|
||||
|
||||
class FaceCenter(val face: Direction): ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
vertex.red = 0.0f; vertex.green = 0.0f; vertex.blue = 0.0f;
|
||||
val b = IntArray(4)
|
||||
boxFaces[face].allCorners.forEachIndexed { idx, corner ->
|
||||
val shading = context.lighting(face, corner.first, corner.second)
|
||||
vertex.red += shading.red
|
||||
vertex.green += shading.green
|
||||
vertex.blue += shading.blue
|
||||
b[idx] = shading.brightness
|
||||
}
|
||||
vertex.apply { red *= 0.25f; green *= 0.25f; blue *= 0.25f }
|
||||
vertex.brightness = brSum(0.25f, *b)
|
||||
}
|
||||
override fun rotate(rot: Rotation) = FaceCenter(face.rotate(rot))
|
||||
}
|
||||
|
||||
class FaceFlat(val face: Direction): ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
vertex.shade(context.brightness(face.offset), context.color(Int3.zero))
|
||||
}
|
||||
override fun rotate(rot: Rotation): ModelLighter = FaceFlat(face.rotate(rot))
|
||||
}
|
||||
|
||||
class FlatOffset(val offset: Int3): ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
vertex.brightness = context.brightness(offset)
|
||||
vertex.setColor(context.color(offset))
|
||||
}
|
||||
override fun rotate(rot: Rotation): ModelLighter = this
|
||||
}
|
||||
|
||||
class FlatOffsetNoColor(val offset: Int3): ModelLighter {
|
||||
override fun shade(context: LightingCtx, vertex: RenderVertex) {
|
||||
vertex.brightness = context.brightness(offset)
|
||||
vertex.red = 1.0f; vertex.green = 1.0f; vertex.blue = 1.0f
|
||||
}
|
||||
override fun rotate(rot: Rotation): ModelLighter = this
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@file:JvmName("PixelFormat")
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
146
src/main/kotlin/mods/betterfoliage/render/lighting/Vertex.kt
Normal file
146
src/main/kotlin/mods/betterfoliage/render/lighting/Vertex.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package mods.betterfoliage.render.lighting
|
||||
|
||||
import mods.betterfoliage.render.old.CombinedContext
|
||||
import mods.betterfoliage.render.old.Quad
|
||||
import mods.betterfoliage.render.old.Vertex
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.Direction.*
|
||||
import java.awt.Color
|
||||
|
||||
typealias QuadIconResolver = (CombinedContext, Int, Quad) -> TextureAtlasSprite?
|
||||
typealias PostProcessLambda = RenderVertex.(CombinedContext, Int, Quad, Int, Vertex) -> Unit
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
class RenderVertex {
|
||||
var x: Double = 0.0
|
||||
var y: Double = 0.0
|
||||
var z: Double = 0.0
|
||||
var u: Double = 0.0
|
||||
var v: Double = 0.0
|
||||
var brightness: Int = 0
|
||||
var red: Float = 0.0f
|
||||
var green: Float = 0.0f
|
||||
var blue: Float = 0.0f
|
||||
|
||||
val rawData = IntArray(7)
|
||||
|
||||
fun init(vertex: Vertex, rot: Rotation, trans: Double3): RenderVertex {
|
||||
val result = vertex.xyz.rotate(rot) + trans
|
||||
x = result.x; y = result.y; z = result.z
|
||||
return this
|
||||
}
|
||||
fun init(vertex: Vertex): RenderVertex {
|
||||
x = vertex.xyz.x; y = vertex.xyz.y; z = vertex.xyz.z;
|
||||
u = vertex.uv.u; v = vertex.uv.v
|
||||
return this
|
||||
}
|
||||
fun translate(trans: Double3): RenderVertex { x += trans.x; y += trans.y; z += trans.z; return this }
|
||||
fun rotate(rot: Rotation): RenderVertex {
|
||||
if (rot === Rotation.identity) return this
|
||||
val rotX = rot.rotatedComponent(EAST, x, y, z)
|
||||
val rotY = rot.rotatedComponent(UP, x, y, z)
|
||||
val rotZ = rot.rotatedComponent(SOUTH, x, y, z)
|
||||
x = rotX; y = rotY; z = rotZ
|
||||
return this
|
||||
}
|
||||
inline fun rotateUV(n: Int): RenderVertex {
|
||||
when (n % 4) {
|
||||
1 -> { val t = v; v = -u; u = t; return this }
|
||||
2 -> { u = -u; v = -v; return this }
|
||||
3 -> { val t = -v; v = u; u = t; return this }
|
||||
else -> { return this }
|
||||
}
|
||||
}
|
||||
inline fun mirrorUV(mirrorU: Boolean, mirrorV: Boolean) {
|
||||
if (mirrorU) u = -u
|
||||
if (mirrorV) v = -v
|
||||
}
|
||||
inline fun setIcon(icon: TextureAtlasSprite): RenderVertex {
|
||||
u = (icon.maxU - icon.minU) * (u + 0.5) + icon.minU
|
||||
v = (icon.maxV - icon.minV) * (v + 0.5) + icon.minV
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun setGrey(level: Float) {
|
||||
val grey = Math.min((red + green + blue) * 0.333f * level, 1.0f)
|
||||
red = grey; green = grey; blue = grey
|
||||
}
|
||||
inline fun multiplyColor(color: Int) {
|
||||
red *= (color shr 16 and 255) / 256.0f
|
||||
green *= (color shr 8 and 255) / 256.0f
|
||||
blue *= (color and 255) / 256.0f
|
||||
}
|
||||
inline fun setColor(color: Int) {
|
||||
red = (color shr 16 and 255) / 256.0f
|
||||
green = (color shr 8 and 255) / 256.0f
|
||||
blue = (color and 255) / 256.0f
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** List of bit-shift offsets in packed brightness values where meaningful (4-bit) data is contained. */
|
||||
var brightnessComponents = listOf(20, 4)
|
||||
|
||||
/** Multiply the components of this packed brightness value with the given [Float]. */
|
||||
infix fun Int.brMul(f: Float): Int {
|
||||
val weight = (f * 256.0f).toInt()
|
||||
var result = 0
|
||||
brightnessComponents.forEach { shift ->
|
||||
val raw = (this shr shift) and 15
|
||||
val weighted = (raw) * weight / 256
|
||||
result = result or (weighted shl shift)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Multiply the components of this packed color value with the given [Float]. */
|
||||
infix fun Int.colorMul(f: Float): Int {
|
||||
val weight = (f * 256.0f).toInt()
|
||||
val red = (this shr 16 and 255) * weight / 256
|
||||
val green = (this shr 8 and 255) * weight / 256
|
||||
val blue = (this and 255) * weight / 256
|
||||
return (red shl 16) or (green shl 8) or blue
|
||||
}
|
||||
|
||||
/** Sum the components of all packed brightness values given. */
|
||||
fun brSum(multiplier: Float?, vararg brightness: Int): Int {
|
||||
val sum = Array(brightnessComponents.size) { 0 }
|
||||
brightnessComponents.forEachIndexed { idx, shift -> brightness.forEach { br ->
|
||||
val comp = (br shr shift) and 15
|
||||
sum[idx] += comp
|
||||
} }
|
||||
var result = 0
|
||||
brightnessComponents.forEachIndexed { idx, shift ->
|
||||
val comp = if (multiplier == null)
|
||||
((sum[idx]) shl shift)
|
||||
else
|
||||
((sum[idx].toFloat() * multiplier).toInt() shl shift)
|
||||
result = result or comp
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun brWeighted(br1: Int, weight1: Float, br2: Int, weight2: Float): Int {
|
||||
val w1int = (weight1 * 256.0f + 0.5f).toInt()
|
||||
val w2int = (weight2 * 256.0f + 0.5f).toInt()
|
||||
var result = 0
|
||||
brightnessComponents.forEachIndexed { idx, shift ->
|
||||
val comp1 = (br1 shr shift) and 15
|
||||
val comp2 = (br2 shr shift) and 15
|
||||
val compWeighted = (comp1 * w1int + comp2 * w2int) / 256
|
||||
result = result or ((compWeighted and 15) shl shift)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
||||
companion object {
|
||||
fun fromColor(color: Int): HSB {
|
||||
val hsbVals = Color.RGBtoHSB((color shr 16) and 255, (color shr 8) and 255, color and 255, null)
|
||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||
}
|
||||
}
|
||||
val asColor: Int get() = Color.HSBtoRGB(hue, saturation, brightness)
|
||||
}
|
||||
Reference in New Issue
Block a user