mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 11:34:05 +09:00
new lightmap: nice try but didn't work
This commit is contained in:
@@ -70,6 +70,21 @@ class Cvec {
|
|||||||
set(color)
|
set(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get RGBA Element using index, of which:
|
||||||
|
* - 0: R
|
||||||
|
* - 1: G
|
||||||
|
* - 2: B
|
||||||
|
* - 3: A
|
||||||
|
*/
|
||||||
|
fun getElem(index: Int) = when(index) {
|
||||||
|
0 -> r
|
||||||
|
1 -> g
|
||||||
|
2 -> b
|
||||||
|
3 -> a
|
||||||
|
else -> throw IndexOutOfBoundsException("Invalid index $index")
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets this color to the given color.
|
/** Sets this color to the given color.
|
||||||
*
|
*
|
||||||
* @param color the Cvec
|
* @param color the Cvec
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ internal class UnsafeCvecArray(val width: Int, val height: Int) {
|
|||||||
fun setB(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 8, value) }
|
fun setB(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 8, value) }
|
||||||
fun setA(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 12, value) }
|
fun setA(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 12, value) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param channel 0 for R, 1 for G, 2 for B, 3 for A
|
||||||
|
*/
|
||||||
|
inline fun channelSet(x: Int, y: Int, channel: Int, value: Float) {
|
||||||
|
array.setFloat(toAddr(x, y) + 4L * channel, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param channel 0 for R, 1 for G, 2 for B, 3 for A
|
||||||
|
*/
|
||||||
|
inline fun channelGet(x: Int, y: Int, channel: Int) = array.getFloat(toAddr(x, y) + 4L * channel)
|
||||||
|
|
||||||
fun max(x: Int, y: Int, other: Cvec) {
|
fun max(x: Int, y: Int, other: Cvec) {
|
||||||
setR(x, y, maxOf(getR(x, y), other.r))
|
setR(x, y, maxOf(getR(x, y), other.r))
|
||||||
setG(x, y, maxOf(getG(x, y), other.g))
|
setG(x, y, maxOf(getG(x, y), other.g))
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ class BlockProp {
|
|||||||
|
|
||||||
lateinit var opacity: Cvec
|
lateinit var opacity: Cvec
|
||||||
|
|
||||||
|
fun getOpacity(channel: Int) = when (channel) {
|
||||||
|
0 -> shadeColR
|
||||||
|
1 -> shadeColG
|
||||||
|
2 -> shadeColB
|
||||||
|
3 -> shadeColA
|
||||||
|
else -> throw IllegalArgumentException("Invalid channel $channel")
|
||||||
|
}
|
||||||
|
|
||||||
var strength: Int = 0
|
var strength: Int = 0
|
||||||
var density: Int = 0
|
var density: Int = 0
|
||||||
var viscosity: Int = 0
|
var viscosity: Int = 0
|
||||||
@@ -48,6 +56,16 @@ class BlockProp {
|
|||||||
inline val luminosity: Cvec
|
inline val luminosity: Cvec
|
||||||
get() = BlockPropUtil.getDynamicLumFunc(internalLumCol, dynamicLuminosityFunction)
|
get() = BlockPropUtil.getDynamicLumFunc(internalLumCol, dynamicLuminosityFunction)
|
||||||
|
|
||||||
|
fun getLum(channel: Int) = BlockPropUtil.getDynamicLumFuncByChan(
|
||||||
|
when (channel) {
|
||||||
|
0 -> lumColR
|
||||||
|
1 -> lumColG
|
||||||
|
2 -> lumColB
|
||||||
|
3 -> lumColA
|
||||||
|
else -> throw IllegalArgumentException("Invalid channel $channel")
|
||||||
|
}, dynamicLuminosityFunction, channel
|
||||||
|
)
|
||||||
|
|
||||||
var drop: Int = 0
|
var drop: Int = 0
|
||||||
|
|
||||||
var maxSupport: Int = -1 // couldn't use NULL at all...
|
var maxSupport: Int = -1 // couldn't use NULL at all...
|
||||||
|
|||||||
@@ -37,22 +37,31 @@ object BlockPropUtil {
|
|||||||
|
|
||||||
private fun getTorchFlicker(baseLum: Cvec): Cvec {
|
private fun getTorchFlicker(baseLum: Cvec): Cvec {
|
||||||
val funcY = FastMath.interpolateLinear(flickerFuncX / flickerFuncDomain, flickerP0, flickerP1)
|
val funcY = FastMath.interpolateLinear(flickerFuncX / flickerFuncDomain, flickerP0, flickerP1)
|
||||||
|
|
||||||
return alterBrightnessUniform(baseLum, funcY)
|
return alterBrightnessUniform(baseLum, funcY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getTorchFlicker(baseLum: Float): Float {
|
||||||
|
return baseLum + FastMath.interpolateLinear(flickerFuncX / flickerFuncDomain, flickerP0, flickerP1)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSlowBreath(baseLum: Cvec): Cvec {
|
private fun getSlowBreath(baseLum: Cvec): Cvec {
|
||||||
val funcY = FastMath.sin(FastMath.PI * breathFuncX / breathCycleDuration) * breathRange
|
val funcY = FastMath.sin(FastMath.PI * breathFuncX / breathCycleDuration) * breathRange
|
||||||
|
|
||||||
return alterBrightnessUniform(baseLum, funcY)
|
return alterBrightnessUniform(baseLum, funcY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSlowBreath(baseLum: Float): Float {
|
||||||
|
return baseLum + FastMath.sin(FastMath.PI * breathFuncX / breathCycleDuration) * breathRange
|
||||||
|
}
|
||||||
|
|
||||||
private fun getPulsate(baseLum: Cvec): Cvec {
|
private fun getPulsate(baseLum: Cvec): Cvec {
|
||||||
val funcY = FastMath.sin(FastMath.PI * pulsateFuncX / pulsateCycleDuration) * pulsateRange
|
val funcY = FastMath.sin(FastMath.PI * pulsateFuncX / pulsateCycleDuration) * pulsateRange
|
||||||
|
|
||||||
return alterBrightnessUniform(baseLum, funcY)
|
return alterBrightnessUniform(baseLum, funcY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPulsate(baseLum: Float): Float {
|
||||||
|
return baseLum + FastMath.sin(FastMath.PI * pulsateFuncX / pulsateCycleDuration) * pulsateRange
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using our own timer so that they flickers for same duration regardless of game's FPS
|
* Using our own timer so that they flickers for same duration regardless of game's FPS
|
||||||
*/
|
*/
|
||||||
@@ -94,6 +103,20 @@ object BlockPropUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param chan 0 for R, 1 for G, 2 for B, 3 for A
|
||||||
|
*/
|
||||||
|
fun getDynamicLumFuncByChan(baseLum: Float, type: Int, chan: Int): Float {
|
||||||
|
return when (type) {
|
||||||
|
1 -> getTorchFlicker(baseLum)
|
||||||
|
2 -> (Terrarum.ingame!!.world).globalLight.cpy().mul(LightmapRenderer.DIV_FLOAT).getElem(chan) // current global light
|
||||||
|
3 -> WeatherMixer.getGlobalLightOfTime(Terrarum.ingame!!.world, WorldTime.DAY_LENGTH / 2).cpy().mul(LightmapRenderer.DIV_FLOAT).getElem(chan) // daylight at noon
|
||||||
|
4 -> getSlowBreath(baseLum)
|
||||||
|
5 -> getPulsate(baseLum)
|
||||||
|
else -> baseLum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Darken or brighten colour by 'brighten' argument
|
* Darken or brighten colour by 'brighten' argument
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import com.badlogic.gdx.utils.Disposable
|
|||||||
import net.torvald.UnsafeHelper
|
import net.torvald.UnsafeHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* As the fast access to this LUT is critical for the performance because of the way light calculation work,
|
||||||
|
* even the IO can be a bottleneck so I use UnsafePointer.
|
||||||
|
*
|
||||||
* Created by Torvald on 2019-12-05.
|
* Created by Torvald on 2019-12-05.
|
||||||
*/
|
*/
|
||||||
internal object LightmapHDRMap : Disposable {
|
internal object LightmapHDRMap : Disposable {
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ import net.torvald.terrarum.gameactors.Luminous
|
|||||||
import net.torvald.terrarum.gameworld.BlockAddress
|
import net.torvald.terrarum.gameworld.BlockAddress
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||||
|
import net.torvald.terrarum.modulebasegame.ui.abs
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
|
import net.torvald.terrarum.worlddrawer.LightmapRenderer.convX
|
||||||
|
import net.torvald.terrarum.worlddrawer.LightmapRenderer.convY
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,6 +229,38 @@ object LightmapRenderer {
|
|||||||
// when disabled, light will "decay out" instead of "instantly out", which can have a cool effect
|
// when disabled, light will "decay out" instead of "instantly out", which can have a cool effect
|
||||||
// but the performance boost is measly 0.1 ms on 6700K
|
// but the performance boost is measly 0.1 ms on 6700K
|
||||||
lightmap.zerofill()
|
lightmap.zerofill()
|
||||||
|
|
||||||
|
|
||||||
|
// pre-seed the lightmap with known value
|
||||||
|
for (x in for_x_start - overscan_open + 1..for_x_end + overscan_open - 1) {
|
||||||
|
for (y in for_y_start - overscan_open + 1..for_y_end + overscan_open - 1) {
|
||||||
|
val tile = world.getTileFromTerrain(x, y)
|
||||||
|
val wall = world.getTileFromWall(x, y)
|
||||||
|
|
||||||
|
val lightlevel = if (!BlockCodex[tile].isSolid && !BlockCodex[wall].isSolid)
|
||||||
|
sunLight.cpy()
|
||||||
|
else
|
||||||
|
colourNull.cpy()
|
||||||
|
// are you a light source?
|
||||||
|
lightlevel.add(BlockCodex[tile].luminosity)
|
||||||
|
|
||||||
|
// TODO: add lanterns
|
||||||
|
|
||||||
|
// subtract attenuation value
|
||||||
|
if (!lightlevel.nonZero()) {
|
||||||
|
lightlevel.sub(
|
||||||
|
BlockCodex[tile].opacity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lx = x.convX(); val ly = y.convY()
|
||||||
|
|
||||||
|
lightmap.setR(lx, ly, lightlevel.r)
|
||||||
|
lightmap.setG(lx, ly, lightlevel.g)
|
||||||
|
lightmap.setB(lx, ly, lightlevel.b)
|
||||||
|
lightmap.setA(lx, ly, lightlevel.a)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// O((5*9)n) == O(n) where n is a size of the map.
|
// O((5*9)n) == O(n) where n is a size of the map.
|
||||||
// Because of inevitable overlaps on the area, it only works with MAX blend
|
// Because of inevitable overlaps on the area, it only works with MAX blend
|
||||||
@@ -240,7 +275,7 @@ object LightmapRenderer {
|
|||||||
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightTotal") {
|
AppLoader.measureDebugTime("Renderer.LightTotal") {
|
||||||
// Round 2
|
// Round 2
|
||||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
/*for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||||
// TODO multithread the following for loop duh
|
// TODO multithread the following for loop duh
|
||||||
for (x in for_x_start - overscan_open..for_x_end) {
|
for (x in for_x_start - overscan_open..for_x_end) {
|
||||||
calculateAndAssign(lightmap, x, y)
|
calculateAndAssign(lightmap, x, y)
|
||||||
@@ -266,6 +301,69 @@ object LightmapRenderer {
|
|||||||
for (x in for_x_start - overscan_open..for_x_end) {
|
for (x in for_x_start - overscan_open..for_x_end) {
|
||||||
calculateAndAssign(lightmap, x, y)
|
calculateAndAssign(lightmap, x, y)
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
// per-channel operation for bit more aggressive optimisation
|
||||||
|
repeat(4) { rgbaOffset ->
|
||||||
|
val visitedCells = setOf(-1L) // (x shl 32) or y
|
||||||
|
|
||||||
|
for (x in for_x_start - overscan_open + 1..for_x_end + overscan_open - 1) {
|
||||||
|
for (y in for_y_start - overscan_open + 1..for_y_end + overscan_open - 1) {
|
||||||
|
val lx = x.convX()
|
||||||
|
val ly = y.convY()
|
||||||
|
|
||||||
|
val hash = (x.toLong() shl 32) or y.toLong()
|
||||||
|
if (visitedCells.contains(hash)) continue
|
||||||
|
|
||||||
|
var thisLightValue = lightmap.channelGet(lx, ly, rgbaOffset)
|
||||||
|
|
||||||
|
|
||||||
|
// nearby tiles, namely a b c d e f g h
|
||||||
|
val tilea = world.getTileFromTerrain(x - 1, y - 1)
|
||||||
|
val tileb = world.getTileFromTerrain(x, y - 1)
|
||||||
|
val tilec = world.getTileFromTerrain(x + 1, y - 1)
|
||||||
|
val tiled = world.getTileFromTerrain(x - 1, y)
|
||||||
|
val tilee = world.getTileFromTerrain(x + 1, y)
|
||||||
|
val tilef = world.getTileFromTerrain(x - 1, y + 1)
|
||||||
|
val tileg = world.getTileFromTerrain(x, y + 1)
|
||||||
|
val tileh = world.getTileFromTerrain(x + 1, y + 1)
|
||||||
|
|
||||||
|
// max value from plus-shaped neighbouring tiles
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileb].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tiled].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilee].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileg].getLum(rgbaOffset))
|
||||||
|
// max vaule from x-shaped neighbouring tiles
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilea].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilec].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilef].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileh].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
|
||||||
|
lightmap.channelSet(lx, ly, rgbaOffset, thisLightValue)
|
||||||
|
|
||||||
|
// recurse condition
|
||||||
|
if (lightmap.channelGet(lx - 1, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y - 1, rgbaOffset, visitedCells, 0)
|
||||||
|
if (lightmap.channelGet(lx, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x, y - 1, rgbaOffset, visitedCells, 0)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y - 1, rgbaOffset, visitedCells, 0)
|
||||||
|
|
||||||
|
if (lightmap.channelGet(lx - 1, ly, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y, rgbaOffset, visitedCells, 0)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y, rgbaOffset, visitedCells, 0)
|
||||||
|
|
||||||
|
if (lightmap.channelGet(lx - 1, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y + 1, rgbaOffset, visitedCells, 0)
|
||||||
|
if (lightmap.channelGet(lx, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x, y + 1, rgbaOffset, visitedCells, 0)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y + 1, rgbaOffset, visitedCells, 0)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +409,69 @@ object LightmapRenderer {
|
|||||||
// TODO re-init at every resize
|
// TODO re-init at every resize
|
||||||
private lateinit var updateMessages: List<Array<ThreadedLightmapUpdateMessage>>
|
private lateinit var updateMessages: List<Array<ThreadedLightmapUpdateMessage>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x x position in the world
|
||||||
|
* @param y y position in the world
|
||||||
|
*/
|
||||||
|
private fun spread(x: Int, y: Int, rgbaOffset: Int, visitedCells: Set<Long>, recurseCount: Int) {
|
||||||
|
val lx = x.convX()
|
||||||
|
val ly = y.convY()
|
||||||
|
|
||||||
|
val hash = (x.toLong() shl 32) or y.toLong()
|
||||||
|
|
||||||
|
// stop condition
|
||||||
|
if (visitedCells.contains(hash)) return
|
||||||
|
if (x !in for_x_start - overscan_open + 1..for_x_end + overscan_open - 1) return
|
||||||
|
if (y !in for_y_start - overscan_open + 1..for_y_end + overscan_open - 1) return
|
||||||
|
if (recurseCount > 128) return
|
||||||
|
|
||||||
|
var thisLightValue = lightmap.channelGet(lx, ly, rgbaOffset)
|
||||||
|
|
||||||
|
// nearby tiles, namely a b c d e f g h
|
||||||
|
val tilea = world.getTileFromTerrain(x - 1, y - 1)
|
||||||
|
val tileb = world.getTileFromTerrain(x, y - 1)
|
||||||
|
val tilec = world.getTileFromTerrain(x + 1, y - 1)
|
||||||
|
val tiled = world.getTileFromTerrain(x - 1, y)
|
||||||
|
val tilee = world.getTileFromTerrain(x + 1, y)
|
||||||
|
val tilef = world.getTileFromTerrain(x - 1, y + 1)
|
||||||
|
val tileg = world.getTileFromTerrain(x, y + 1)
|
||||||
|
val tileh = world.getTileFromTerrain(x + 1, y + 1)
|
||||||
|
|
||||||
|
// max value from plus-shaped neighbouring tiles
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileb].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tiled].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilee].getLum(rgbaOffset))
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileg].getLum(rgbaOffset))
|
||||||
|
// max vaule from x-shaped neighbouring tiles
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilea].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilec].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tilef].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
thisLightValue = maxOf(thisLightValue, BlockCodex[tileh].getLum(rgbaOffset) * 0.70710678f)
|
||||||
|
|
||||||
|
lightmap.channelSet(lx, ly, rgbaOffset, thisLightValue)
|
||||||
|
|
||||||
|
// recurse condition
|
||||||
|
if (lightmap.channelGet(lx - 1, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y - 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
if (lightmap.channelGet(lx, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x, y - 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly - 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y - 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
|
||||||
|
if (lightmap.channelGet(lx - 1, ly, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
|
||||||
|
if (lightmap.channelGet(lx - 1, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x - 1, y + 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
if (lightmap.channelGet(lx, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x, y + 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
if (lightmap.channelGet(lx + 1, ly + 1, rgbaOffset) < thisLightValue)
|
||||||
|
spread(x + 1, y + 1, rgbaOffset, visitedCells, recurseCount + 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeUpdateTaskList() {
|
private fun makeUpdateTaskList() {
|
||||||
val lightTaskArr = ArrayList<ThreadedLightmapUpdateMessage>()
|
val lightTaskArr = ArrayList<ThreadedLightmapUpdateMessage>()
|
||||||
|
|
||||||
@@ -786,7 +947,10 @@ object LightmapRenderer {
|
|||||||
hdr(this.a.coerceIn(0f, 1f))
|
hdr(this.a.coerceIn(0f, 1f))
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun Cvec.nonZero() = this.r + this.g + this.b + this.a > epsilon
|
private fun Cvec.nonZero() = this.r.abs() > epsilon &&
|
||||||
|
this.g.abs() > epsilon &&
|
||||||
|
this.b.abs() > epsilon &&
|
||||||
|
this.a.abs() > epsilon
|
||||||
|
|
||||||
val histogram: Histogram
|
val histogram: Histogram
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
Reference in New Issue
Block a user