sorta working unsafesvecarray; and then issue #26 is fucking shit up

This commit is contained in:
minjaesong
2019-06-22 04:16:03 +09:00
parent 79b317d9e1
commit 88a288243c
7 changed files with 283 additions and 78 deletions

2
.idea/misc.xml generated
View File

@@ -38,7 +38,7 @@
<property name="caretWidth" class="java.lang.Integer" /> <property name="caretWidth" class="java.lang.Integer" />
</properties> </properties>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@@ -0,0 +1,66 @@
package net.torvald
import sun.misc.Unsafe
/**
* Created by minjaesong on 2019-06-21.
*/
object UnsafeHelper {
internal val unsafe: Unsafe
init {
val unsafeConstructor = Unsafe::class.java.getDeclaredConstructor()
unsafeConstructor.isAccessible = true
unsafe = unsafeConstructor.newInstance()
}
fun allocate(size: Long): UnsafePtr {
val ptr = unsafe.allocateMemory(size)
return UnsafePtr(ptr, size)
}
}
/**
* To allocate a memory, use UnsafeHelper.allocate(long)
*/
class UnsafePtr(val ptr: Long, val allocSize: Long) {
var destroyed = false
private set
fun destroy() {
if (!destroyed) {
UnsafeHelper.unsafe.freeMemory(ptr)
destroyed = true
}
}
private inline fun checkNullPtr(index: Long) {
if (destroyed) throw NullPointerException()
// OOB Check: debugging purposes only -- comment out for the production
//if (index !in 0 until allocSize) throw NullPointerException("Out of bounds: $index; alloc size: $allocSize")
}
operator fun get(index: Long): Byte {
checkNullPtr(index)
return UnsafeHelper.unsafe.getByte(ptr + index)
}
fun getFloat(index: Long): Float {
checkNullPtr(index)
return UnsafeHelper.unsafe.getFloat(ptr + index)
}
operator fun set(index: Long, value: Byte) {
checkNullPtr(index)
UnsafeHelper.unsafe.putByte(ptr + index, value)
}
fun setFloat(index: Long, value: Float) {
checkNullPtr(index)
UnsafeHelper.unsafe.putFloat(ptr + index, value)
}
}

View File

@@ -0,0 +1,41 @@
package net.torvald.gdx.graphics
import net.torvald.UnsafeHelper
/**
* Created by minjaesong on 2019-06-21.
*/
internal class UnsafeCvecArray(val width: Int, val height: Int) {
val TOTAL_SIZE_IN_BYTES = 16L * width * height
val array = UnsafeHelper.allocate(TOTAL_SIZE_IN_BYTES)
private inline fun toAddr(x: Int, y: Int) = 16L * (y * width + x)
fun zerofill() = UnsafeHelper.unsafe.setMemory(this.array.ptr, TOTAL_SIZE_IN_BYTES, 0)
init {
zerofill()
}
fun getR(x: Int, y: Int) = array.getFloat(toAddr(x, y))
fun getG(x: Int, y: Int) = array.getFloat(toAddr(x, y) + 4)
fun getB(x: Int, y: Int) = array.getFloat(toAddr(x, y) + 8)
fun getA(x: Int, y: Int) = array.getFloat(toAddr(x, y) + 12)
fun setR(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y), value) }
fun setG(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 4, value) }
fun setB(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 8, value) }
fun setA(x: Int, y: Int, value: Float) { array.setFloat(toAddr(x, y) + 12, value) }
fun max(x: Int, y: Int, other: Cvec) {
setR(x, y, maxOf(getR(x, y), other.r))
setG(x, y, maxOf(getG(x, y), other.g))
setB(x, y, maxOf(getB(x, y), other.b))
setA(x, y, maxOf(getA(x, y), other.a))
}
fun destroy() = this.array.destroy()
}

View File

@@ -22,7 +22,7 @@ open class BlockLayer(val width: Int, val height: Int) : Disposable {
private var layerPtr = unsafe.allocateMemory(width * height * BYTES_PER_BLOCK.toLong()) private var layerPtr = unsafe.allocateMemory(width * height * BYTES_PER_BLOCK.toLong())
init { init {
unsafe.setMemory(layerPtr, width * height * BYTES_PER_BLOCK.toLong(), 0) // sometimes does not work?! unsafe.setMemory(layerPtr, width * height * BYTES_PER_BLOCK.toLong(), 0) // does reliably fill the memory with zeroes
} }
/** /**
@@ -89,12 +89,9 @@ open class BlockLayer(val width: Int, val height: Int) : Disposable {
} }
override fun next(): Byte { override fun next(): Byte {
val y = iteratorCount / width
val x = iteratorCount % width
// advance counter
iteratorCount += 1 iteratorCount += 1
return unsafe.getByte(layerPtr + 1) return unsafe.getByte(layerPtr + iteratorCount)
} }
} }
} }

View File

@@ -165,17 +165,13 @@ open class GameWorld : Disposable {
private fun coerceXY(x: Int, y: Int) = (x fmod width) to (y.coerceIn(0, height - 1)) private fun coerceXY(x: Int, y: Int) = (x fmod width) to (y.coerceIn(0, height - 1))
fun getTileFromWall(x: Int, y: Int): Int { fun getTileFromWall(rawX: Int, rawY: Int): Int {
val (x, y) = coerceXY(x, y) val (x, y) = coerceXY(rawX, rawY)
if (y !in 0 until height) throw Error("Y coord out of world boundary: $y")
return layerWall.unsafeGetTile(x, y) return layerWall.unsafeGetTile(x, y)
} }
fun getTileFromTerrain(x: Int, y: Int): Int { fun getTileFromTerrain(rawX: Int, rawY: Int): Int {
val (x, y) = coerceXY(x, y) val (x, y) = coerceXY(rawX, rawY)
if (y !in 0 until height) throw Error("Y coord out of world boundary: $y")
return layerTerrain.unsafeGetTile(x, y) return layerTerrain.unsafeGetTile(x, y)
} }

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum.tests
import net.torvald.terrarum.gameworld.toUint
import sun.misc.Unsafe
/**
* Created by minjaesong on 2019-06-22.
*/
class UnsafeTest {
private val unsafe: Unsafe
init {
val unsafeConstructor = Unsafe::class.java.getDeclaredConstructor()
unsafeConstructor.isAccessible = true
unsafe = unsafeConstructor.newInstance()
}
private val memsize = 2048L // must be big enough value so that your OS won't always return zero-filled pieces
fun main() {
val ptr = unsafe.allocateMemory(memsize)
printDump(ptr)
unsafe.setMemory(ptr, memsize, 0x00.toByte())
printDump(ptr)
for (k in 0 until memsize step 4) {
unsafe.putInt(ptr + k, 0xcafebabe.toInt())
}
printDump(ptr)
unsafe.freeMemory(ptr)
}
fun printDump(ptr: Long) {
println("MINIMINIDUMP START")
for (i in 0 until memsize) {
val b = unsafe.getByte(ptr + i).toUint().toString(16).padStart(2, '0')
print("$b ")
}
println("\nMINIMINIDUMP END")
}
}
fun main(args: Array<String>) {
UnsafeTest().main()
}

View File

@@ -6,13 +6,14 @@ import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.graphics.glutils.ShaderProgram
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.gdx.graphics.UnsafeCvecArray
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.concurrent.ThreadParallel import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameactors.ActorWBMovable import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.Luminous import net.torvald.terrarum.gameactors.Luminous
@@ -47,16 +48,18 @@ object LightmapRenderer {
if (this.world != world) { if (this.world != world) {
printdbg(this, "World change detected -- old world: ${this.world.hashCode()}, new world: ${world.hashCode()}") printdbg(this, "World change detected -- old world: ${this.world.hashCode()}, new world: ${world.hashCode()}")
for (y in 0 until LIGHTMAP_HEIGHT) { /*for (y in 0 until LIGHTMAP_HEIGHT) {
for (x in 0 until LIGHTMAP_WIDTH) { for (x in 0 until LIGHTMAP_WIDTH) {
lightmap[y][x] = colourNull lightmap[y][x] = colourNull
} }
} }*/
/*for (i in 0 until lightmap.size) { /*for (i in 0 until lightmap.size) {
lightmap[i] = colourNull lightmap[i] = colourNull
}*/ }*/
lightmap.zerofill()
makeUpdateTaskList() makeUpdateTaskList()
} }
} }
@@ -84,7 +87,8 @@ object LightmapRenderer {
*/ */
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light) // it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
// will use array of array from now on because fuck it; debug-ability > slight framerate drop. 2019-06-01 // will use array of array from now on because fuck it; debug-ability > slight framerate drop. 2019-06-01
private var lightmap: Array<Array<Cvec>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH) { Cvec(0) } } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4. private var lightmap: UnsafeCvecArray = UnsafeCvecArray(LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT)
//private var lightmap: Array<Array<Cvec>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH) { 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 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)
@@ -124,15 +128,34 @@ object LightmapRenderer {
* @param y world tile coord * @param y world tile coord
*/ */
internal fun getLight(x: Int, y: Int): Cvec? { internal fun getLight(x: Int, y: Int): Cvec? {
val col = getLightInternal(x, y) if (!inBounds(x, y)) {
if (col == null) {
return null return null
} }
else { else {
return Cvec(col.r * MUL_FLOAT, col.g * MUL_FLOAT, col.b * MUL_FLOAT, col.a * MUL_FLOAT) val x = x.convX()
val y = y.convY()
return Cvec(
lightmap.getR(x, y) * MUL_FLOAT,
lightmap.getG(x, y) * MUL_FLOAT,
lightmap.getB(x, y) * MUL_FLOAT,
lightmap.getA(x, y) * MUL_FLOAT
)
} }
} }
/**
* @param x world coord
* @param y world coord
*/
private fun inBounds(x: Int, y: Int) =
(y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH)
/** World coord to array coord */
private inline fun Int.convX() = this - for_x_start + overscan_open
/** World coord to array coord */
private inline fun Int.convY() = this - for_y_start + overscan_open
/** /**
* Internal level (0..1) * Internal level (0..1)
* *
@@ -140,7 +163,7 @@ object LightmapRenderer {
* @param y world tile coord * @param y world tile coord
*/ */
// TODO in regard of "colour math against integers", return Int? // TODO in regard of "colour math against integers", return Int?
private fun getLightInternal(x: Int, y: Int): Cvec? { /*private fun getLightInternal(x: Int, y: Int): Cvec? {
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT && if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) { x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
@@ -153,7 +176,7 @@ object LightmapRenderer {
} }
return null return null
} }*/
/** /**
@@ -168,7 +191,7 @@ object LightmapRenderer {
* @param colour Cvec to write * @param colour Cvec to write
* @param applyFun A function ```foo(old_colour, given_colour)``` * @param applyFun A function ```foo(old_colour, given_colour)```
*/ */
private fun setLightOf(list: Array<Array<Cvec>>, x: Int, y: Int, colour: Cvec, applyFun: (Cvec, Cvec) -> Cvec = { _, c -> c }) { /*private fun setLightOf(list: Array<Array<Cvec>>, x: Int, y: Int, colour: Cvec, applyFun: (Cvec, Cvec) -> Cvec = { _, c -> c }) {
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT && if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) { x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
@@ -179,7 +202,7 @@ object LightmapRenderer {
lightmap[ypos][xpos] = applyFun.invoke(list[ypos][xpos], colour) lightmap[ypos][xpos] = applyFun.invoke(list[ypos][xpos], colour)
//list[ypos * LIGHTMAP_WIDTH + xpos] = applyFun.invoke(list[ypos * LIGHTMAP_WIDTH + xpos], colour) //list[ypos * LIGHTMAP_WIDTH + xpos] = applyFun.invoke(list[ypos * LIGHTMAP_WIDTH + xpos], colour)
} }
} }*/
internal fun fireRecalculateEvent(vararg actorContainers: List<ActorWithBody>?) { internal fun fireRecalculateEvent(vararg actorContainers: List<ActorWithBody>?) {
try { try {
@@ -238,9 +261,10 @@ object LightmapRenderer {
// wipe out lightmap // wipe out lightmap
AppLoader.measureDebugTime("Renderer.Light0") { AppLoader.measureDebugTime("Renderer.Light0") {
//for (k in 0 until lightmap.size) lightmap[k] = colourNull //for (k in 0 until lightmap.size) lightmap[k] = colourNull
for (y in 0 until lightmap.size) for (x in 0 until lightmap[0].size) lightmap[y][x] = colourNull //for (y in 0 until lightmap.size) for (x in 0 until lightmap[0].size) lightmap[y][x] = colourNull
// when disabled, light will "decay out" instead of "instantly out", which can have a cool effect // when disabled, light will "decay out" instead of "instantly out", which can have a cool effect
// but the performance boost is measly 0.1 ms on 6700K // but the performance boost is measly 0.1 ms on 6700K
lightmap.zerofill()
} }
// O((5*9)n) == O(n) where n is a size of the map. // O((5*9)n) == O(n) where n is a size of the map.
// Because of inevitable overlaps on the area, it only works with MAX blend // Because of inevitable overlaps on the area, it only works with MAX blend
@@ -404,7 +428,7 @@ object LightmapRenderer {
BlockCodex[world.getTileFromTerrain(x, y)].isSolid BlockCodex[world.getTileFromTerrain(x, y)].isSolid
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
System.err.println("Invalid block id ${world.getTileFromTerrain(x, y)} from coord ($x, $y)") System.err.println("[LightmapRendererNew.buildNoopMask] Invalid block id ${world.getTileFromTerrain(x, y)} from coord ($x, $y)")
e.printStackTrace() e.printStackTrace()
false false
@@ -466,6 +490,28 @@ object LightmapRenderer {
thisFluid = world.getFluid(x, y) thisFluid = world.getFluid(x, y)
thisWall = world.getTileFromWall(x, y) ?: Block.STONE 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")
System.exit(1)
}
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)
@@ -544,13 +590,16 @@ object LightmapRenderer {
/** /**
* Calculates the light simulation, using main lightmap as one of the input. * Calculates the light simulation, using main lightmap as one of the input.
*/ */
private fun calculateAndAssign(lightmap: Array<Array<Cvec>>, x: Int, y: Int) { private fun calculateAndAssign(lightmap: UnsafeCvecArray, worldX: Int, worldY: Int) {
if (inNoopMask(x, y)) return if (inNoopMask(worldX, worldY)) return
// O(9n) == O(n) where n is a size of the map // O(9n) == O(n) where n is a size of the map
getLightsAndShades(x, y) getLightsAndShades(worldX, worldY)
val x = worldX.convX()
val y = worldY.convY()
// calculate ambient // calculate ambient
/* + * + 0 4 1 /* + * + 0 4 1
@@ -562,41 +611,36 @@ object LightmapRenderer {
// will "overwrite" what's there in the lightmap if it's the first pass // will "overwrite" what's there in the lightmap if it's the first pass
// takes about 2 ms on 6700K // takes about 2 ms on 6700K
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x - 1, y - 1) ?: colourNull, thisTileOpacity2)) /* + */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y - 1, thisTileOpacity2))
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x + 1, y - 1) ?: colourNull, thisTileOpacity2)) /* + */lightLevelThis.maxAndAssign(darkenColoured(x + 1, y - 1, thisTileOpacity2))
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x - 1, y + 1) ?: colourNull, thisTileOpacity2)) /* + */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y + 1, thisTileOpacity2))
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x + 1, y + 1) ?: colourNull, thisTileOpacity2)) /* + */lightLevelThis.maxAndAssign(darkenColoured(x + 1, y + 1, thisTileOpacity2))
/* * */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x, y - 1) ?: colourNull, thisTileOpacity)) /* * */lightLevelThis.maxAndAssign(darkenColoured(x, y - 1, thisTileOpacity))
/* * */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x, y + 1) ?: colourNull, thisTileOpacity)) /* * */lightLevelThis.maxAndAssign(darkenColoured(x, y + 1, thisTileOpacity))
/* * */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x - 1, y) ?: colourNull, thisTileOpacity)) /* * */lightLevelThis.maxAndAssign(darkenColoured(x - 1, y, thisTileOpacity))
/* * */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x + 1, y) ?: colourNull, thisTileOpacity)) /* * */lightLevelThis.maxAndAssign(darkenColoured(x + 1, y, thisTileOpacity))
//return lightLevelThis.cpy() // it HAS to be a cpy(), otherwise all cells gets the same instance //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.cpy())
lightmap.setR(x, y, lightLevelThis.r)
lightmap.setG(x, y, lightLevelThis.g)
lightmap.setB(x, y, lightLevelThis.b)
lightmap.setA(x, y, lightLevelThis.a)
} }
private fun getLightForOpaque(x: Int, y: Int): Cvec? { // ...so that they wouldn't appear too dark private fun isSolid(x: Int, y: Int): Float? { // ...so that they wouldn't appear too dark
val l = getLightInternal(x, y) if (!inBounds(x, y)) return null
if (l == null) return null
// brighten if solid // brighten if solid
if (BlockCodex[world.getTileFromTerrain(x, y)].isSolid) { return if (BlockCodex[world.getTileFromTerrain(x, y)].isSolid) 1.2f else 1f
return Cvec(
(l.r * 1.2f),
(l.g * 1.2f),
(l.b * 1.2f),
(l.a * 1.2f)
)
}
else {
return l
}
} }
var lightBuffer: Pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888) var lightBuffer: Pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888)
private val colourNull = Cvec(0) private val colourNull = Cvec(0)
private val gdxColorNull = Color(0)
private val epsilon = 1f/1024f private val epsilon = 1f/1024f
private var _lightBufferAsTex: Texture = Texture(1, 1, Pixmap.Format.RGBA8888) private var _lightBufferAsTex: Texture = Texture(1, 1, Pixmap.Format.RGBA8888)
@@ -626,7 +670,20 @@ object LightmapRenderer {
for (x in this_x_start..this_x_end) { for (x in this_x_start..this_x_end) {
val color = (getLightForOpaque(x, y) ?: Cvec(0f, 0f, 0f, 0f)).normaliseToHDR() val solidMultMagic = isSolid(x, y)
val arrayX = x.convX()
val arrayY = y.convY()
val color = if (solidMultMagic == null)
gdxColorNull
else
Color(
lightmap.getR(arrayX, arrayY) * solidMultMagic,
lightmap.getG(arrayX, arrayY) * solidMultMagic,
lightmap.getB(arrayX, arrayY) * solidMultMagic,
lightmap.getA(arrayX, arrayY) * solidMultMagic
).normaliseToHDR()
lightBuffer.setColor(color) lightBuffer.setColor(color)
@@ -661,34 +718,30 @@ object LightmapRenderer {
/** /**
* Subtract each channel's RGB value. * Subtract each channel's RGB value.
* *
* @param data Raw channel value (0-255) per channel * @param x array coord
* @param y array coord
* @param darken (0-255) per channel * @param darken (0-255) per channel
* @return darkened data (0-255) per channel * @return darkened data (0-255) per channel
*/ */
fun darkenColoured(data: Cvec, darken: Cvec): Cvec { fun darkenColoured(x: Int, y: Int, darken: Cvec): Cvec {
// use equation with magic number 8.0 // use equation with magic number 8.0
// this function, when done recursively (A_x = darken(A_x-1, C)), draws exponential curve. (R^2 = 1) // this function, when done recursively (A_x = darken(A_x-1, C)), draws exponential curve. (R^2 = 1)
return Cvec( /*return Cvec(
data.r * (1f - darken.r * lightScalingMagic),//.clampZero(), data.r * (1f - darken.r * lightScalingMagic),//.clampZero(),
data.g * (1f - darken.g * lightScalingMagic),//.clampZero(), data.g * (1f - darken.g * lightScalingMagic),//.clampZero(),
data.b * (1f - darken.b * lightScalingMagic),//.clampZero(), data.b * (1f - darken.b * lightScalingMagic),//.clampZero(),
data.a * (1f - darken.a * lightScalingMagic)) data.a * (1f - darken.a * lightScalingMagic))*/
}
/** if (x !in 0 until LIGHTMAP_WIDTH || y !in 0 until LIGHTMAP_HEIGHT) return colourNull
* Darken each channel by 'darken' argument
* return Cvec(
* @param data Raw channel value (0-255) per channel lightmap.getR(x, y) * (1f - darken.r * lightScalingMagic),
* @param darken (0-255) lightmap.getG(x, y) * (1f - darken.g * lightScalingMagic),
* @return lightmap.getB(x, y) * (1f - darken.b * lightScalingMagic),
*/ lightmap.getA(x, y) * (1f - darken.a * lightScalingMagic)
fun darkenUniformInt(data: Cvec, darken: Float): Cvec { )
if (darken < 0 || darken > CHANNEL_MAX)
throw IllegalArgumentException("darken: out of range ($darken)")
val darkenColoured = Cvec(darken, darken, darken, darken)
return darkenColoured(data, darkenColoured)
} }
/** /**
@@ -768,7 +821,8 @@ object LightmapRenderer {
_init = true _init = true
} }
lightBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGBA8888) lightBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGBA8888)
lightmap = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH) { Cvec(0) } } lightmap.destroy()
lightmap = UnsafeCvecArray(LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT)
//lightmap = Array<Cvec>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Cvec(0) } //lightmap = Array<Cvec>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Cvec(0) }
@@ -843,7 +897,7 @@ object LightmapRenderer {
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f // isn't it beautiful? 1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f // isn't it beautiful?
) )
/** To eliminated visible edge on the gradient when 255/1023 is exceeded */ /** To eliminated visible edge on the gradient when 255/1023 is exceeded */
internal fun Cvec.normaliseToHDR() = Color( internal fun Color.normaliseToHDR() = Color(
hdr(this.r.coerceIn(0f, 1f)), hdr(this.r.coerceIn(0f, 1f)),
hdr(this.g.coerceIn(0f, 1f)), hdr(this.g.coerceIn(0f, 1f)),
hdr(this.b.coerceIn(0f, 1f)), hdr(this.b.coerceIn(0f, 1f)),
@@ -864,12 +918,14 @@ object LightmapRenderer {
for (y in overscan_open..render_height + overscan_open + 1) { for (y in overscan_open..render_height + overscan_open + 1) {
for (x in overscan_open..render_width + overscan_open + 1) { for (x in overscan_open..render_width + overscan_open + 1) {
try { try {
val colour = lightmap[y][x] //val colour = lightmap[y][x]
//val colour = lightmap[y * LIGHTMAP_WIDTH + x] //val colour = lightmap[y * LIGHTMAP_WIDTH + x]
reds[minOf(CHANNEL_MAX, colour.r.times(MUL).floorInt())] += 1 val x = x.convX()
greens[minOf(CHANNEL_MAX, colour.g.times(MUL).floorInt())] += 1 val y = y.convY()
blues[minOf(CHANNEL_MAX, colour.b.times(MUL).floorInt())] += 1 //reds[minOf(CHANNEL_MAX, lightmap.getR(x, y).times(MUL).floorInt())] += 1
uvs[minOf(CHANNEL_MAX, colour.a.times(MUL).floorInt())] += 1 //greens[minOf(CHANNEL_MAX, lightmap.getG(x, y).times(MUL).floorInt())] += 1
//blues[minOf(CHANNEL_MAX, lightmap.getB(x, y).floorInt())] += 1
//uvs[minOf(CHANNEL_MAX, lightmap.getA(x, y).floorInt())] += 1
} }
catch (e: ArrayIndexOutOfBoundsException) { } catch (e: ArrayIndexOutOfBoundsException) { }
} }