memoised dynamic luminosity

This commit is contained in:
minjaesong
2020-02-22 15:47:34 +09:00
parent 7eee6e34d1
commit 8b68e8917b
7 changed files with 85 additions and 79 deletions

View File

@@ -7,6 +7,7 @@ import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.utils.CSVFetcher import net.torvald.terrarum.utils.CSVFetcher
import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.LightmapRenderer
import net.torvald.util.SortedArrayList
import org.apache.commons.csv.CSVRecord import org.apache.commons.csv.CSVRecord
import java.io.IOException import java.io.IOException
@@ -17,6 +18,8 @@ object BlockCodex {
private var blockProps = HashMap<Int, BlockProp>() private var blockProps = HashMap<Int, BlockProp>()
val dynamicLights = SortedArrayList<Int>()
/** 4096 */ /** 4096 */
const val MAX_TERRAIN_TILES = GameWorld.TILES_SUPPORTED const val MAX_TERRAIN_TILES = GameWorld.TILES_SUPPORTED
@@ -45,6 +48,10 @@ object BlockCodex {
val id = intVal(it, "id") val id = intVal(it, "id")
setProp(id, it) setProp(id, it)
if ((blockProps[id]?.dynamicLuminosityFunction ?: 0) != 0) {
dynamicLights.add(id)
}
if (id > highestNumber) if (id > highestNumber)
highestNumber = id highestNumber = id
} }
@@ -113,11 +120,11 @@ object BlockCodex {
prop.strength = intVal(record, "str") prop.strength = intVal(record, "str")
prop.density = intVal(record, "dsty") prop.density = intVal(record, "dsty")
prop.lumColR = floatVal(record, "lumr") / LightmapRenderer.MUL_FLOAT prop.baseLumColR = floatVal(record, "lumr") / LightmapRenderer.MUL_FLOAT
prop.lumColG = floatVal(record, "lumg") / LightmapRenderer.MUL_FLOAT prop.baseLumColG = floatVal(record, "lumg") / LightmapRenderer.MUL_FLOAT
prop.lumColB = floatVal(record, "lumb") / LightmapRenderer.MUL_FLOAT prop.baseLumColB = floatVal(record, "lumb") / LightmapRenderer.MUL_FLOAT
prop.lumColA = floatVal(record, "lumuv") / LightmapRenderer.MUL_FLOAT prop.baseLumColA = floatVal(record, "lumuv") / LightmapRenderer.MUL_FLOAT
prop.internalLumCol = Cvec(prop.lumColR, prop.lumColG, prop.lumColB, prop.lumColA) prop.baseLumCol.set(prop.baseLumColR, prop.baseLumColG, prop.baseLumColB, prop.baseLumColA)
prop.friction = intVal(record, "fr") prop.friction = intVal(record, "fr")
prop.viscosity = intVal(record, "vscs") prop.viscosity = intVal(record, "vscs")

View File

@@ -44,27 +44,24 @@ class BlockProp {
/** 1.0f for 1023, 0.25f for 255 */ /** 1.0f for 1023, 0.25f for 255 */
var lumColR = 0f internal var baseLumColR = 0f // base value used to calculate dynamic luminosity
var lumColG = 0f internal var baseLumColG = 0f // base value used to calculate dynamic luminosity
var lumColB = 0f internal var baseLumColB = 0f // base value used to calculate dynamic luminosity
var lumColA = 0f internal var baseLumColA = 0f // base value used to calculate dynamic luminosity
lateinit var internalLumCol: Cvec internal val baseLumCol = Cvec(0)
var lumColR = 0f // memoised value of dynamic luminosity
var lumColG = 0f // memoised value of dynamic luminosity
var lumColB = 0f // memoised value of dynamic luminosity
var lumColA = 0f // memoised value of dynamic luminosity
var lumCol = Cvec(0)
/** /**
* @param luminosity * @param luminosity
*/ */
inline val luminosity: Cvec //inline val luminosity: Cvec
get() = BlockPropUtil.getDynamicLumFunc(internalLumCol, dynamicLuminosityFunction) // get() = BlockPropUtil.getDynamicLumFunc(internalLumCol, dynamicLuminosityFunction)
fun getLum(channel: Int) = BlockPropUtil.getDynamicLumFuncByChan( fun getLum(channel: Int) = lumCol.getElem(channel)
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

View File

@@ -86,13 +86,28 @@ object BlockPropUtil {
// pulsate-related vars // pulsate-related vars
if (pulsateFuncX > pulsateCycleDuration) pulsateFuncX -= pulsateCycleDuration if (pulsateFuncX > pulsateCycleDuration) pulsateFuncX -= pulsateCycleDuration
// update the memoised values in props
for (key in BlockCodex.dynamicLights) {
try {
val prop = BlockCodex[key]
if (prop.dynamicLuminosityFunction != 0) {
prop.lumCol.set(getDynamicLumFunc(prop.baseLumCol, prop.dynamicLuminosityFunction))
prop.lumColR = prop.lumCol.r
prop.lumColG = prop.lumCol.g
prop.lumColB = prop.lumCol.b
prop.lumColA = prop.lumCol.a
}
}
catch (skip: NullPointerException) {}
}
} }
private fun getNewRandom() = random.nextFloat().times(2).minus(1f) * flickerFuncRange private fun getNewRandom() = random.nextFloat().times(2).minus(1f) * flickerFuncRange
private fun linearInterpolation1D(a: Float, b: Float, x: Float) = a * (1 - x) + b * x private fun linearInterpolation1D(a: Float, b: Float, x: Float) = a * (1 - x) + b * x
fun getDynamicLumFunc(baseLum: Cvec, type: Int): Cvec { private fun getDynamicLumFunc(baseLum: Cvec, type: Int): Cvec {
return when (type) { return when (type) {
1 -> getTorchFlicker(baseLum) 1 -> getTorchFlicker(baseLum)
2 -> (Terrarum.ingame!!.world).globalLight.cpy().mul(LightmapRenderer.DIV_FLOAT) // current global light 2 -> (Terrarum.ingame!!.world).globalLight.cpy().mul(LightmapRenderer.DIV_FLOAT) // current global light
@@ -106,7 +121,7 @@ object BlockPropUtil {
/** /**
* @param chan 0 for R, 1 for G, 2 for B, 3 for A * @param chan 0 for R, 1 for G, 2 for B, 3 for A
*/ */
fun getDynamicLumFuncByChan(baseLum: Float, type: Int, chan: Int): Float { private fun getDynamicLumFuncByChan(baseLum: Float, type: Int, chan: Int): Float {
return when (type) { return when (type) {
1 -> getTorchFlicker(baseLum) 1 -> getTorchFlicker(baseLum)
2 -> (Terrarum.ingame!!.world).globalLight.cpy().mul(LightmapRenderer.DIV_FLOAT).getElem(chan) // current global light 2 -> (Terrarum.ingame!!.world).globalLight.cpy().mul(LightmapRenderer.DIV_FLOAT).getElem(chan) // current global light

View File

@@ -16,7 +16,7 @@ import java.util.*
internal class FixtureTikiTorch : FixtureBase(BlockBox(BlockBox.NO_COLLISION, 1, 2)), Luminous { internal class FixtureTikiTorch : FixtureBase(BlockBox(BlockBox.NO_COLLISION, 1, 2)), Luminous {
override var color: Cvec override var color: Cvec
get() = BlockCodex[Block.TORCH].luminosity get() = BlockCodex[Block.TORCH].lumCol
set(value) { set(value) {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }

View File

@@ -0,0 +1,15 @@
package net.torvald.terrarum.tests
import net.torvald.util.SortedArrayList
fun main(args: Array<String>) {
val t = SortedArrayList<Int>()
t.add(5)
t.add(1)
t.add(4)
t.add(2)
t.add(3)
t.forEach { print(it) }
}

View File

@@ -24,6 +24,8 @@ 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.convX
import net.torvald.terrarum.worlddrawer.LightmapRenderer.convY import net.torvald.terrarum.worlddrawer.LightmapRenderer.convY
import net.torvald.util.SortedArrayList
import kotlin.math.sign
import kotlin.system.exitProcess import kotlin.system.exitProcess
/** /**
@@ -100,7 +102,7 @@ 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) private val lightsourceMap = ArrayList<Pair<BlockAddress, Cvec>>(256)
init { init {
LightmapHDRMap.invoke() LightmapHDRMap.invoke()
@@ -245,13 +247,13 @@ object LightmapRenderer {
else else
colourNull.cpy() colourNull.cpy()
// are you a light source? // are you a light source?
lightlevel.maxAndAssign(BlockCodex[tile].luminosity) lightlevel.maxAndAssign(BlockCodex[tile].lumCol)
// there will be a way to slightly optimise this following line but hey, let's make everything working right first... // 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) lightlevel.maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
if (!lightlevel.nonZero()) { if (!lightlevel.nonZero()) {
// mark the tile as a light source // mark the tile as a light source
lightsourceMap[LandUtil.getBlockAddr(world, x, y)] = lightlevel lightsourceMap.add(LandUtil.getBlockAddr(world, x, y) to lightlevel)
} }
val lx = x.convX(); val ly = y.convY() val lx = x.convX(); val ly = y.convY()
@@ -269,32 +271,32 @@ object LightmapRenderer {
fun r1() { fun r1() {
// Round 1 // Round 1
for (y in for_y_start - overscan_open..for_y_end + overscan_open) { for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_start - overscan_open..for_x_end + overscan_open) { for (x in for_x_start - overscan_open..for_x_end) {
calculateAndAssign(lightmap, x, y) calculateAndAssign(lightmap, x, y)
} }
} }
} }
fun r2() { fun r2() {
// Round 2 // Round 2
for (y in for_y_end + overscan_open downTo for_y_start - overscan_open) { for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_start - overscan_open..for_x_end + overscan_open) { for (x in for_x_start - overscan_open..for_x_end) {
calculateAndAssign(lightmap, x, y) calculateAndAssign(lightmap, x, y)
} }
} }
} }
fun r3() { fun r3() {
// Round 3 // Round 3
for (y in for_y_end + overscan_open downTo for_y_start - overscan_open) { for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_end + overscan_open downTo for_x_start - overscan_open) { for (x in for_x_end + overscan_open downTo for_x_start) {
calculateAndAssign(lightmap, x, y) calculateAndAssign(lightmap, x, y)
} }
} }
} }
fun r4() { fun r4() {
// Round 4 // Round 4
for (y in for_y_start - overscan_open..for_y_end + overscan_open) { for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_end + overscan_open downTo for_x_start - overscan_open) { for (x in for_x_end + overscan_open downTo for_x_start) {
calculateAndAssign(lightmap, x, y) calculateAndAssign(lightmap, x, y)
} }
} }
@@ -311,7 +313,7 @@ object LightmapRenderer {
AppLoader.measureDebugTime("Renderer.LightTotal") { AppLoader.measureDebugTime("Renderer.LightTotal") {
//r1();r2();r3();r4() r3();r4();r1();r2();
// ANECDOTES // ANECDOTES
@@ -322,7 +324,7 @@ object LightmapRenderer {
// per-channel operation for bit more aggressive optimisation // per-channel operation for bit more aggressive optimisation
for (lightsource in lightsourceMap) { /*for (lightsource in lightsourceMap) {
val (lsx, lsy) = LandUtil.resolveBlockAddr(world, lightsource.key) val (lsx, lsy) = LandUtil.resolveBlockAddr(world, lightsource.key)
// lightmap MUST BE PRE-SEEDED from known lightsources! // lightmap MUST BE PRE-SEEDED from known lightsources!
@@ -356,7 +358,7 @@ object LightmapRenderer {
if (skip) break 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
@@ -608,7 +610,7 @@ object LightmapRenderer {
// regarding the issue #26 // regarding the issue #26
try { try {
val fuck = BlockCodex[thisTerrain].luminosity val fuck = BlockCodex[thisTerrain].lumCol
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain") System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain")
@@ -631,13 +633,13 @@ object LightmapRenderer {
if (thisFluid.type != Fluid.NULL) { if (thisFluid.type != Fluid.NULL) {
fluidAmountToCol.set(thisFluid.amount, thisFluid.amount, thisFluid.amount, thisFluid.amount) fluidAmountToCol.set(thisFluid.amount, thisFluid.amount, thisFluid.amount, thisFluid.amount)
thisTileLuminosity.set(BlockCodex[thisTerrain].luminosity) thisTileLuminosity.set(BlockCodex[thisTerrain].lumCol)
thisTileLuminosity.maxAndAssign(BlockCodex[thisFluid.type].luminosity.mul(fluidAmountToCol)) // already been div by four thisTileLuminosity.maxAndAssign(BlockCodex[thisFluid.type].lumCol.mul(fluidAmountToCol)) // already been div by four
thisTileOpacity.set(BlockCodex[thisTerrain].opacity) thisTileOpacity.set(BlockCodex[thisTerrain].opacity)
thisTileOpacity.maxAndAssign(BlockCodex[thisFluid.type].opacity.mul(fluidAmountToCol)) // already been div by four thisTileOpacity.maxAndAssign(BlockCodex[thisFluid.type].opacity.mul(fluidAmountToCol)) // already been div by four
} }
else { else {
thisTileLuminosity.set(BlockCodex[thisTerrain].luminosity) thisTileLuminosity.set(BlockCodex[thisTerrain].lumCol)
thisTileOpacity.set(BlockCodex[thisTerrain].opacity) thisTileOpacity.set(BlockCodex[thisTerrain].opacity)
} }
@@ -662,7 +664,7 @@ object LightmapRenderer {
// regarding the issue #26 // regarding the issue #26
try { try {
val fuck = BlockCodex[thisTerrain].luminosity val fuck = BlockCodex[thisTerrain].lumCol
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain") System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain")

View File

@@ -8,7 +8,7 @@ import java.util.concurrent.locks.ReentrantLock
* *
* Created by minjaesong on 2019-03-12. * Created by minjaesong on 2019-03-12.
*/ */
class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) { class SortedArrayList<T>(initialSize: Int = 10) : Iterable<T> {
val arrayList = ArrayList<T>(initialSize) val arrayList = ArrayList<T>(initialSize)
@@ -24,7 +24,7 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
while (low < high) { while (low < high) {
val mid = (low + high).ushr(1) val mid = (low + high).ushr(1)
if (arrayList[mid] > elem) if ((arrayList[mid] as Comparable<T>).compareTo(elem) > 0)
high = mid high = mid
else else
low = mid + 1 low = mid + 1
@@ -56,7 +56,7 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
val midVal = get(mid) val midVal = get(mid)
if (element > midVal) if ((element as Comparable<T>).compareTo(midVal) > 0)
low = mid + 1 low = mid + 1
else if (element < midVal) else if (element < midVal)
high = mid - 1 high = mid - 1
@@ -100,7 +100,7 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
*/ */
fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R): T? = getOrNull(searchForIndex(searchQuery, searchHow)) fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R): T? = getOrNull(searchForIndex(searchQuery, searchHow))
inline fun iterator() = arrayList.iterator() override fun iterator() = arrayList.iterator()
inline fun forEach(action: (T) -> Unit) = arrayList.forEach(action) inline fun forEach(action: (T) -> Unit) = arrayList.forEach(action)
inline fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action) inline fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action)
@@ -116,42 +116,12 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
inline fun <reified R> filterIsInstance() = arrayList.filterIsInstance<R>() inline fun <reified R> filterIsInstance() = arrayList.filterIsInstance<R>()
/**
* Select one unsorted element from the array and put it onto the sorted spot.
*
* The list must be fully sorted except for that one "renegade", otherwise the operation is undefined behaviour.
*/
private fun sortThisRenegade(index: Int) {
if (
(index == arrayList.lastIndex && arrayList[index - 1] <= arrayList[index]) ||
(index == 0 && arrayList[index] <= arrayList[index + 1]) ||
(arrayList[index - 1] <= arrayList[index] && arrayList[index] <= arrayList[index + 1])
) return
// modified binary search
ReentrantLock().lock {
val renegade = arrayList.removeAt(index)
var low = 0
var high = arrayList.size
while (low < high) {
val mid = (low + high).ushr(1)
if (arrayList[mid] > renegade)
high = mid
else
low = mid + 1
}
arrayList.add(low, renegade)
}
}
/** /**
* Does NOT create copies! * Does NOT create copies!
*/ */
fun toArrayList() = arrayList fun toArrayList() = arrayList
fun clear() = arrayList.clear()
} }
fun <T: Comparable<T>> sortedArrayListOf(vararg elements: T): SortedArrayList<T> { fun <T: Comparable<T>> sortedArrayListOf(vararg elements: T): SortedArrayList<T> {