Compare commits

...

4 Commits

Author SHA1 Message Date
minjaesong
82d675fd59 hooey indexing, dont know why 2019-05-10 00:32:49 +09:00
minjaesong
bda2f7ba82 wip3 2019-05-09 16:14:49 +09:00
Minjae Song
dfdd0ef411 wip2 2019-05-09 12:09:48 +09:00
minjaesong
4b3736cfa7 wip lets precalc and recycle numbers 2019-05-09 02:53:48 +09:00
3 changed files with 143 additions and 30 deletions

View File

@@ -93,6 +93,13 @@ public class Color {
this.a = a;
}
public Color (float c) {
this.r = c;
this.g = c;
this.b = c;
this.a = c;
}
/** Constructs a new color using the given color
*
* @param color the color */

View File

@@ -16,8 +16,7 @@ class BlockProp {
var shadeColG = 0f
var shadeColB = 0f
var shadeColA = 0f
lateinit var opacity: Color
var opacity: Color = Color(0)
var strength: Int = 0
var density: Int = 0
@@ -36,7 +35,7 @@ class BlockProp {
var lumColG = 0f
var lumColB = 0f
var lumColA = 0f
lateinit var internalLumCol: Color
var internalLumCol: Color = Color(0)
/**
* @param luminosity

View File

@@ -14,7 +14,6 @@ import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.realestate.LandUtil
@@ -34,6 +33,7 @@ import net.torvald.terrarum.realestate.LandUtil
*/
object LightmapRenderer {
private const val TILE_SIZE = CreateTileAtlas.TILE_SIZE
private const val SQRT2 = 1.41421356f
private var world: GameWorld = GameWorld.makeNullWorld()
private lateinit var lightCalcShader: ShaderProgram
@@ -83,7 +83,51 @@ object LightmapRenderer {
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
//private val lightmap: Array<Array<Color>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH, { Color(0f,0f,0f,0f) }) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
private val lightmap: Array<Color> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0f,0f,0f,0f) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
private val lanternMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
/**
* Sstores both the block light sources and actor light sources.
*/
//private val lightSourcesMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
private val lightSourcesMap = Array<Color>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0) }
private fun toLightOffset(y: Int, x: Int): Int {
val xpos = x - for_x_start + overscan_open
val ypos = y - for_y_start + overscan_open
val index = ypos * LIGHTMAP_WIDTH + xpos
if (index >= lightSourcesMap.size)
println("$x, $y | $xpos, $ypos | $for_x_start, $for_y_start | $index")
return index
}
private fun getLightSourceMap(worldX: Int, worldY: Int) =
lightSourcesMap[toLightOffset(worldX, worldY)]
private fun setLightSourcesMap(worldX: Int, worldY: Int, value: Color) {
lightSourcesMap[toLightOffset(worldX, worldY)] = value
}
private fun mixLightSourcesMap(worldX: Int, worldY: Int, value: Color) {
lightSourcesMap[toLightOffset(worldX, worldY)].maxAndAssign(value)
}
private fun clearLightSourcesOffset() {
for (i in 0 until lightSourcesMap.size) { lightSourcesMap[i] = Color(0) }
}
/**
* Pair of: Regular shade, the former shade times 1.4142
*/
//private val shadesMap = HashMap<BlockAddress, Pair<Color, Color>>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
private val shadesMap = Array<Pair<Color, Color>>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0) to Color(0) }
private fun getShadesMap(worldX: Int, worldY: Int) =
shadesMap[toLightOffset(worldX, worldY)]
private fun setShadesMap(worldX: Int, worldY: Int, value: Color) {
shadesMap[toLightOffset(worldX, worldY)] = value to (value.cpy().mul(SQRT2))
}
private fun mixShadesMap(worldX: Int, worldY: Int, value: Color) {
val field = shadesMap[toLightOffset(worldX, worldY)]
val baseval = field.first.maxAndAssign(value)
field.second.set(baseval); field.second.mul(SQRT2)
}
private fun clearShadesMap() {
for (i in 0 until shadesMap.size) { shadesMap[i] = Color(0) to Color(0) }
}
init {
printdbg(this, "Overscan open: $overscan_open; opaque: $overscan_opaque")
@@ -201,8 +245,15 @@ object LightmapRenderer {
//println("$for_x_start..$for_x_end, $for_x\t$for_y_start..$for_y_end, $for_y")
AppLoader.measureDebugTime("Renderer.Lanterns") {
buildLanternmap(actorContainers)
// set sunlight
sunLight.set(world.globalLight); sunLight.mul(DIV_FLOAT)
AppLoader.measureDebugTime("Renderer.LightPreload") {
// this is to recycle pre-calculated lights and shades for all 4 rounds.
// the old code always re-calculates them (calls 'getLightsAndShades()') for every blocks for every round.
// the light source information can also be used to create no-op mask? I'm sceptical about that, there must
// exist some edge cases like the other time...
buildLightSourcesMap(actorContainers)
} // usually takes 3000 ns
/*
@@ -219,9 +270,6 @@ object LightmapRenderer {
* If you run only 4 sets, orthogonal/diagonal artefacts are bound to occur,
*/
// set sunlight
sunLight.set(world.globalLight); sunLight.mul(DIV_FLOAT)
// set no-op mask from solidity of the block
AppLoader.measureDebugTime("Renderer.LightNoOpMask") {
noopMask.clear()
@@ -245,6 +293,7 @@ object LightmapRenderer {
// The skipping is dependent on how you get ambient light,
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
AppLoader.measureDebugTime("Renderer.LightTotal") {
// Round 2
for (y in for_y_end + overscan_open downTo for_y_start) {
@@ -331,8 +380,22 @@ object LightmapRenderer {
internal data class ThreadedLightmapUpdateMessage(val x: Int, val y: Int)
private fun buildLanternmap(actorContainers: Array<out List<ActorWithBody>?>) {
lanternMap.clear()
private var _block = 0
private var _blockProp = BlockCodex[0]
private var _wall = 0
private var _blockLum = Color(0)
private var _fluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
private var _fluidProp = BlockCodex[_fluid.type]
private var _tileAddr = 0L
private var _fluidAmountToCol = Color(0)
private fun buildLightSourcesMap(actorContainers: Array<out List<ActorWithBody>?>) {
clearLightSourcesOffset()
clearShadesMap()
//lightSourcesMapOffset.set(for_x_start - overscan_open, for_y_start + overscan_open)
// lanterns from actors
actorContainers.forEach { actorContainer ->
actorContainer?.forEach {
if (it is Luminous && it is ActorWBMovable) {
@@ -349,17 +412,55 @@ object LightmapRenderer {
val normalisedColor = it.color//.cpy().mul(DIV_FLOAT)
lanternMap[LandUtil.getBlockAddr(world, x, y)] = normalisedColor
//lanternMap[Point2i(x, y)] = normalisedColor
//lightSourcesMap[LandUtil.getBlockAddr(world, x, y)] = normalisedColor
setLightSourcesMap(x, y, normalisedColor)
//lightSourcesMap[Point2i(x, y)] = normalisedColor
// Q&D fix for Roundworld anomaly
//lanternMap[Point2i(x + world.width, y)] = normalisedColor
//lanternMap[Point2i(x - world.width, y)] = normalisedColor
//lightSourcesMap[Point2i(x + world.width, y)] = normalisedColor
//lightSourcesMap[Point2i(x - world.width, y)] = normalisedColor
}
}
}
}
}
}
// light sources and shades from a block
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) {
_block = world.getTileFromTerrain(x, y) ?: Block.STONE
_blockProp = BlockCodex[_block]
_wall = world.getTileFromWall(x, y) ?: Block.STONE
_blockLum = _blockProp.luminosity
_fluid = world.getFluid(x, y)
_fluidProp = BlockCodex[_fluid.type]
_tileAddr = LandUtil.getBlockAddr(world, x, y)
_fluidAmountToCol = Color(_fluid.amount.coerceIn(0f, 1f))
// light sources from blocks //
// mix with the existing value
mixLightSourcesMap(x, y, _blockLum)
// see if sunlight is applicable. If it does, mix with the existing value
if ((_block == AIR && _wall == AIR) || (_blockLum.nonZero() && _wall == AIR)) {
mixLightSourcesMap(x, y, sunLight)
}
// mix the lava light
if (_fluid.type != Fluid.NULL) {
mixLightSourcesMap(x, y, _fluidProp.luminosity mul _fluidAmountToCol)
}
// deal with the shades //
// shade from the block
setShadesMap(x, y, _blockProp.opacity)
// shade from the fluid
mixShadesMap(x, y, _fluidProp.opacity mul _fluidAmountToCol)
}
}
}
private fun buildNoopMask() {
@@ -394,14 +495,14 @@ object LightmapRenderer {
//private val ambientAccumulator = Color(0f,0f,0f,0f)
private val lightLevelThis = Color(0)
private var thisTerrain = 0
private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
private val fluidAmountToCol = Color(0)
private var thisWall = 0
private val thisTileLuminosity = Color(0)
private val thisTileOpacity = Color(0)
private val thisTileOpacity2 = Color(0) // thisTileOpacity * sqrt(2)
//private val lightLevelThis = Color(0)
//private var thisTerrain = 0
//private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
//private val fluidAmountToCol = Color(0)
//private var thisWall = 0
//private val thisTileLuminosity = Color(0)
//private val thisTileOpacity = Color(0)
//private val thisTileOpacity2 = Color(0) // thisTileOpacity * sqrt(2)
private val sunLight = Color(0)
/**
@@ -415,7 +516,10 @@ object LightmapRenderer {
* - thisTileOpacity2
* - sunlight
*/
private fun getLightsAndShades(x: Int, y: Int) {
/*private fun getLightsAndShades(x: Int, y: Int) {
// TODO lanternmap now also holds light sources (incl. sunlight)
lightLevelThis.set(colourNull)
thisTerrain = world.getTileFromTerrain(x, y) ?: Block.STONE
thisFluid = world.getFluid(x, y)
@@ -444,9 +548,9 @@ object LightmapRenderer {
}
// blend lantern
lightLevelThis.maxAndAssign(thisTileLuminosity).maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
lightLevelThis.maxAndAssign(thisTileLuminosity).maxAndAssign(lightSourcesMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
}
}*/
private val inNoopMaskp = Point2i(0,0)
@@ -505,7 +609,9 @@ object LightmapRenderer {
// O(9n) == O(n) where n is a size of the map
getLightsAndShades(x, y)
//getLightsAndShades(x, y)
val lightLevelThis = getLightSourceMap(x, y) // it HAS to be a cpy()...?, otherwise all cells gets the same instance
val (thisTileOpacity, thisTileOpacity2) = getShadesMap(x, y)
// calculate ambient
/* + * + 0 4 1
@@ -516,6 +622,7 @@ object LightmapRenderer {
*/
// will "overwrite" what's there in the lightmap if it's the first pass
// using "map"s actually makes it slower. Guess I'll keep it dirty...
// takes about 2 ms on 6700K
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x - 1, y - 1) ?: colourNull, thisTileOpacity2))
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x + 1, y - 1) ?: colourNull, thisTileOpacity2))
@@ -528,7 +635,7 @@ object LightmapRenderer {
//return lightLevelThis.cpy() // it HAS to be a cpy(), otherwise all cells gets the same instance
setLightOf(lightmap, x, y, lightLevelThis.cpy())
setLightOf(lightmap, x, y, lightLevelThis)
}
private fun getLightForOpaque(x: Int, y: Int): Color? { // ...so that they wouldn't appear too dark
@@ -887,4 +994,4 @@ object LightmapRenderer {
}
fun Color.toRGBA() = (255 * r).toInt() shl 24 or ((255 * g).toInt() shl 16) or ((255 * b).toInt() shl 8) or (255 * a).toInt()
//fun Color(c: Float) = Color(c, c, c, c)