mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
memoised dynamic luminosity
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
15
src/net/torvald/terrarum/tests/SortedArrayListTest.kt
Normal file
15
src/net/torvald/terrarum/tests/SortedArrayListTest.kt
Normal 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) }
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user