From 1be8e5d0c7a48f1c7e0170e89483ecee59c14694 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 25 Aug 2017 19:37:12 +0900 Subject: [PATCH] trying to tile by shader --- assets/tiling.frag | 46 ++ src/net/torvald/terrarum/GlslTilingTest.kt | 130 ++++ .../torvald/terrarum/TerrarumAppLoader.java | 3 +- .../terrarum/worlddrawer/BlocksDrawerNew.kt | 736 ++++++++++++++++++ .../terrarum/worlddrawer/LightmapRenderer.kt | 2 +- .../graphics/sprites/npc_template - Copy.psd | 3 + work_files/graphics/sprites/npc_template.psd | 4 +- 7 files changed, 919 insertions(+), 5 deletions(-) create mode 100644 assets/tiling.frag create mode 100644 src/net/torvald/terrarum/GlslTilingTest.kt create mode 100644 src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt create mode 100644 work_files/graphics/sprites/npc_template - Copy.psd diff --git a/assets/tiling.frag b/assets/tiling.frag new file mode 100644 index 000000000..722e58ed7 --- /dev/null +++ b/assets/tiling.frag @@ -0,0 +1,46 @@ +#version 120 +#ifdef GL_ES + precision mediump float; +#endif + +layout(origin_upper_left) in vec4 gl_FragCoord; + + +varying vec4 v_color; +varying vec2 v_texCoords; + +uniform sampler2D tilesAtlas; +uniform sampler2D backgroundTexture; + +uniform vec2 tileInAtlas = vec2(256, 256); +uniform vec2 atlasTexSize = vec2(4096, 4096); + + +uniform vec2 tileInDim; // vec2(tiles_in_horizontal, tiles_in_vertical) +uniform vec2 cameraTranslation = vec2(0, 0); +uniform float tileSizeInPx = 16; + + +uniform float tilemap[tileInDim.x * tileInDim.y]; // must be float array + + +void main() { + + vec2 pxCoord = gl_FragCoord.xy - cameraTranslation; + vec2 pxCoordModTilesize = mod(pxCoord, tileSizeInPx); + vec2 tileCoord = floor(pxCoord / tileCoord); + + int absoluteTileCoord = int(tileCoord.x + tileCoord.y * tileInDim.x); + + + float tile = tilemap[absoluteTileCoord]; // sure it's integer at this point + vec2 fragCoordInAtlas = vec2( + tileSizeInPx * mod(tile, tileInAtlas.x) + pxCoordModTilesize.x, + tileSizeInPx * floor(tile / tileInAtlas.x) + pxCoordModTilesize.y + ); + vec2 fragCoordUV = vec2(fragCoordInAtlas.x / atlasTexSize.x, 1 - fragCoordInAtlas.y / atlasTexSize.y); + vec4 fragInAtlas = texture2D(tilesAtlas, fragCoordUV); + + + gl_FragColor = fragInAtlas; +} diff --git a/src/net/torvald/terrarum/GlslTilingTest.kt b/src/net/torvald/terrarum/GlslTilingTest.kt new file mode 100644 index 000000000..7e21342f3 --- /dev/null +++ b/src/net/torvald/terrarum/GlslTilingTest.kt @@ -0,0 +1,130 @@ +package net.torvald.terrarum + +import com.badlogic.gdx.ApplicationAdapter +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.backends.lwjgl.LwjglApplication +import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration +import com.badlogic.gdx.graphics.* +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShaderProgram +import net.torvald.terrarum.gameactors.ceilInt +import net.torvald.terrarumsansbitmap.gdx.GameFontBase + +/** + * Created by minjaesong on 2017-08-25. + */ +fun main(args: Array) { // LWJGL 3 won't work? java.lang.VerifyError + val config = LwjglApplicationConfiguration() + //config.useGL30 = true + config.vSyncEnabled = false + config.resizable = false + config.width = 1072 + config.height = 742 + config.foregroundFPS = 9999 + LwjglApplication(GlslTilingTest, config) +} + +object GlslTilingTest : ApplicationAdapter() { + + lateinit var shader: ShaderProgram + lateinit var font: GameFontBase + + lateinit var tilesQuad: Mesh + + lateinit var camera: OrthographicCamera + + lateinit var batch: SpriteBatch + + lateinit var fucktex: Texture + + val TILE_SIZE = 16 + + + lateinit var tilesBuffer: FloatArray + + + override fun create() { + ShaderProgram.pedantic = false + + shader = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/loadingCircle.frag")) + + + font = GameFontBase("assets/graphics/fonts/terrarum-sans-bitmap", flipY = false) + + + if (!shader.isCompiled) { + Gdx.app.log("Shader", shader.log) + System.exit(1) + } + + + + val tilesInHorizontal = Gdx.graphics.width.toFloat() / TILE_SIZE + val tilesInVertical = Gdx.graphics.height.toFloat() / TILE_SIZE + + tilesQuad = Mesh( + true, 4, 6, + VertexAttribute.Position(), + VertexAttribute.ColorUnpacked(), + VertexAttribute.TexCoords(0) + ) + + tilesQuad.setVertices(floatArrayOf( + 0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f, tilesInVertical, + Gdx.graphics.width.toFloat(), 0f, 0f, 1f, 1f, 1f, 1f, tilesInHorizontal, tilesInVertical, + Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat(), 0f, 1f, 1f, 1f, 1f, tilesInHorizontal, 0f, + 0f, Gdx.graphics.height.toFloat(), 0f, 1f, 1f, 1f, 1f, 0f, 0f + )) + tilesQuad.setIndices(shortArrayOf(0, 1, 2, 2, 3, 0)) + + + tilesBuffer = FloatArray(tilesInHorizontal.ceilInt() * tilesInVertical.ceilInt()) + + + camera = OrthographicCamera(Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()) + camera.setToOrtho(true, Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()) + camera.update() + + + batch = SpriteBatch() + + fucktex = Texture(Gdx.files.internal("assets/graphics/ortho_line_tex_2px.tga")) + } + + + override fun render() { + Gdx.graphics.setTitle("ShitOnGlsl — F: ${Gdx.graphics.framesPerSecond}") + + Gdx.gl.glClearColor(.094f, .094f, .094f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + + + + batch.inUse { + + batch.shader = shader + + shader.setUniformMatrix("u_projTrans", camera.combined) + shader.setUniformi("u_texture", 0) + shader.setUniform1fv("tilesBuffer", tilesBuffer, 0, tilesBuffer.size) + //tilesQuad.render(shader, GL20.GL_TRIANGLES) + + batch.draw(fucktex, 0f, 0f, Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()) + + } + + + /*shader.begin() + shader.setUniformMatrix("u_projTrans", batch.projectionMatrix) + shader.setUniformi("u_texture", 0) + shader.setUniformf("circleCentrePoint", Gdx.graphics.width / 2f, Gdx.graphics.height / 2f) + shader.setUniformf("colorCentrePoint", Gdx.graphics.width / 2f, Gdx.graphics.height / 2f) + shader.setUniformf("circleSize", 200f) + tilesQuad.render(shader, GL20.GL_TRIANGLES) + shader.end()*/ + } + + override fun dispose() { + shader.dispose() + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/TerrarumAppLoader.java b/src/net/torvald/terrarum/TerrarumAppLoader.java index a1eaeb27a..2b62b1dd0 100644 --- a/src/net/torvald/terrarum/TerrarumAppLoader.java +++ b/src/net/torvald/terrarum/TerrarumAppLoader.java @@ -145,8 +145,7 @@ public class TerrarumAppLoader implements ApplicationListener { Terrarum.INSTANCE.setAppLoader(this); Terrarum.INSTANCE.setScreenW(appConfig.width); Terrarum.INSTANCE.setScreenH(appConfig.height); - //setScreen(Terrarum.INSTANCE); - setScreen(ErrorDisp.INSTANCE); + setScreen(Terrarum.INSTANCE); } } else { diff --git a/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt new file mode 100644 index 000000000..a9d478cf5 --- /dev/null +++ b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt @@ -0,0 +1,736 @@ +package net.torvald.terrarum.worlddrawer + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.PairedMapLayer +import net.torvald.terrarum.blockproperties.Block +import net.torvald.terrarum.blockproperties.BlockCodex +import net.torvald.terrarum.* +import net.torvald.terrarum.gameactors.roundInt +import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_TILES +import net.torvald.terrarum.worlddrawer.WorldCamera.x +import net.torvald.terrarum.worlddrawer.WorldCamera.y +import net.torvald.terrarum.worlddrawer.WorldCamera.height +import net.torvald.terrarum.worlddrawer.WorldCamera.width +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.util.zip.GZIPInputStream + + +/** + * Created by minjaesong on 16-01-19. + */ +object BlocksDrawerNew { + lateinit var world: GameWorld + + + private val TILE_SIZE = FeaturesDrawer.TILE_SIZE + private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat() + + // TODO modular + //val tilesTerrain = SpriteSheet(ModMgr.getPath("basegame", "blocks/terrain.tga.gz"), TILE_SIZE, TILE_SIZE) // 64 MB + //val tilesWire = SpriteSheet(ModMgr.getPath("basegame", "blocks/wire.tga.gz"), TILE_SIZE, TILE_SIZE) // 4 MB + + val tilesTerrain: TextureRegionPack + val tilesWire: TextureRegionPack + val tileItemWall: TextureRegionPack + + //val tileItemWall = Image(TILE_SIZE * 16, TILE_SIZE * GameWorld.TILES_SUPPORTED / 16) // 4 MB + + + val wallOverlayColour = Color(2f/3f, 2f/3f, 2f/3f, 1f) + + val breakAnimSteps = 10 + + val WALL = GameWorld.WALL + val TERRAIN = GameWorld.TERRAIN + val WIRE = GameWorld.WIRE + + private val NEARBY_TILE_KEY_UP = 0 + private val NEARBY_TILE_KEY_RIGHT = 1 + private val NEARBY_TILE_KEY_DOWN = 2 + private val NEARBY_TILE_KEY_LEFT = 3 + + private val NEARBY_TILE_CODE_UP = 1 + private val NEARBY_TILE_CODE_RIGHT = 2 + private val NEARBY_TILE_CODE_DOWN = 4 + private val NEARBY_TILE_CODE_LEFT = 8 + + + private val GZIP_READBUF_SIZE = 8192 + + init { + // hard-coded as tga.gz + val gzFileList = listOf("blocks/terrain.tga.gz", "blocks/wire.tga.gz") + val gzTmpFName = listOf("tmp_terrain.tga", "tmp_wire.tga") + // unzip GZIP temporarily + gzFileList.forEachIndexed { index, filename -> + val terrainTexFile = ModMgr.getGdxFile("basegame", filename) + val gzi = GZIPInputStream(terrainTexFile.read(GZIP_READBUF_SIZE)) + val wholeFile = gzi.readBytes() + gzi.close() + val fos = BufferedOutputStream(FileOutputStream(gzTmpFName[index])) + fos.write(wholeFile) + fos.flush() + fos.close() + } + + val terrainPixMap = Pixmap(Gdx.files.internal(gzTmpFName[0])) + val wirePixMap = Pixmap(Gdx.files.internal(gzTmpFName[1])) + + // delete temp files + gzTmpFName.forEach { File(it).delete() } + + tilesTerrain = TextureRegionPack(Texture(terrainPixMap), TILE_SIZE, TILE_SIZE) + tilesTerrain.texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + tilesWire = TextureRegionPack(Texture(wirePixMap), TILE_SIZE, TILE_SIZE) + tilesWire.texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + + // also dispose unused temp files + //terrainPixMap.dispose() // commented: tileItemWall needs it + wirePixMap.dispose() + + + + + // create item_wall images + // --> make pixmap + val tileItemImgPixMap = Pixmap(TILE_SIZE * 16, TILE_SIZE * GameWorld.TILES_SUPPORTED / 16, Pixmap.Format.RGBA8888) + tileItemImgPixMap.pixels.rewind() + + for (tileID in ITEM_TILES) { + + val tile = tilesTerrain.get((tileID % 16) * 16, (tileID / 16)) + + // slow memory copy :\ I'm afraid I can't random-access bytebuffer... + for (y in 0..TILE_SIZE - 1) { + for (x in 0..TILE_SIZE - 1) { + tileItemImgPixMap.pixels.putInt( + terrainPixMap.getPixel( + tile.regionX + x, + tile.regionY + y + ) + ) + } + } + } + tileItemImgPixMap.pixels.rewind() + // turn pixmap into texture + tileItemWall = TextureRegionPack(Texture(tileItemImgPixMap), TILE_SIZE, TILE_SIZE) + + + + tileItemImgPixMap.dispose() + terrainPixMap.dispose() // finally + } + + /** + * Connectivity group 01 : artificial tiles + * It holds different shading rule to discriminate with group 02, index 0 is single tile. + * These are the tiles that only connects to itself, will not connect to colour variants + */ + private val TILES_CONNECT_SELF = hashSetOf( + Block.GLASS_CRUDE, + Block.GLASS_CLEAN, + Block.ILLUMINATOR_BLACK, + Block.ILLUMINATOR_BLUE, + Block.ILLUMINATOR_BROWN, + Block.ILLUMINATOR_CYAN, + Block.ILLUMINATOR_FUCHSIA, + Block.ILLUMINATOR_GREEN, + Block.ILLUMINATOR_GREEN_DARK, + Block.ILLUMINATOR_GREY_DARK, + Block.ILLUMINATOR_GREY_LIGHT, + Block.ILLUMINATOR_GREY_MED, + Block.ILLUMINATOR_ORANGE, + Block.ILLUMINATOR_PURPLE, + Block.ILLUMINATOR_RED, + Block.ILLUMINATOR_TAN, + Block.ILLUMINATOR_WHITE, + Block.ILLUMINATOR_YELLOW, + Block.ILLUMINATOR_BLACK_OFF, + Block.ILLUMINATOR_BLUE_OFF, + Block.ILLUMINATOR_BROWN_OFF, + Block.ILLUMINATOR_CYAN_OFF, + Block.ILLUMINATOR_FUCHSIA_OFF, + Block.ILLUMINATOR_GREEN_OFF, + Block.ILLUMINATOR_GREEN_DARK_OFF, + Block.ILLUMINATOR_GREY_DARK_OFF, + Block.ILLUMINATOR_GREY_LIGHT_OFF, + Block.ILLUMINATOR_GREY_MED_OFF, + Block.ILLUMINATOR_ORANGE_OFF, + Block.ILLUMINATOR_PURPLE_OFF, + Block.ILLUMINATOR_RED_OFF, + Block.ILLUMINATOR_TAN_OFF, + Block.ILLUMINATOR_WHITE_OFF, + Block.ILLUMINATOR_YELLOW, + Block.DAYLIGHT_CAPACITOR + ) + + /** + * To interact with external modules + */ + @JvmStatic fun addConnectSelf(blockID: Int): Boolean { + return TILES_CONNECT_SELF.add(blockID) + } + + /** + * Connectivity group 02 : natural tiles + * It holds different shading rule to discriminate with group 01, index 0 is middle tile. + */ + private val TILES_CONNECT_MUTUAL = hashSetOf( + Block.STONE, + Block.STONE_QUARRIED, + Block.STONE_TILE_WHITE, + Block.STONE_BRICKS, + Block.DIRT, + Block.GRASS, + Block.GRASSWALL, + Block.PLANK_BIRCH, + Block.PLANK_BLOODROSE, + Block.PLANK_EBONY, + Block.PLANK_NORMAL, + Block.SAND, + Block.SAND_WHITE, + Block.SAND_RED, + Block.SAND_DESERT, + Block.SAND_BLACK, + Block.SAND_GREEN, + Block.GRAVEL, + Block.GRAVEL_GREY, + Block.SNOW, + Block.ICE_NATURAL, + Block.ICE_MAGICAL, + Block.ORE_COPPER, + Block.ORE_IRON, + Block.ORE_GOLD, + Block.ORE_SILVER, + Block.ORE_ILMENITE, + Block.ORE_AURICHALCUM, + + Block.SANDSTONE, + Block.SANDSTONE_BLACK, + Block.SANDSTONE_DESERT, + Block.SANDSTONE_RED, + Block.SANDSTONE_WHITE, + Block.SANDSTONE_GREEN, + + Block.WATER, + Block.WATER_1, + Block.WATER_2, + Block.WATER_3, + Block.WATER_4, + Block.WATER_5, + Block.WATER_6, + Block.WATER_7, + Block.WATER_8, + Block.WATER_9, + Block.WATER_10, + Block.WATER_11, + Block.WATER_12, + Block.WATER_13, + Block.WATER_14, + Block.WATER_15, + Block.LAVA, + Block.LAVA_1, + Block.LAVA_2, + Block.LAVA_3, + Block.LAVA_4, + Block.LAVA_5, + Block.LAVA_6, + Block.LAVA_7, + Block.LAVA_8, + Block.LAVA_9, + Block.LAVA_10, + Block.LAVA_11, + Block.LAVA_12, + Block.LAVA_13, + Block.LAVA_14, + Block.LAVA_15 + ) + + /** + * To interact with external modules + */ + @JvmStatic fun addConnectMutual(blockID: Int): Boolean { + return TILES_CONNECT_MUTUAL.add(blockID) + } + + /** + * Torches, levers, switches, ... + */ + private val TILES_WALL_STICKER = hashSetOf( + Block.TORCH, + Block.TORCH_FROST, + Block.TORCH_OFF, + Block.TORCH_FROST_OFF + ) + + /** + * To interact with external modules + */ + @JvmStatic fun addWallSticker(blockID: Int): Boolean { + return TILES_WALL_STICKER.add(blockID) + } + + /** + * platforms, ... + */ + private val TILES_WALL_STICKER_CONNECT_SELF = hashSetOf( + Block.PLATFORM_BIRCH, + Block.PLATFORM_BLOODROSE, + Block.PLATFORM_EBONY, + Block.PLATFORM_STONE, + Block.PLATFORM_WOODEN + ) + + /** + * To interact with external modules + */ + @JvmStatic fun addWallStickerConnectSelf(blockID: Int): Boolean { + return TILES_WALL_STICKER_CONNECT_SELF.add(blockID) + } + + /** + * Tiles that half-transparent and has hue + * will blend colour using colour multiplication + * i.e. red hues get lost if you dive into the water + */ + private val TILES_BLEND_MUL = hashSetOf( + Block.WATER, + Block.WATER_1, + Block.WATER_2, + Block.WATER_3, + Block.WATER_4, + Block.WATER_5, + Block.WATER_6, + Block.WATER_7, + Block.WATER_8, + Block.WATER_9, + Block.WATER_10, + Block.WATER_11, + Block.WATER_12, + Block.WATER_13, + Block.WATER_14, + Block.WATER_15, + Block.LAVA, + Block.LAVA_1, + Block.LAVA_2, + Block.LAVA_3, + Block.LAVA_4, + Block.LAVA_5, + Block.LAVA_6, + Block.LAVA_7, + Block.LAVA_8, + Block.LAVA_9, + Block.LAVA_10, + Block.LAVA_11, + Block.LAVA_12, + Block.LAVA_13, + Block.LAVA_14, + Block.LAVA_15 + ) + + /** + * To interact with external modules + */ + @JvmStatic fun addBlendMul(blockID: Int): Boolean { + return TILES_BLEND_MUL.add(blockID) + } + + + /////////////////////////////////////////// + // NO draw lightmap using colour filter, actors must also be hidden behind the darkness + /////////////////////////////////////////// + + fun renderWall(batch: SpriteBatch) { + /** + * render to camera + */ + blendNormal() + + drawTiles(batch, WALL, false, wallOverlayColour) + } + + fun renderTerrain(batch: SpriteBatch) { + /** + * render to camera + */ + blendNormal() + + drawTiles(batch, TERRAIN, false, Color.WHITE) // regular tiles + } + + fun renderFront(batch: SpriteBatch, drawWires: Boolean) { + /** + * render to camera + */ + blendMul() + + drawTiles(batch, TERRAIN, true, Color.WHITE) // blendmul tiles + + if (drawWires) { + drawTiles(batch, WIRE, false, Color.WHITE) + } + + blendNormal() + } + + private val tileDrawLightThreshold = 2f / LightmapRenderer.MUL + + private fun canIHazRender(mode: Int, x: Int, y: Int) = + (world.getTileFrom(mode, x, y) != 0) // not an air tile + && + // for WALLs; else: ret true + if (mode == WALL) { // DRAW WHEN it is visible and 'is a lip' + ( BlockCodex[world.getTileFromTerrain(x, y) ?: 0].isClear || + ! + ((!BlockCodex[world.getTileFromTerrain(x, y - 1) ?: 0].isClear && !BlockCodex[world.getTileFromTerrain(x, y + 1) ?: 0].isClear) + && + (!BlockCodex[world.getTileFromTerrain(x - 1, y) ?: 0].isClear && !BlockCodex[world.getTileFromTerrain(x + 1, y + 1) ?: 0].isClear) + ) + ) + } + else + true + + // end + + private fun hasLightNearby(x: Int, y: Int) = ( // check if light level of nearby or this tile is illuminated + LightmapRenderer.getHighestRGB(x, y) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x - 1, y) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x + 1, y) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x, y - 1) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x, y + 1) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x - 1, y - 1) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x + 1, y + 1) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x + 1, y - 1) ?: 0f >= tileDrawLightThreshold || + LightmapRenderer.getHighestRGB(x - 1, y + 1) ?: 0f >= tileDrawLightThreshold + ) + + private fun drawTiles(batch: SpriteBatch, mode: Int, drawModeTilesBlendMul: Boolean, color: Color) { + val for_y_start = y / TILE_SIZE + val for_y_end = BlocksDrawer.clampHTile(for_y_start + (height / TILE_SIZE) + 2) + + val for_x_start = x / TILE_SIZE - 1 + val for_x_end = for_x_start + (width / TILE_SIZE) + 3 + + val originalBatchColour = batch.color.cpy() + batch.color = color + + // loop + for (y in for_y_start..for_y_end) { + var zeroTileCounter = 0 + + for (x in for_x_start..for_x_end) { + + val thisTile: Int? + if (mode % 3 == WALL) + thisTile = world.getTileFromWall(x, y) + else if (mode % 3 == TERRAIN) + thisTile = world.getTileFromTerrain(x, y) + else if (mode % 3 == WIRE) + thisTile = world.getTileFromWire(x, y) + else + throw IllegalArgumentException() + + val noDamageLayer = mode % 3 == WIRE + + // draw a tile, but only when illuminated + try { + if (canIHazRender(mode, x, y)) { + + if (!hasLightNearby(x, y)) { + // draw black patch + zeroTileCounter += 1 // unused for now + + // temporary solution; FIXME bad scanlines bug + batch.color = Color.BLACK + batch.fillRect(x * TILE_SIZEF, y * TILE_SIZEF, TILE_SIZEF, TILE_SIZEF) + } + else { + // commented out; FIXME bad scanlines bug + if (zeroTileCounter > 0) { + /*batch.color = Color.BLACK + batch.fillRect(x * TILE_SIZEF, y * TILE_SIZEF, -zeroTileCounter * TILE_SIZEF, TILE_SIZEF) + batch.color = color + zeroTileCounter = 0*/ + } + + + val nearbyTilesInfo: Int + if (isPlatform(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoPlatform(x, y) + } + else if (isWallSticker(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y) + } + else if (isConnectMutual(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode) + } + else if (isConnectSelf(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile) + } + else { + nearbyTilesInfo = 0 + } + + + val thisTileX = if (!noDamageLayer) + PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo + else + nearbyTilesInfo + + val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE + + + // draw a tile + if (drawModeTilesBlendMul) { + if (BlocksDrawer.isBlendMul(thisTile)) { + batch.color = color + drawTile(batch, mode, x, y, thisTileX, thisTileY) + // TODO: store to the buffer (floatArray) instead of draw right away + } + } + else { + // do NOT add "if (!isBlendMul(thisTile))"! + // or else they will not look like they should be when backed with wall + batch.color = color + drawTile(batch, mode, x, y, thisTileX, thisTileY) + // TODO: store to the buffer (floatArray) instead of draw right away + } + + // draw a breakage + if (mode == TERRAIN || mode == WALL) { + val breakage = if (mode == TERRAIN) world.getTerrainDamage(x, y) else world.getWallDamage(x, y) + val maxHealth = BlockCodex[world.getTileFromTerrain(x, y)].strength + val stage = (breakage / maxHealth).times(breakAnimSteps).roundInt() + // actual drawing + if (stage > 0) { + batch.color = color + drawTile(batch, mode, x, y, 5 + stage, 0) + // TODO: store to the buffer (floatArray) instead of draw right away + } + } + + + } // end if (is illuminated) + } // end if (not an air) + } catch (e: NullPointerException) { + // do nothing. WARNING: This exception handling may hide erratic behaviour completely. + } + + + // hit the end of the current scanline + // FIXME bad scanlines bug + /*if (x == for_x_end) { + val x = x + 1 // because current tile is also counted + batch.color = Color.BLACK + batch.fillRect(x * TILE_SIZEF, y * TILE_SIZEF, -zeroTileCounter * TILE_SIZEF, TILE_SIZEF) + batch.color = color + zeroTileCounter = 0 + }*/ + } + } + + + + // TODO: with the tiles buffer, draw tiles with tiling shader so that GPU would tile itself, COMPUTED IN PARALLEL! + + + + batch.color = originalBatchColour + } + + /** + + * @param x + * * + * @param y + * * + * @return binary [0-15] 1: up, 2: right, 4: down, 8: left + */ + fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int { + val nearbyTiles = IntArray(4) + nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906 + nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Block.NULL + + // try for + var ret = 0 + for (i in 0..3) { + if (nearbyTiles[i] == mark) { + ret += 1 shl i // add 1, 2, 4, 8 for i = 0, 1, 2, 3 + } + } + + return ret + } + + fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int { + val nearbyTiles = IntArray(4) + nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906 + nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Block.NULL + + // try for + var ret = 0 + for (i in 0..3) { + try { + if (!BlockCodex[nearbyTiles[i]].isSolid && + !BlockCodex[nearbyTiles[i]].isFluid) { + ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3 + } + } catch (e: ArrayIndexOutOfBoundsException) { + } + + } + + return ret + } + + fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int { + val nearbyTiles = IntArray(4) + val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP + nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , y) ?: Block.NULL + + try { + if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid) + // has tile on the bottom + return 3 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid + && BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid) + // has tile on both sides + return 0 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid) + // has tile on the right + return 2 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid) + // has tile on the left + return 1 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_BACK]].isSolid) + // has tile on the back + return 0 + else + return 3 + } catch (e: ArrayIndexOutOfBoundsException) { + return if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid) + // has tile on the bottom + 3 else 0 + } + } + + fun getNearbyTilesInfoPlatform(x: Int, y: Int): Int { + val nearbyTiles = IntArray(4) + nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Block.NULL + nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Block.NULL + + if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && + BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid) || + isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT]) && + isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // LR solid || LR platform + return 0 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT]) && + !BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // L solid and not platform && R not solid and not platform + return 4 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT]) && + !BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // R solid and not platform && L not solid and nto platform + return 6 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // L solid && L not platform + return 3 + else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // R solid && R not platform + return 5 + else if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid || + isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) && + !BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // L solid or platform && R not solid and not platform + return 1 + else if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid || + isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) && + !BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && + !isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // R solid or platform && L not solid and not platform + return 2 + else + return 7 + } + + private fun drawTile(batch: SpriteBatch, mode: Int, tilewisePosX: Int, tilewisePosY: Int, sheetX: Int, sheetY: Int) { + if (mode == TERRAIN || mode == WALL) + batch.draw( + tilesTerrain.get(sheetX, sheetY), + tilewisePosX * TILE_SIZEF, + tilewisePosY * TILE_SIZEF + ) + else if (mode == WIRE) + batch.draw( + tilesWire.get(sheetX, sheetY), + tilewisePosX * TILE_SIZEF, + tilewisePosY * TILE_SIZEF + ) + else + throw IllegalArgumentException() + } + + fun clampH(x: Int): Int { + if (x < 0) { + return 0 + } else if (x > world.height * TILE_SIZE) { + return world.height * TILE_SIZE + } else { + return x + } + } + + fun clampWTile(x: Int): Int { + if (x < 0) { + return 0 + } else if (x > world.width) { + return world.width + } else { + return x + } + } + + fun clampHTile(x: Int): Int { + if (x < 0) { + return 0 + } else if (x > world.height) { + return world.height + } else { + return x + } + } + + fun getRenderStartX(): Int = x / TILE_SIZE + fun getRenderStartY(): Int = y / TILE_SIZE + + fun getRenderEndX(): Int = clampWTile(getRenderStartX() + (width / TILE_SIZE) + 2) + fun getRenderEndY(): Int = clampHTile(getRenderStartY() + (height / TILE_SIZE) + 2) + + fun isConnectSelf(b: Int?): Boolean = TILES_CONNECT_SELF.contains(b) + fun isConnectMutual(b: Int?): Boolean = TILES_CONNECT_MUTUAL.contains(b) + fun isWallSticker(b: Int?): Boolean = TILES_WALL_STICKER.contains(b) + fun isPlatform(b: Int?): Boolean = TILES_WALL_STICKER_CONNECT_SELF.contains(b) + fun isBlendMul(b: Int?): Boolean = TILES_BLEND_MUL.contains(b) + + fun tileInCamera(x: Int, y: Int) = + x >= WorldCamera.x.div(TILE_SIZE) && y >= WorldCamera.y.div(TILE_SIZE) && + x <= WorldCamera.x.plus(width).div(TILE_SIZE) && y <= WorldCamera.y.plus(width).div(TILE_SIZE) +} diff --git a/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt b/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt index ae9c09af0..713768e90 100644 --- a/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt +++ b/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt @@ -198,7 +198,7 @@ object LightmapRenderer { * | | 3| |↗ | | ↖| |3 | | * +--+-----+ +--------+ +--------+ +-----+--+ * round: 1 2 3 4 - * for all staticLightMap[y][x] + * for all lightmap[y][x] */ purgeLightmap() diff --git a/work_files/graphics/sprites/npc_template - Copy.psd b/work_files/graphics/sprites/npc_template - Copy.psd new file mode 100644 index 000000000..8f7d81779 --- /dev/null +++ b/work_files/graphics/sprites/npc_template - Copy.psd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f769fc717416e9a55cf890cd9239a4ebc219ed342dfe65c38487428ddad8b0f6 +size 66052 diff --git a/work_files/graphics/sprites/npc_template.psd b/work_files/graphics/sprites/npc_template.psd index 6150c09c1..e5e7b3a09 100644 --- a/work_files/graphics/sprites/npc_template.psd +++ b/work_files/graphics/sprites/npc_template.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cbc64e9d0dce06484e9a464a40a90a5aa0938201488817f4568aa481e2a2db1 -size 79340 +oid sha256:b93615b90fc9f3705184723b88232bdcb5db116ca1d5da37aa46a96f8e1a4780 +size 79386