From 7554459281993c334a4f841f88e368bbcfb64224 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 28 Aug 2024 01:51:55 +0900 Subject: [PATCH] deblocking wip --- assets/graphics/blocks/deblocking_mask.tga | 3 + .../terrarum/worlddrawer/BlocksDrawer.kt | 127 +++++++++++++----- .../terrarum/worlddrawer/CreateTileAtlas.kt | 3 + src/shaders/deblocking.frag | 43 ++++++ src/shaders/tiling.frag | 93 ++++++++++--- .../graphics/terrain/deblocking_mask.kra | 3 + 6 files changed, 223 insertions(+), 49 deletions(-) create mode 100644 assets/graphics/blocks/deblocking_mask.tga create mode 100644 src/shaders/deblocking.frag create mode 100644 work_files/graphics/terrain/deblocking_mask.kra diff --git a/assets/graphics/blocks/deblocking_mask.tga b/assets/graphics/blocks/deblocking_mask.tga new file mode 100644 index 000000000..54abaa5fe --- /dev/null +++ b/assets/graphics/blocks/deblocking_mask.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b29c821109b47d35ab97d96ea37422e62b4edea8d0aef9dca2d3f11cebfd59ee +size 50194 diff --git a/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt b/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt index 1ebbb046d..c010402b9 100644 --- a/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt +++ b/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt @@ -1,7 +1,10 @@ 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.graphics.glutils.Float16FrameBuffer +import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.math.Matrix4 import com.jme3.math.FastMath import net.torvald.gdx.graphics.Cvec @@ -98,7 +101,14 @@ internal object BlocksDrawer { 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") + private val shaderTiling = App.loadShaderFromClasspath("shaders/default.vert", "shaders/tiling.frag") + private val shaderDeblock = App.loadShaderFromClasspath("shaders/default.vert", "shaders/deblocking.frag") + + private lateinit var deblockingFBO: Float16FrameBuffer + private lateinit var blurmapFBO: Float16FrameBuffer + + lateinit var batch: FlippingSpriteBatch + private lateinit var camera: OrthographicCamera init { @@ -151,6 +161,10 @@ internal object BlocksDrawer { tilesTerrain = seasonalTerrains[1] + batch = FlippingSpriteBatch() + camera = OrthographicCamera(App.scr.width.toFloat(), App.scr.height.toFloat()) + + printdbg(this, "init() exit") } @@ -258,6 +272,19 @@ internal object BlocksDrawer { renderUsingBuffer(OCCLUSION, projectionMatrix, false, drawEmissive) } + + private fun clearBuffer() { + gdxClearAndEnableBlend(0f,0f,0f,0f) + } + + private fun setCameraPosition(newX: Float, newY: Float) { + camera.position.set((-newX + App.scr.halfw).roundToFloat(), (-newY + App.scr.halfh).roundToFloat(), 0f) + camera.update() + batch.projectionMatrix = camera.combined + } + + private val testTexture = Texture(Gdx.files.internal("./assets/test_texture.tga")) + internal fun drawTerrain(projectionMatrix: Matrix4, drawGlow: Boolean, drawEmissive: Boolean = false) { gdxBlendNormalStraightAlpha() @@ -277,6 +304,24 @@ internal object BlocksDrawer { gdxBlendNormalStraightAlpha() } + + private fun deblockAndWriteTexture(projectionMatrix: Matrix4, dest: FrameBuffer, blurmap: FrameBuffer) { + blurmap.colorBufferTexture.bind(1) + dest.colorBufferTexture.bind(0) + + + + // basically this part is correct, test with any test texture + shaderDeblock.bind() + shaderDeblock.setUniformMatrix("u_projTrans", projectionMatrix) + shaderDeblock.setUniformf("resolution", oldScreenW.toFloat(), oldScreenH.toFloat()) + shaderDeblock.setUniformi("u_blurmap", 1) + shaderDeblock.setUniformi("u_texture", 0) + tilesQuad.render(shaderDeblock, GL20.GL_TRIANGLE_FAN) + + Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) + } + /** * Turns bitmask-with-single-bit-set into its bit index. The LSB is counted as 1, and thus the index starts at one. * @return 0 -> -1, 1 -> 0, 2 -> 1, 4 -> 2, 8 -> 3, 16 -> 4, ... @@ -491,11 +536,10 @@ internal object BlocksDrawer { (fillThis * 16f - 0.5f).floorToInt().coerceIn(0, 15) } else if (treeLeavesTiles.binarySearch(rawTileNum) >= 0) { - getNearbyTilesInfoTrees(x, y, mode).swizzle8(renderTag.maskType, hash) + getNearbyTilesInfoTreeLeaves(x, y, mode).swizzle8(renderTag.maskType, hash) } else if (treeTrunkTiles.binarySearch(rawTileNum) >= 0) { - hash = 0 - getNearbyTilesInfoTrees(x, y, mode) + getNearbyTilesInfoTreeTrunks(x, y, mode) } else if (platformTiles.binarySearch(rawTileNum) >= 0) { hash %= 2 @@ -777,23 +821,28 @@ internal object BlocksDrawer { return fluidSolidMaskLut[i] } - private fun getNearbyTilesInfoTrees(x: Int, y: Int, mode: Int): Int { - val tileThis = world.getTileFromTerrain(x, y) + private fun getNearbyTilesInfoTreeLeaves(x: Int, y: Int, mode: Int): Int { val nearbyTiles: List = getNearbyTilesPos(x, y).map { world.getTileFrom(mode, it.x, it.y) } var ret = 0 - if (isTreeFoliage(tileThis)) { - for (i in nearbyTiles.indices) { - if (isTreeFoliage(nearbyTiles[i])) { // foliage "shadow" should not connect to the tree trunk - ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3 - } + for (i in nearbyTiles.indices) { + if (isTreeFoliage(nearbyTiles[i])) { // foliage "shadow" should not connect to the tree trunk + ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3 } } - else if (isTreeTrunk(tileThis)) { - for (i in nearbyTiles.indices) { - if (isTreeTrunk(nearbyTiles[i]) || i == 6 && isTreeFoliage(nearbyTiles[6])) { // if tile above is leaves, connect to it - ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3 - } + + return ret + } + + private fun getNearbyTilesInfoTreeTrunks(x: Int, y: Int, mode: Int): Int { + val nearbyTiles: List = getNearbyTilesPos(x, y).map { world.getTileFrom(mode, it.x, it.y) } + + var ret = 0 + for (i in nearbyTiles.indices) { + if (isTreeTrunk(nearbyTiles[i]) || + i == 6 && isTreeFoliage(nearbyTiles[i]) || + i == 2 && isCultivable(nearbyTiles[i])) { // if tile above is leaves or tile below is cultivable, connect to it + ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3 } } @@ -1104,32 +1153,38 @@ internal object BlocksDrawer { tilesGlow.texture.bind(0) // for some fuck reason, it must be bound as last } else { + // bind map for deblocking + if (mode == TERRAIN || mode == WALL) { + blurmapFBO.colorBufferTexture.bind(4) + } + _tilesBufferAsTex2.bind(3) _tilesBufferAsTex.bind(2) tilesTerrainNext.texture.bind(1) tileAtlas.texture.bind(0) // for some fuck reason, it must be bound as last } - shader.bind() - shader.setUniformMatrix("u_projTrans", projectionMatrix)//camera.combined) - shader.setUniformf("colourFilter", vertexColour) - shader.setUniformi("tilesAtlas", 0) - 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 - shader.setUniformf("tilesInAtlas", tileAtlas.horizontalCount * 2f, tileAtlas.verticalCount * 2f) //depends on the tile atlas - shader.setUniformf("atlasTexSize", tileAtlas.texture.width.toFloat(), tileAtlas.texture.height.toFloat()) //depends on the tile atlas + shaderTiling.bind() + shaderTiling.setUniformMatrix("u_projTrans", projectionMatrix)//camera.combined) + shaderTiling.setUniformf("colourFilter", vertexColour) + shaderTiling.setUniformi("tilesAtlas", 0) + shaderTiling.setUniformi("tilesBlendAtlas", 1) + shaderTiling.setUniformi("tilemap", 2) + shaderTiling.setUniformi("tilemap2", 3) + shaderTiling.setUniformi("deblockingMap", 4) + shaderTiling.setUniformi("tilemapDimension", tilesBuffer.width, tilesBuffer.height) + shaderTiling.setUniformf("tilesInAxes", tilesInHorizontal.toFloat(), tilesInVertical.toFloat()) + shaderTiling.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 + shaderTiling.setUniformf("tilesInAtlas", tileAtlas.horizontalCount * 2f, tileAtlas.verticalCount * 2f) //depends on the tile atlas + shaderTiling.setUniformf("atlasTexSize", tileAtlas.texture.width.toFloat(), tileAtlas.texture.height.toFloat()) //depends on the tile atlas // set the blend value as world's time progresses, in linear fashion - shader.setUniformf("tilesBlend", if (mode == TERRAIN || mode == WALL) + shaderTiling.setUniformf("tilesBlend", if (mode == TERRAIN || mode == WALL) tilesTerrainBlendDegree else 0f ) - shader.setUniformf("mulBlendIntensity", if (mode == OCCLUSION) occlusionIntensity else 1f) - tilesQuad.render(shader, GL20.GL_TRIANGLE_FAN) + shaderTiling.setUniformf("mulBlendIntensity", if (mode == OCCLUSION) occlusionIntensity else 1f) + tilesQuad.render(shaderTiling, GL20.GL_TRIANGLE_FAN) //tilesBufferAsTex.dispose() } @@ -1191,6 +1246,11 @@ internal object BlocksDrawer { tilesQuad.setIndices(shortArrayOf(0, 1, 2, 3)) } + if (::deblockingFBO.isInitialized) deblockingFBO.dispose() + deblockingFBO = Float16FrameBuffer(screenW, screenH, false) + if (::blurmapFBO.isInitialized) deblockingFBO.dispose() + blurmapFBO = Float16FrameBuffer(screenW, screenH, false) + oldScreenW = screenW oldScreenH = screenH @@ -1237,7 +1297,8 @@ internal object BlocksDrawer { _tilesBufferAsTex.dispose() _tilesBufferAsTex2.dispose() tilesQuad.tryDispose() - shader.dispose() + shaderTiling.dispose() + shaderDeblock.dispose() if (::terrainTilesBuffer.isInitialized) terrainTilesBuffer.destroy() if (::wallTilesBuffer.isInitialized) wallTilesBuffer.destroy() @@ -1246,6 +1307,7 @@ internal object BlocksDrawer { if (::occlusionBuffer.isInitialized) occlusionBuffer.destroy() if (::tempRenderTypeBuffer.isInitialized) tempRenderTypeBuffer.destroy() + if (::batch.isInitialized) batch.tryDispose() App.tileMaker.dispose() } @@ -1263,6 +1325,7 @@ internal object BlocksDrawer { //fun isBlendMul(b: Int) = TILES_BLEND_MUL.contains(b) fun isTreeFoliage(b: ItemID) = BlockCodex[b].hasAllTagsOf("TREE", "LEAVES") fun isTreeTrunk(b: ItemID) = BlockCodex[b].hasAllTagsOf("TREE", "TREETRUNK") + fun isCultivable(b: ItemID) = BlockCodex[b].hasAllTagsOf("NATURAL", "CULTIVABLE") fun tileInCamera(x: Int, y: Int) = x >= WorldCamera.x.div(TILE_SIZE) && y >= WorldCamera.y.div(TILE_SIZE) && diff --git a/src/net/torvald/terrarum/worlddrawer/CreateTileAtlas.kt b/src/net/torvald/terrarum/worlddrawer/CreateTileAtlas.kt index 93387eee6..a25781bdd 100644 --- a/src/net/torvald/terrarum/worlddrawer/CreateTileAtlas.kt +++ b/src/net/torvald/terrarum/worlddrawer/CreateTileAtlas.kt @@ -471,6 +471,9 @@ class CreateTileAtlas { // 0000 (0): INVALID // 1000 (1): connect-mutual // 0100 (2): connect-self + // Line 3: Request for Post-Processing + // 0000 (0): do nothing + // 1000 (1): subtile deblocking // NOTE: For this system, the "NORANDTILE" tag is ignored val maskType = if (tilesPixmap.width >= 3*W_SUBTILE_GENERIC) MASK_SUBTILE_GRASS else MASK_SUBTILE_GENERIC var connectionType0 = 0 diff --git a/src/shaders/deblocking.frag b/src/shaders/deblocking.frag new file mode 100644 index 000000000..6039cfe26 --- /dev/null +++ b/src/shaders/deblocking.frag @@ -0,0 +1,43 @@ +#ifdef GL_ES +precision mediump float; +#endif + + +in vec4 v_color; +in vec2 v_texCoords; + +out vec4 fragColor; + +uniform sampler2D u_texture; +uniform sampler2D u_blurmap; +uniform vec2 resolution; + +const vec4 _four = vec4(1.0 / 4.0); +const vec4 _two = vec4(1.0 / 2.0); +const float blur = 1.0; +vec2 blurUp = vec2(0.0, -blur); +vec2 blurDown = vec2(0.0, +blur); +vec2 blurLeft = vec2(-blur, 0.0); +vec2 blurRight = vec2(+blur, 0.0); + +void main(void) { + vec4 rgbColourIn = texture(u_texture, v_texCoords); + vec4 rgbColourL = texture(u_texture, v_texCoords + (blurLeft / resolution)); + vec4 rgbColourR = texture(u_texture, v_texCoords + (blurRight / resolution)); + vec4 rgbColourU = texture(u_texture, v_texCoords + (blurUp / resolution)); + vec4 rgbColourD = texture(u_texture, v_texCoords + (blurDown / resolution)); + + vec4 blurH = (rgbColourIn + rgbColourIn + rgbColourL + rgbColourR) * _four; + vec4 blurV = (rgbColourIn + rgbColourIn + rgbColourU + rgbColourD) * _four; + + vec4 mapCol = texture(u_blurmap, v_texCoords); + + fragColor = v_color * mix( + mix(rgbColourIn, blurH, mapCol.x), + mix(rgbColourIn, blurV, mapCol.y), + 0.5 + ); + + fragColor = rgbColourIn; +// fragColor = vec4(v_texCoords.x, v_texCoords.y, 0.0, 1.0); +} \ No newline at end of file diff --git a/src/shaders/tiling.frag b/src/shaders/tiling.frag index 063e93273..598dd15d5 100644 --- a/src/shaders/tiling.frag +++ b/src/shaders/tiling.frag @@ -15,9 +15,10 @@ uniform vec2 tilesInAxes; // 8x8 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) +uniform sampler2D deblockingMap; + uniform float tilesBlend = 0.0; // percentage of blending [0f..1f]. 0: draws tilesAtlas, 1: draws tilesBlendAtlas uniform vec2 tilesInAtlas = vec2(256.0, 256.0); @@ -85,24 +86,26 @@ vec2 uvFlipRot(int op, vec2 uv) { return (flipRotMat[op] * vec3(uv, 1.0)).xy; } -void main() { +const vec4 _four = vec4(1.0 / 4.0); +const vec4 _three = vec4(1.0 / 3.0); +const vec4 _two = vec4(1.0 / 2.0); +const float blur = 1.0; +vec2 blurU = vec2(0.0, -blur); +vec2 blurD = vec2(0.0, +blur); +vec2 blurL = vec2(-blur, 0.0); +vec2 blurR = vec2(+blur, 0.0); - // READ THE FUCKING MANUAL, YOU DONKEY !! // - // This code purposedly uses flipped fragcoord. // - // Make sure you don't use gl_FragCoord unknowingly! // - // Remember, if there's a compile error, shader SILENTLY won't do anything // - - - // 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; // 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 // +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 +vec4[2] getTileNumbersFromMap(vec2 fragCoord) { vec4 tileFromMap = texture(tilemap, fragCoord / overscannedScreenDimension); // raw tile number vec4 tileFromMap2 = texture(tilemap2, fragCoord / overscannedScreenDimension); // raw tile number - ivec3 tbf = _colToInt(tileFromMap, tileFromMap2); + return vec4[2](tileFromMap, tileFromMap2); +} + +vec4[2] getFragColorForOnscreenCoord(vec2 fragCoord) { + vec4[] tileFromMap = getTileNumbersFromMap(fragCoord); + ivec3 tbf = _colToInt(tileFromMap[0], tileFromMap[1]); int tile = tbf.x; int breakage = tbf.y; int flipRot = tbf.z; @@ -115,20 +118,76 @@ void main() { // don't really need highp here; read the GLES spec vec2 uvCoordForTile = uvFlipRot(flipRot, mod(fragCoord, tileSizeInPx)) * _tileSizeInPx * _tilesInAtlas; // 0..0.00390625 regardless of tile position in atlas + vec2 uvCoordForTile1 = mod(fragCoord, tileSizeInPx) * _tileSizeInPx * _tilesInAtlas;// 0..0.00390625 regardless of tile position in atlas vec2 uvCoordOffsetTile = tileXY * _tilesInAtlas; // where the tile starts in the atlas, using uv coord (0..1) vec2 uvCoordOffsetBreakage = (breakageXY + tileQ) * _tilesInAtlas; // get final UV coord for the actual sampling // vec2 finalUVCoordForTile = uvCoordForTile + uvCoordOffsetTile;// where we should be actually looking for in atlas, using UV coord (0..1) - vec2 finalUVCoordForBreakage = uvCoordForTile + uvCoordOffsetBreakage; + vec2 finalUVCoordForBreakage = uvCoordForTile1 + uvCoordOffsetBreakage; // blending a breakage tex with main tex // vec4 tileCol = texture(tilesAtlas, finalUVCoordForTile); vec4 tileAltCol = texture(tilesBlendAtlas, finalUVCoordForTile); - vec4 finalTile = mix(tileCol, tileAltCol, tilesBlend); + return vec4[]( + mix(tileCol, tileAltCol, tilesBlend), + finalUVCoordForBreakage.xyxy + ); +} + +vec4 getFragColorForOnscreenCoord1(vec2 fragCoord) { + vec4[] tileFromMap = getTileNumbersFromMap(fragCoord); + ivec3 tbf = _colToInt(tileFromMap[0], tileFromMap[1]); + int tile = tbf.x; + int breakage = tbf.y; + int flipRot = tbf.z; + ivec4 tileXYnQ = tileNumberToXY(tile); + ivec2 tileXY = tileXYnQ.xy; + ivec2 tileQ = tileXYnQ.zw; + + // calculate the UV coord value for texture sampling // + + // don't really need highp here; read the GLES spec + vec2 uvCoordForTile = uvFlipRot(flipRot, mod(fragCoord, tileSizeInPx)) * _tileSizeInPx * _tilesInAtlas; // 0..0.00390625 regardless of tile position in atlas + vec2 uvCoordOffsetTile = tileXY * _tilesInAtlas; // where the tile starts in the atlas, using uv coord (0..1) + + // get final UV coord for the actual sampling // + + vec2 finalUVCoordForTile = uvCoordForTile + uvCoordOffsetTile;// where we should be actually looking for in atlas, using UV coord (0..1) + + // blending a breakage tex with main tex // + + vec4 tileCol = texture(tilesAtlas, finalUVCoordForTile); + vec4 tileAltCol = texture(tilesBlendAtlas, finalUVCoordForTile); + + return mix(tileCol, tileAltCol, tilesBlend); +} + +void main() { + 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 + + vec4[] fv = getFragColorForOnscreenCoord(fragCoord); + vec2 finalUVCoordForBreakage = fv[1].xy; + + vec4 finalTileC = fv[0]; + vec4 finalTileL = getFragColorForOnscreenCoord1(fragCoord + blurL); + vec4 finalTileR = getFragColorForOnscreenCoord1(fragCoord + blurR); + vec4 finalTileU = getFragColorForOnscreenCoord1(fragCoord + blurU); + vec4 finalTileD = getFragColorForOnscreenCoord1(fragCoord + blurD); + + vec4 blurH = (finalTileC + finalTileL + finalTileR) * _three; + vec4 blurV = (finalTileC + finalTileU + finalTileD) * _three; + + vec4 mapCol = vec4(1.0, 0.0, 1.0, finalTileC.a);//texture(u_blurmap, v_texCoords); + + vec4 finalTile = mix( + mix(finalTileC, blurH, mapCol.x), + mix(finalTileC, blurV, mapCol.y), + 0.5 + ); vec4 finalBreakage = drawBreakage * texture(tilesAtlas, finalUVCoordForBreakage); // drawBreakeage = 0 to not draw, = 1 to draw diff --git a/work_files/graphics/terrain/deblocking_mask.kra b/work_files/graphics/terrain/deblocking_mask.kra new file mode 100644 index 000000000..1dab92c96 --- /dev/null +++ b/work_files/graphics/terrain/deblocking_mask.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e61c5605a93db32eecaadc9e20cfc94bec3c405845a3c5f1791108aea0171661 +size 60490