mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
making room for larger tile ID to accomodate subtiling
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user