memoised dynamic luminosity

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

View File

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

View File

@@ -86,13 +86,28 @@ object BlockPropUtil {
// pulsate-related vars
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 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) {
1 -> getTorchFlicker(baseLum)
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
*/
fun getDynamicLumFuncByChan(baseLum: Float, type: Int, chan: Int): Float {
private 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

View File

@@ -16,7 +16,7 @@ import java.util.*
internal class FixtureTikiTorch : FixtureBase(BlockBox(BlockBox.NO_COLLISION, 1, 2)), Luminous {
override var color: Cvec
get() = BlockCodex[Block.TORCH].luminosity
get() = BlockCodex[Block.TORCH].lumCol
set(value) {
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.worlddrawer.LightmapRenderer.convX
import net.torvald.terrarum.worlddrawer.LightmapRenderer.convY
import net.torvald.util.SortedArrayList
import kotlin.math.sign
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 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 {
LightmapHDRMap.invoke()
@@ -245,13 +247,13 @@ object LightmapRenderer {
else
colourNull.cpy()
// 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...
lightlevel.maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
if (!lightlevel.nonZero()) {
// 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()
@@ -269,32 +271,32 @@ object LightmapRenderer {
fun r1() {
// Round 1
for (y in for_y_start - overscan_open..for_y_end + overscan_open) {
for (x in for_x_start - overscan_open..for_x_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) {
calculateAndAssign(lightmap, x, y)
}
}
}
fun r2() {
// Round 2
for (y in for_y_end + overscan_open downTo for_y_start - overscan_open) {
for (x in for_x_start - overscan_open..for_x_end + 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) {
calculateAndAssign(lightmap, x, y)
}
}
}
fun r3() {
// Round 3
for (y in for_y_end + overscan_open downTo for_y_start - overscan_open) {
for (x in for_x_end + overscan_open downTo for_x_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) {
calculateAndAssign(lightmap, x, y)
}
}
}
fun r4() {
// Round 4
for (y in for_y_start - overscan_open..for_y_end + overscan_open) {
for (x in for_x_end + overscan_open downTo for_x_start - 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) {
calculateAndAssign(lightmap, x, y)
}
}
@@ -311,7 +313,7 @@ object LightmapRenderer {
AppLoader.measureDebugTime("Renderer.LightTotal") {
//r1();r2();r3();r4()
r3();r4();r1();r2();
// ANECDOTES
@@ -322,7 +324,7 @@ object LightmapRenderer {
// per-channel operation for bit more aggressive optimisation
for (lightsource in lightsourceMap) {
/*for (lightsource in lightsourceMap) {
val (lsx, lsy) = LandUtil.resolveBlockAddr(world, lightsource.key)
// lightmap MUST BE PRE-SEEDED from known lightsources!
@@ -356,7 +358,7 @@ object LightmapRenderer {
if (skip) break
}
}
}
}*/
}
}
else if (world.worldIndex != -1) { // to avoid updating on the null world
@@ -608,7 +610,7 @@ object LightmapRenderer {
// regarding the issue #26
try {
val fuck = BlockCodex[thisTerrain].luminosity
val fuck = BlockCodex[thisTerrain].lumCol
}
catch (e: NullPointerException) {
System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain")
@@ -631,13 +633,13 @@ object LightmapRenderer {
if (thisFluid.type != Fluid.NULL) {
fluidAmountToCol.set(thisFluid.amount, thisFluid.amount, thisFluid.amount, thisFluid.amount)
thisTileLuminosity.set(BlockCodex[thisTerrain].luminosity)
thisTileLuminosity.maxAndAssign(BlockCodex[thisFluid.type].luminosity.mul(fluidAmountToCol)) // already been div by four
thisTileLuminosity.set(BlockCodex[thisTerrain].lumCol)
thisTileLuminosity.maxAndAssign(BlockCodex[thisFluid.type].lumCol.mul(fluidAmountToCol)) // already been div by four
thisTileOpacity.set(BlockCodex[thisTerrain].opacity)
thisTileOpacity.maxAndAssign(BlockCodex[thisFluid.type].opacity.mul(fluidAmountToCol)) // already been div by four
}
else {
thisTileLuminosity.set(BlockCodex[thisTerrain].luminosity)
thisTileLuminosity.set(BlockCodex[thisTerrain].lumCol)
thisTileOpacity.set(BlockCodex[thisTerrain].opacity)
}
@@ -662,7 +664,7 @@ object LightmapRenderer {
// regarding the issue #26
try {
val fuck = BlockCodex[thisTerrain].luminosity
val fuck = BlockCodex[thisTerrain].lumCol
}
catch (e: NullPointerException) {
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.
*/
class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
class SortedArrayList<T>(initialSize: Int = 10) : Iterable<T> {
val arrayList = ArrayList<T>(initialSize)
@@ -24,7 +24,7 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
while (low < high) {
val mid = (low + high).ushr(1)
if (arrayList[mid] > elem)
if ((arrayList[mid] as Comparable<T>).compareTo(elem) > 0)
high = mid
else
low = mid + 1
@@ -56,7 +56,7 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
val midVal = get(mid)
if (element > midVal)
if ((element as Comparable<T>).compareTo(midVal) > 0)
low = mid + 1
else if (element < midVal)
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))
inline fun iterator() = arrayList.iterator()
override fun iterator() = arrayList.iterator()
inline fun forEach(action: (T) -> Unit) = arrayList.forEach(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>()
/**
* 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!
*/
fun toArrayList() = arrayList
fun clear() = arrayList.clear()
}
fun <T: Comparable<T>> sortedArrayListOf(vararg elements: T): SortedArrayList<T> {