making room for larger tile ID to accomodate subtiling

This commit is contained in:
minjaesong
2024-08-19 02:52:32 +09:00
parent e18f6512bc
commit 88680fb0fc
3 changed files with 89 additions and 78 deletions

View File

@@ -1,6 +1,5 @@
package net.torvald.terrarum.worlddrawer
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input.Keys
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.math.Matrix4
@@ -21,7 +20,7 @@ import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.toBig64
import net.torvald.terrarum.worlddrawer.CreateTileAtlas.Companion.WALL_OVERLAY_COLOUR
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.unsafe.UnsafeInt2D
import net.torvald.unsafe.UnsafeLong2D
import kotlin.math.roundToInt
@@ -88,13 +87,14 @@ internal object BlocksDrawer {
private const val GZIP_READBUF_SIZE = 8192
private lateinit var terrainTilesBuffer: UnsafeInt2D
private lateinit var wallTilesBuffer: UnsafeInt2D
private lateinit var oreTilesBuffer: UnsafeInt2D
private lateinit var fluidTilesBuffer: UnsafeInt2D
private lateinit var occlusionBuffer: UnsafeInt2D
private lateinit var tempRenderTypeBuffer: UnsafeInt2D // 0x tttt 00 ii; where t=rawTileNum, i=nearbyTilesInfo
private lateinit var terrainTilesBuffer: UnsafeLong2D
private lateinit var wallTilesBuffer: UnsafeLong2D
private lateinit var oreTilesBuffer: UnsafeLong2D
private lateinit var fluidTilesBuffer: UnsafeLong2D
private lateinit var occlusionBuffer: UnsafeLong2D
private lateinit var tempRenderTypeBuffer: UnsafeLong2D // 0x tttt 00 ii; where t=rawTileNum, i=nearbyTilesInfo
private var tilesBuffer: Pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888)
private var tilesBuffer2: Pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888)
private lateinit var tilesQuad: Mesh
private val shader = App.loadShaderFromClasspath("shaders/default.vert", "shaders/tiling.frag")
@@ -422,11 +422,11 @@ internal object BlocksDrawer {
else if (rawTileNum == 0)
0
else if (bufferY > 0 && tempRenderTypeBuffer[bufferY - 1, bufferX].let {
it.ushr(16) == rawTileNum && it.and(255) == 0
it.ushr(16).toInt() == rawTileNum && it.and(255) == 0L
})
16
else if (bufferY > 0 && tempRenderTypeBuffer[bufferY - 1, bufferX].let {
it.ushr(16) == rawTileNum && (it.and(255) < 18 || it.and(255) >= 36)
it.ushr(16).toInt() == rawTileNum && (it.and(255) < 18 || it.and(255) >= 36)
})
17
else if (fillThis < 0.5f / 16f)
@@ -438,12 +438,12 @@ internal object BlocksDrawer {
val tileNum = tileUpTag ushr 16
val tileTag = tileUpTag and 255
if (tileNum == rawTileNum && tileTag in 18..33) {
if (tileTag - 18 and 0b0110 == 0b0110)
if (tileNum.toInt() == rawTileNum && tileTag in 18..33) {
if ((tileTag - 18 and 0b0110).toInt() == 0b0110)
38
else if (tileTag - 18 and 0b0100 == 0b0100)
else if ((tileTag - 18 and 0b0100).toInt() == 0b0100)
37
else if (tileTag - 18 and 0b0010 == 0b0010)
else if ((tileTag - 18 and 0b0010).toInt() == 0b0010)
36
else
15
@@ -520,7 +520,7 @@ internal object BlocksDrawer {
// draw a tile
writeToBuffer(mode, bufferX, bufferY, thisTileX, thisTileY, breakingStage, hash)
tempRenderTypeBuffer[bufferY, bufferX] = nearbyTilesInfo or rawTileNum.shl(16)
tempRenderTypeBuffer[bufferY, bufferX] = (nearbyTilesInfo or rawTileNum.shl(16)).toLong()
}
}
}
@@ -795,18 +795,18 @@ internal object BlocksDrawer {
}
/**
* Raw format of RGBA8888, where RGB portion actually encodes the absolute tile number and A is always 255.
* Raw format of RGBA8888.
*
* @return Raw colour bits in RGBA8888 format
*/
private fun sheetXYToTilemapColour(mode: Int, sheetX: Int, sheetY: Int, breakage: Int, hash: Int): Int =
// the tail ".or(255)" is there to write 1.0 to the A channel (remember, return type is RGBA)
private fun sheetXYToTilemapColour1(mode: Int, sheetX: Int, sheetY: Int, breakage: Int, hash: Int): Int =
(tilesTerrain.horizontalCount * sheetY + sheetX).shl(8) or // the actual tile bits
255 // does it premultiply the alpha?!?!!!?!?!
// this code is synced to the tilesTerrain's tile configuration, but everything else is hard-coded
// right now.
(tilesTerrain.horizontalCount * sheetY + sheetX).shl(8).or(255) or // the actual tile bits
breakage.and(15).shl(28) or // breakage bits
hash.and(15).shl(24) // flip-rot
private fun sheetXYToTilemapColour2(mode: Int, sheetX: Int, sheetY: Int, breakage: Int, hash: Int): Int =
breakage.and(15).shl(8) or // breakage bits on B
hash.and(15).shl(16) or // fliprot on G
255 // does it premultiply the alpha?!?!!!?!?!
private fun writeToBuffer(mode: Int, bufferPosX: Int, bufferPosY: Int, sheetX: Int, sheetY: Int, breakage: Int, hash: Int) {
@@ -820,10 +820,13 @@ internal object BlocksDrawer {
}
sourceBuffer[bufferPosY, bufferPosX] = sheetXYToTilemapColour(mode, sheetX, sheetY, breakage, hash)
sourceBuffer[bufferPosY, bufferPosX] =
sheetXYToTilemapColour1(mode, sheetX, sheetY, breakage, hash).toLong().and(0xFFFFFFFFL) or
sheetXYToTilemapColour2(mode, sheetX, sheetY, breakage, hash).toLong().and(0xFFFFFFFFL).shl(32)
}
private var _tilesBufferAsTex: Texture = Texture(1, 1, Pixmap.Format.RGBA8888)
private var _tilesBufferAsTex2: Texture = Texture(1, 1, Pixmap.Format.RGBA8888)
private val occlusionIntensity = 0.22222222f // too low value and dark-coloured walls won't darken enough
private fun renderUsingBuffer(mode: Int, projectionMatrix: Matrix4, drawGlow: Boolean, drawEmissive: Boolean) {
@@ -858,9 +861,15 @@ internal object BlocksDrawer {
// As the texture size is very small, multithreading it would be less effective
for (y in 0 until tilesBuffer.height) {
for (x in 0 until tilesBuffer.width) {
val color = sourceBuffer[y, x]
tilesBuffer.setColor(color)
val colRaw = sourceBuffer[y, x]
val colMain = colRaw.toInt()
val colSub = colRaw.ushr(32).toInt()
tilesBuffer.setColor(colMain)
tilesBuffer.drawPixel(x, y)
tilesBuffer2.setColor(colSub)
tilesBuffer2.drawPixel(x, y)
}
}
@@ -869,32 +878,39 @@ internal object BlocksDrawer {
_tilesBufferAsTex = Texture(tilesBuffer)
_tilesBufferAsTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
_tilesBufferAsTex2.dispose()
_tilesBufferAsTex2 = Texture(tilesBuffer2)
_tilesBufferAsTex2.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
if (drawEmissive) {
tilesEmissive.texture.bind(2)
_tilesBufferAsTex.bind(1) // trying 1 and 0...
_tilesBufferAsTex2.bind(3)
_tilesBufferAsTex.bind(2)
tilesEmissive.texture.bind(1)
tilesEmissive.texture.bind(0) // for some fuck reason, it must be bound as last
}
else if (drawGlow) {
tilesGlow.texture.bind(2)
_tilesBufferAsTex.bind(1) // trying 1 and 0...
_tilesBufferAsTex2.bind(3)
_tilesBufferAsTex.bind(2)
tilesGlow.texture.bind(1)
tilesGlow.texture.bind(0) // for some fuck reason, it must be bound as last
}
else {
tilesTerrainNext.texture.bind(2)
_tilesBufferAsTex.bind(1) // trying 1 and 0...
_tilesBufferAsTex2.bind(3)
_tilesBufferAsTex.bind(2)
tilesTerrainNext.texture.bind(1)
tileAtlas.texture.bind(0) // for some fuck reason, it must be bound as last
}
val magn = App.scr.magn.toFloat()
shader.bind()
shader.setUniformMatrix("u_projTrans", projectionMatrix)//camera.combined)
shader.setUniform2fv("tilesInAtlas", App.tileMaker.SHADER_SIZE_KEYS, 2, 2)
shader.setUniform2fv("atlasTexSize", App.tileMaker.SHADER_SIZE_KEYS, 0, 2)
shader.setUniformf("colourFilter", vertexColour)
shader.setUniformi("tilesAtlas", 0)
shader.setUniformi("tilesBlendAtlas", 2)
shader.setUniformi("tilemap", 1)
shader.setUniformi("tilesBlendAtlas", 1)
shader.setUniformi("tilemap", 2)
shader.setUniformi("tilemap2", 3)
shader.setUniformi("tilemapDimension", tilesBuffer.width, tilesBuffer.height)
shader.setUniformf("tilesInAxes", tilesInHorizontal.toFloat(), tilesInVertical.toFloat())
shader.setUniformi("cameraTranslation", WorldCamera.x fmod TILE_SIZE, WorldCamera.y fmod TILE_SIZE) // usage of 'fmod' and '%' were depend on the for_x_start, which I can't just do naive int div
@@ -934,15 +950,17 @@ internal object BlocksDrawer {
if (::occlusionBuffer.isInitialized) occlusionBuffer.destroy()
if (::tempRenderTypeBuffer.isInitialized) tempRenderTypeBuffer.destroy()
terrainTilesBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
wallTilesBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
oreTilesBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
fluidTilesBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
occlusionBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
tempRenderTypeBuffer = UnsafeInt2D(tilesInHorizontal, tilesInVertical)
terrainTilesBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
wallTilesBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
oreTilesBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
fluidTilesBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
occlusionBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
tempRenderTypeBuffer = UnsafeLong2D(tilesInHorizontal, tilesInVertical)
tilesBuffer.dispose()
tilesBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGBA8888)
tilesBuffer2.dispose()
tilesBuffer2 = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGBA8888)
}
if (oldScreenW != screenW || oldScreenH != screenH) {
@@ -1014,7 +1032,9 @@ internal object BlocksDrawer {
tileItemWallGlow.dispose()
tileItemWallEmissive.dispose()
tilesBuffer.dispose()
tilesBuffer2.dispose()
_tilesBufferAsTex.dispose()
_tilesBufferAsTex2.dispose()
tilesQuad.tryDispose()
shader.dispose()

View File

@@ -3,17 +3,17 @@ package net.torvald.unsafe
/**
* Created by minjaesong on 2024-07-22.
*/
class UnsafeInt2D(width: Int, height: Int) {
class UnsafeLong2D(width: Int, height: Int) {
val width = width.toLong()
val height = height.toLong()
private val ptr = UnsafeHelper.allocate(4L * width * height)
private val ptr = UnsafeHelper.allocate(8L * width * height)
private inline fun toIndex(y: Int, x: Int) = width * y + x
operator fun set(y: Int, x: Int, value: Int) = ptr.setInt(toIndex(y, x), value)
operator fun get(y: Int, x: Int) = ptr.getInt(toIndex(y, x))
operator fun set(y: Int, x: Int, value: Long) = ptr.setLong(toIndex(y, x), value)
operator fun get(y: Int, x: Int) = ptr.getLong(toIndex(y, x))
fun destroy() = ptr.destroy()
val destroyed: Boolean

View File

@@ -7,7 +7,7 @@
precision mediump float;
#endif
layout(origin_upper_left,pixel_center_integer) in vec4 gl_FragCoord;
layout(origin_upper_left,pixel_center_integer) in vec4 gl_FragCoord; // is now top-down and strictly integer
in vec4 v_color;
in vec2 v_texCoords;
@@ -18,6 +18,7 @@ uniform sampler2D u_texture;
uniform vec2 tilesInAxes; // size of the tilemap texture; vec2(tiles_in_horizontal, tiles_in_vertical)
uniform sampler2D tilemap; // RGBA8888
uniform sampler2D tilemap2; // RGBA8888
uniform sampler2D tilesAtlas; // terrain, wire, fluids, etc.
uniform sampler2D tilesBlendAtlas; // alternative terrain for the weather mix (e.g. yellowed grass)
@@ -47,37 +48,25 @@ ivec2 getTileXY(int tileNumber) {
return ivec2(tileNumber % int(tilesInAtlas.x), tileNumber / int(tilesInAtlas.x));
}
// return: int=0x(aa)rrggbb
int _colToInt(vec4 color) {
return int(color.b * 255) | (int(color.g * 255) << 8) | (int(color.r * 255) << 16) | (int(color.a * 255) << 24);
}
// return: ivec3(tileID, breakage, fliprot)
ivec3 _colToInt(vec4 map1, vec4 map2) {
ivec3 col1 = ivec3(
int(map1.r * 255),
int(map1.g * 255),
int(map1.b * 255));
// 0x00ggbb where int=0xaarrggbb
// return: [0..65535]
int getTileFromColor(vec4 color) {
return _colToInt(color) & 0xFFFF;
}
ivec3 col2 = ivec3(
int(map2.r * 255),
int(map2.g * 255),
int(map2.b * 255));
// 0x00r00000 where int=0xaarrggbb
// return: [0..15]
int getBreakageFromColor(vec4 color) {
return (_colToInt(color) >> 20) & 0xF;
return ivec3(
(col1.r << 16) | (col1.g << 8) | col1.b, // tile
col2.b, // breakage
col2.g // fliprot
);
}
// 0x000r0000 where int=0xaarrggbb
// return: [0..15]
int getTileFlipRotFromColor(vec4 color) {
return (_colToInt(color) >> 16) & 0xF;
}
// 0baaaa_aaa0_0[24] where int=0baaaaaaaa_rrrrrrrr_...
// in other words, least significant bit of the alpha value is unused.
// return: [0..127]
int getSubtileFromColor(vec4 color) {
return (_colToInt(color) >> 25) & 0x7F;
}
mat3x2[] flipRotMat = mat3x2[](
mat3x2( 1.0, 0.0, 0.0, 1.0, tileSizeInPx.x*0.0, tileSizeInPx.y*0.0),
mat3x2(-1.0, 0.0, 0.0, 1.0, tileSizeInPx.x*1.0, tileSizeInPx.y*0.0),
@@ -104,14 +93,16 @@ void main() {
// default gl_FragCoord takes half-integer (represeting centre of the pixel) -- could be useful for phys solver?
// This one, however, takes exact integer by rounding down. //
vec2 overscannedScreenDimension = tilesInAxes * tileSizeInPx; // how many tiles will fit into a screen; one used by the tileFromMap; we need this because screen size is not integer multiple of the tile size
vec2 fragCoord = gl_FragCoord.xy + cameraTranslation + haalf; // NO IVEC2!!; this flips Y
vec2 fragCoord = gl_FragCoord.xy + cameraTranslation + haalf; // manually adding half-int to the flipped gl_FragCoord: this avoids driver bug present on the Asahi Linux and possibly (but unlikely) others
// get required tile numbers //
vec4 tileFromMap = texture(tilemap, fragCoord / overscannedScreenDimension); // raw tile number
int tile = getTileFromColor(tileFromMap);
int breakage = getBreakageFromColor(tileFromMap);
int flipRot = getTileFlipRotFromColor(tileFromMap);
vec4 tileFromMap2 = texture(tilemap2, fragCoord / overscannedScreenDimension); // raw tile number
ivec3 tbf = _colToInt(tileFromMap, tileFromMap2);
int tile = tbf.x;
int breakage = tbf.y;
int flipRot = tbf.z;
ivec2 tileXY = getTileXY(tile);
ivec2 breakageXY = getTileXY(breakage + 5); // +5 is hard-coded constant that depends on the contents of the atlas