diff --git a/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt b/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt index ace81a869..30bd5d5d0 100644 --- a/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt +++ b/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt @@ -278,40 +278,68 @@ object LightmapRenderer { 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 for (y in for_y_end + overscan_open downTo for_y_start) { - // TODO multithread the following for loop duh for (x in for_x_start - overscan_open..for_x_end) { calculateAndAssign(lightmap, x, y) } } // Round 3 for (y in for_y_end + overscan_open downTo for_y_start) { - // TODO multithread the following for loop duh for (x in for_x_end + overscan_open downTo for_x_start) { calculateAndAssign(lightmap, x, y) } } // Round 4 for (y in for_y_start - overscan_open..for_y_end) { - // TODO multithread the following for loop duh for (x in for_x_end + overscan_open downTo for_x_start) { calculateAndAssign(lightmap, x, y) } } // 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) } } + // per-channel version is slower... + /*repeat(4) { channel -> + // Round 1 + 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, channel) + } + } + // Round 2 + 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, channel) + } + } + // Round 3 + 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, channel) + } + } + // Round 4 + 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, channel) + } + } + // Round 1 + 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, channel) + } + } + }*/ // per-channel operation for bit more aggressive optimisation /*for (lightsource in lightsourceMap) { @@ -538,16 +566,23 @@ object LightmapRenderer { } - //private val ambientAccumulator = Cvec(0f,0f,0f,0f) + private val ambientAccumulator = Cvec(0f,0f,0f,0f) private val lightLevelThis = Cvec(0) - private var thisTerrain = 0 - private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f) private val fluidAmountToCol = Cvec(0) - private var thisWall = 0 private val thisTileLuminosity = Cvec(0) private val thisTileOpacity = Cvec(0) private val thisTileOpacity2 = Cvec(0) // thisTileOpacity * sqrt(2) private val sunLight = Cvec(0) + private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f) + private var thisTerrain = 0 + private var thisWall = 0 + + // per-channel variants + private var lightLevelThisCh = 0f + private var fluidAmountToColCh = 0f + private var thisTileLuminosityCh = 0f + private var thisTileOpacityCh = 0f + private var thisTileOpacity2Ch = 0f /** * This function will alter following variables: @@ -612,7 +647,61 @@ object LightmapRenderer { // blend lantern lightLevelThis.maxAndAssign(thisTileLuminosity).maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull) + } + private fun getLightsAndShadesCh(x: Int, y: Int, channel: Int) { + lightLevelThisCh = 0f + thisTerrain = world.getTileFromTerrain(x, y) ?: Block.STONE + thisFluid = world.getFluid(x, y) + thisWall = world.getTileFromWall(x, y) ?: Block.STONE + + // regarding the issue #26 + try { + val fuck = BlockCodex[thisTerrain].luminosity + } + catch (e: NullPointerException) { + System.err.println("## NPE -- x: $x, y: $y, value: $thisTerrain") + e.printStackTrace() + // create shitty minidump + System.err.println("MINIMINIDUMP START") + for (xx in x - 16 until x + 16) { + val raw = world.getTileFromTerrain(xx, y) + val lsb = raw.and(0xff).toString(16).padStart(2, '0') + val msb = raw.ushr(8).and(0xff).toString(16).padStart(2, '0') + System.err.print(lsb) + System.err.print(msb) + System.err.print(" ") + } + System.err.println("\nMINIMINIDUMP END") + + exitProcess(1) + } + + if (thisFluid.type != Fluid.NULL) { + fluidAmountToColCh = thisFluid.amount + + thisTileLuminosityCh = BlockCodex[thisTerrain].getLum(channel) + thisTileLuminosityCh = maxOf(BlockCodex[thisFluid.type].getLum(channel) * fluidAmountToColCh, thisTileLuminosityCh) // already been div by four + thisTileOpacityCh = BlockCodex[thisTerrain].getOpacity(channel) + thisTileOpacityCh = maxOf(BlockCodex[thisFluid.type].getOpacity(channel) * fluidAmountToColCh, thisTileOpacityCh) // already been div by four + } + else { + thisTileLuminosityCh = BlockCodex[thisTerrain].getLum(channel) + thisTileOpacityCh = BlockCodex[thisTerrain].getOpacity(channel) + } + + thisTileOpacity2Ch = thisTileOpacityCh * 1.41421356f + //sunLight.set(world.globalLight); sunLight.mul(DIV_FLOAT) // moved to fireRecalculateEvent() + + + // open air || luminous tile backed by sunlight + if ((thisTerrain == AIR && thisWall == AIR) || (thisTileLuminosityCh > epsilon && thisWall == AIR)) { + lightLevelThisCh = sunLight.getElem(channel) + } + + // blend lantern + lightLevelThisCh = maxOf(thisTileLuminosityCh, lightLevelThisCh) + lightLevelThisCh = maxOf(lanternMap[LandUtil.getBlockAddr(world, x, y)]?.getElem(channel) ?: 0f, lightLevelThisCh) } private val inNoopMaskp = Point2i(0,0) @@ -706,6 +795,41 @@ object LightmapRenderer { lightmap.setA(x, y, lightLevelThis.a) } + // per-channel version is slower... + /*private fun calculateAndAssign(lightmap: UnsafeCvecArray, worldX: Int, worldY: Int, channel: Int) { + + if (inNoopMask(worldX, worldY)) return + + // O(9n) == O(n) where n is a size of the map + + getLightsAndShadesCh(worldX, worldY, channel) + + val x = worldX.convX() + val y = worldY.convY() + + // calculate ambient + /* + * + 0 4 1 + * * @ * 6 @ 7 + * + * + 2 5 3 + * sample ambient for eight points and apply attenuation for those + * maxblend eight values and use it + */ + + // will "overwrite" what's there in the lightmap if it's the first pass + // 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)) + + lightLevelThisCh = maxOf(darken(x, y - 1, thisTileOpacityCh, channel), lightLevelThisCh) + lightLevelThisCh = maxOf(darken(x, y + 1, thisTileOpacityCh, channel), lightLevelThisCh) + lightLevelThisCh = maxOf(darken(x - 1, y, thisTileOpacityCh, channel), lightLevelThisCh) + lightLevelThisCh = maxOf(darken(x + 1, y, thisTileOpacityCh, channel), lightLevelThisCh) + + lightmap.channelSet(x, y, channel, lightLevelThisCh) + }*/ + private fun isSolid(x: Int, y: Int): Float? { // ...so that they wouldn't appear too dark if (!inBounds(x, y)) return null @@ -823,6 +947,11 @@ object LightmapRenderer { } + private fun darken(x: Int, y: Int, darken: Float, channel: Int): Float { + if (x !in 0 until LIGHTMAP_WIDTH || y !in 0 until LIGHTMAP_HEIGHT) return 0f + return lightmap.channelGet(x, y, channel) * (1f - darken * lightScalingMagic) + } + /** infix is removed to clarify the association direction */ private fun Cvec.maxAndAssign(other: Cvec): Cvec { // TODO investigate: if I use assignment instead of set(), it blackens like the vector branch. --Torvald, 2019-06-07