mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 10:04:05 +09:00
radiosity: nice try but works half-assed and is slower
better optimise the old method, there's lots of overlaps there
This commit is contained in:
@@ -100,6 +100,8 @@ object LightmapRenderer {
|
|||||||
//private var lightmap: Array<Cvec> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Cvec(0) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
//private var lightmap: Array<Cvec> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Cvec(0) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
||||||
private val lanternMap = HashMap<BlockAddress, Cvec>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
|
private val lanternMap = HashMap<BlockAddress, Cvec>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
|
||||||
|
|
||||||
|
private val lightsourceMap = HashMap<BlockAddress, Cvec>(256)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
LightmapHDRMap.invoke()
|
LightmapHDRMap.invoke()
|
||||||
printdbg(this, "Overscan open: $overscan_open; opaque: $overscan_opaque")
|
printdbg(this, "Overscan open: $overscan_open; opaque: $overscan_opaque")
|
||||||
@@ -229,6 +231,7 @@ 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()
|
||||||
|
lightsourceMap.clear()
|
||||||
|
|
||||||
|
|
||||||
// pre-seed the lightmap with known value
|
// pre-seed the lightmap with known value
|
||||||
@@ -242,15 +245,13 @@ object LightmapRenderer {
|
|||||||
else
|
else
|
||||||
colourNull.cpy()
|
colourNull.cpy()
|
||||||
// are you a light source?
|
// are you a light source?
|
||||||
lightlevel.add(BlockCodex[tile].luminosity)
|
lightlevel.maxAndAssign(BlockCodex[tile].luminosity)
|
||||||
|
// there will be a way to slightly optimise this following line but hey, let's make everything working right first...
|
||||||
|
lightlevel.maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
|
||||||
|
|
||||||
// TODO: add lanterns
|
|
||||||
|
|
||||||
// subtract attenuation value
|
|
||||||
if (!lightlevel.nonZero()) {
|
if (!lightlevel.nonZero()) {
|
||||||
lightlevel.sub(
|
// mark the tile as a light source
|
||||||
BlockCodex[tile].opacity
|
lightsourceMap[LandUtil.getBlockAddr(world, x, y)] = lightlevel
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val lx = x.convX(); val ly = y.convY()
|
val lx = x.convX(); val ly = y.convY()
|
||||||
@@ -261,6 +262,7 @@ object LightmapRenderer {
|
|||||||
lightmap.setA(lx, ly, lightlevel.a)
|
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
|
||||||
@@ -274,8 +276,15 @@ object LightmapRenderer {
|
|||||||
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
|
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
|
||||||
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightTotal") {
|
AppLoader.measureDebugTime("Renderer.LightTotal") {
|
||||||
|
// Round 1
|
||||||
|
for (y in for_y_start - overscan_open..for_y_end) {
|
||||||
|
// TODO multithread the following for loop duh
|
||||||
|
for (x in for_x_start - overscan_open..for_x_end) {
|
||||||
|
calculateAndAssign(lightmap, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
// 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)
|
||||||
@@ -301,70 +310,47 @@ 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
|
// per-channel operation for bit more aggressive optimisation
|
||||||
repeat(4) { rgbaOffset ->
|
/*for (lightsource in lightsourceMap) {
|
||||||
val visitedCells = setOf(-1L) // (x shl 32) or y
|
val (lsx, lsy) = LandUtil.resolveBlockAddr(world, lightsource.key)
|
||||||
|
|
||||||
for (x in for_x_start - overscan_open + 1..for_x_end + overscan_open - 1) {
|
if (lsx !in for_x_start - overscan_open + 1..for_x_end + overscan_open - 1 || lsy !in for_y_start - overscan_open + 1..for_y_end + overscan_open - 1)
|
||||||
for (y in for_y_start - overscan_open + 1..for_y_end + overscan_open - 1) {
|
continue
|
||||||
val lx = x.convX()
|
|
||||||
val ly = y.convY()
|
|
||||||
|
|
||||||
val hash = (x.toLong() shl 32) or y.toLong()
|
repeat(4) { rgbaOffset ->
|
||||||
if (visitedCells.contains(hash)) continue
|
for (genus in 1..6) { // use of overscan_open for loop limit is completely arbitrary
|
||||||
|
val rimSize = 1 + 2 * genus
|
||||||
|
|
||||||
var thisLightValue = 0f
|
var skip = true
|
||||||
|
// left side, counterclockwise
|
||||||
|
for (k in 0 until rimSize) {
|
||||||
// nearby tiles, namely a b c d e f g h
|
val wx = lsx - genus; val wy = lsy - genus + k
|
||||||
val tilea = world.getTileFromTerrain(x - 1, y - 1)
|
skip = skip and radiate(rgbaOffset, wx, wy, lightsource.value,(lsx - wx)*(lsx - wx) + (lsy - wy)*(lsy - wy))
|
||||||
val tileb = world.getTileFromTerrain(x, y - 1)
|
// whenever radiate() returns false (not-skip), skip is permanently fixated as false
|
||||||
val tilec = world.getTileFromTerrain(x + 1, y - 1)
|
}
|
||||||
val tiled = world.getTileFromTerrain(x - 1, y)
|
// bottom side, counterclockwise
|
||||||
val tilee = world.getTileFromTerrain(x + 1, y)
|
for (k in 1 until rimSize) {
|
||||||
val tilef = world.getTileFromTerrain(x - 1, y + 1)
|
val wx = lsx - genus + k; val wy = lsy + genus
|
||||||
val tileg = world.getTileFromTerrain(x, y + 1)
|
skip = skip and radiate(rgbaOffset, wx, wy, lightsource.value,(lsx - wx)*(lsx - wx) + (lsy - wy)*(lsy - wy))
|
||||||
val tileh = world.getTileFromTerrain(x + 1, y + 1)
|
}
|
||||||
|
// right side, counterclockwise
|
||||||
// max value from plus-shaped neighbouring tiles
|
for (k in 1 until rimSize) {
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tileb].getLum(rgbaOffset))
|
val wx = lsx + genus; val wy = lsy + genus - k
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tiled].getLum(rgbaOffset))
|
skip = skip and radiate(rgbaOffset, wx, wy, lightsource.value,(lsx - wx)*(lsx - wx) + (lsy - wy)*(lsy - wy))
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tilee].getLum(rgbaOffset))
|
}
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tileg].getLum(rgbaOffset))
|
// top side, counterclockwise
|
||||||
// max vaule from x-shaped neighbouring tiles
|
for (k in 1 until rimSize - 1) {
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tilea].getLum(rgbaOffset) * 0.70710678f)
|
val wx = lsx + genus - k; val wy = lsy - genus
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tilec].getLum(rgbaOffset) * 0.70710678f)
|
skip = skip and radiate(rgbaOffset, wx, wy, lightsource.value,(lsx - wx)*(lsx - wx) + (lsy - wy)*(lsy - wy))
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tilef].getLum(rgbaOffset) * 0.70710678f)
|
}
|
||||||
thisLightValue = maxOf(thisLightValue, BlockCodex[tileh].getLum(rgbaOffset) * 0.70710678f)
|
|
||||||
|
|
||||||
lightmap.channelSet(lx, ly, rgbaOffset, lightmap.channelGet(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)
|
|
||||||
|
|
||||||
|
if (skip) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (world.worldIndex != -1) { // to avoid updating on the null world
|
else if (world.worldIndex != -1) { // to avoid updating on the null world
|
||||||
@@ -404,74 +390,42 @@ object LightmapRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the lightmap is already been seeded with lightsource.
|
||||||
|
*
|
||||||
|
* @return true if skip
|
||||||
|
*/
|
||||||
|
private fun radiate(channel: Int, wx: Int, wy: Int, lightsource: Cvec, distSqr: Int): Boolean {
|
||||||
|
val lx = wx.convX(); val ly = wy.convY()
|
||||||
|
|
||||||
|
if (lx !in 1..lightmap.width - 1 || ly !in 1..lightmap.height - 1)
|
||||||
|
return true
|
||||||
|
|
||||||
|
val currentLightLevel = lightmap.channelGet(lx, ly, channel)
|
||||||
|
val attenuate = BlockCodex[world.getTileFromTerrain(wx, wy)].getOpacity(channel)
|
||||||
|
|
||||||
|
var brightestNeighbour = lightmap.channelGet(lx, ly - 1, channel)
|
||||||
|
brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx, ly + 1, channel))
|
||||||
|
brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx - 1, ly, channel))
|
||||||
|
brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx + 1, ly, channel))
|
||||||
|
//brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx - 1, ly - 1, channel) * 0.70710678f)
|
||||||
|
//brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx - 1, ly + 1, channel) * 0.70710678f)
|
||||||
|
//brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx + 1, ly - 1, channel) * 0.70710678f)
|
||||||
|
//brightestNeighbour = maxOf(brightestNeighbour, lightmap.channelGet(lx + 1, ly + 1, channel) * 0.70710678f)
|
||||||
|
|
||||||
|
val newLight = brightestNeighbour - attenuate
|
||||||
|
|
||||||
|
if (newLight <= currentLightLevel) return true
|
||||||
|
|
||||||
|
lightmap.channelSet(lx, ly, channel, newLight)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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 = 0f
|
|
||||||
|
|
||||||
// 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, lightmap.channelGet(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>()
|
||||||
|
|
||||||
@@ -733,10 +687,10 @@ object LightmapRenderer {
|
|||||||
|
|
||||||
// will "overwrite" what's there in the lightmap if it's the first pass
|
// will "overwrite" what's there in the lightmap if it's the first pass
|
||||||
// takes about 2 ms on 6700K
|
// takes about 2 ms on 6700K
|
||||||
/* + */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y - 1, thisTileOpacity2))
|
/* + *///lightLevelThis.maxAndAssign(darkenColoured(x - 1, y - 1, thisTileOpacity2))
|
||||||
/* + */lightLevelThis.maxAndAssign(darkenColoured(x + 1, y - 1, thisTileOpacity2))
|
/* + *///lightLevelThis.maxAndAssign(darkenColoured(x + 1, y - 1, thisTileOpacity2))
|
||||||
/* + */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y + 1, thisTileOpacity2))
|
/* + *///lightLevelThis.maxAndAssign(darkenColoured(x - 1, y + 1, thisTileOpacity2))
|
||||||
/* + */lightLevelThis.maxAndAssign(darkenColoured(x + 1, y + 1, thisTileOpacity2))
|
/* + *///lightLevelThis.maxAndAssign(darkenColoured(x + 1, y + 1, thisTileOpacity2))
|
||||||
/* * */lightLevelThis.maxAndAssign(darkenColoured(x, y - 1, thisTileOpacity))
|
/* * */lightLevelThis.maxAndAssign(darkenColoured(x, y - 1, thisTileOpacity))
|
||||||
/* * */lightLevelThis.maxAndAssign(darkenColoured(x, y + 1, thisTileOpacity))
|
/* * */lightLevelThis.maxAndAssign(darkenColoured(x, y + 1, thisTileOpacity))
|
||||||
/* * */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y, thisTileOpacity))
|
/* * */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y, thisTileOpacity))
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 256 KiB |
Reference in New Issue
Block a user