package net.torvald.terrarum.worlddrawer import com.badlogic.gdx.Gdx import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.utils.GdxRuntimeException import com.jme3.math.FastMath import net.torvald.gdx.graphics.Cvec import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.utils.HashArray import net.torvald.terrarum.worlddrawer.CreateTileAtlas.AtlasSource.* import kotlin.math.roundToInt import kotlin.math.sqrt /** * This class implements work_files/dynamic_shape_2_0.psd * * Tile at (0,0) AND (5,0) must be transparent. Former is because block 0 is considered as an air, and the latter * is because it's breakage of 0, and 0 means no breakage. Breakage part is hard-coded in the tiling shader. * * Any real tiles must begin from (0,16), the first 256x16 section is reserved for special purpose (terrain: breakage, fluid: empty) * * Created by minjaesong on 2019-02-28. */ class CreateTileAtlas { companion object { val WALL_OVERLAY_COLOUR = Color(.65f, .65f, .65f, 1f) } var MAX_TEX_SIZE = App.getConfigInt("atlastexsize").coerceIn(1024, App.glInfo.GL_MAX_TEXTURE_SIZE); private set var TILES_IN_X = MAX_TEX_SIZE / TILE_SIZE; private set var SHADER_SIZE_KEYS = floatArrayOf(MAX_TEX_SIZE.toFloat(), MAX_TEX_SIZE.toFloat(), TILES_IN_X.toFloat(), TILES_IN_X.toFloat()); private set private var TOTAL_TILES = TILES_IN_X * TILES_IN_X lateinit var atlasPrevernal: Pixmap lateinit var atlasVernal: Pixmap lateinit var atlasAestival: Pixmap lateinit var atlasSerotinal: Pixmap lateinit var atlasAutumnal: Pixmap lateinit var atlasHibernal: Pixmap lateinit var atlasFluid: Pixmap lateinit var atlasGlow: Pixmap // glowing won't be affected by the season... for now lateinit var itemTerrainTexture: Texture lateinit var itemTerrainTextureGlow: Texture lateinit var itemWallTexture: Texture lateinit var itemWallTextureGlow: Texture lateinit var terrainTileColourMap: HashMap lateinit var tags: HashMap // TileID, RenderTag private set lateinit var tagsByTileNum: HashArray; private set lateinit var itemSheetNumbers: HashMap // TileID, Int private set private val defaultRenderTag = RenderTag(3, RenderTag.CONNECT_SELF, RenderTag.MASK_NA) // 'update' block var initialised = false private set /** 0.tga, 1.tga.gz, 3242423.tga, 33.tga.gz */ private val tileNameRegex = Regex("""(0|[1-9][0-9]*)\.tga(\.gz)?""") // 16 tiles are reserved for internal use: solid black, solid white, breakage stages. // 0th tile is complete transparent tile and is also a BlockID of zero: air. private var atlasCursor = 64 // 64 predefined tiles. The normal blocks (e.g. Air) should start from this number private val atlasInit = "./assets/graphics/blocks/init.tga" private var itemSheetCursor = 16 internal lateinit var itemTerrainPixmap: Pixmap internal lateinit var itemTerrainPixmapGlow: Pixmap internal lateinit var itemWallPixmap: Pixmap internal lateinit var itemWallPixmapGlow: Pixmap val atlas: Pixmap get() = atlasVernal private fun drawInitPixmap() { val initPixmap = Pixmap(Gdx.files.internal(atlasInit)) val tilesInInitPixmap = (initPixmap.width * initPixmap.height) / (TILE_SIZE * TILE_SIZE) val tilesPossibleInCurrentPixmap = (atlas.width * atlas.height) / (TILE_SIZE * TILE_SIZE) if (tilesInInitPixmap > tilesPossibleInCurrentPixmap) throw Error("Atlas size too small -- can't even fit the init.tga (MAX_TEX_SIZE must be at least ${FastMath.nextPowerOfTwo((sqrt(tilesInInitPixmap.toFloat()) * TILE_SIZE).ceilToInt())})") if (MAX_TEX_SIZE >= initPixmap.width) { atlasPrevernal.drawPixmap(initPixmap, 0, 0) atlasVernal.drawPixmap(initPixmap, 0, 0) atlasAestival.drawPixmap(initPixmap, 0, 0) atlasSerotinal.drawPixmap(initPixmap, 0, 0) atlasAutumnal.drawPixmap(initPixmap, 0, 0) atlasHibernal.drawPixmap(initPixmap, 0, 0) } else { /* What's happening: src: dest: AAAABBBBCCCCDDDD AAAA BBBB CCCC DDDD */ val destX = 0 val srcY = 0 val scanW = MAX_TEX_SIZE val scanH = TILE_SIZE for (scantile in 0 until (initPixmap.width.toFloat() / MAX_TEX_SIZE).ceilToInt()) { val srcX = scantile * scanW val destY = scantile * TILE_SIZE atlasPrevernal.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) atlasVernal.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) atlasAestival.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) atlasSerotinal.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) atlasAutumnal.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) atlasHibernal.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) } } initPixmap.dispose() } /** * Must be called AFTER mods' loading so that all the block props are loaded */ operator fun invoke(updateExisting: Boolean = false) { if (updateExisting || !initialised) { tags = HashMap() tagsByTileNum = HashArray() itemSheetNumbers = HashMap() atlasPrevernal = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasVernal = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasAestival = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasSerotinal = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasAutumnal = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasHibernal = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasFluid = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } atlasGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None } // populate the atlantes with atlasInit // this just directly copies the image to the atlantes :p drawInitPixmap() // get all the files applicable // first, get all the '/blocks' directory, and add all the files, regardless of their extension, to the list // val tgaList = ArrayList>() //Pair of // val tgaListOres = ArrayList>() val tgaList = HashMap>>() // Key: directory name, value: pair of val dirList = listOf("blocks", "ores") dirList.forEach { dirName -> tgaList[dirName] = ArrayList() ModMgr.getGdxFilesFromEveryMod(dirName).forEach { (modname, dir) -> if (!dir.isDirectory) { throw Error("Path '${dir.path()}' is not a directory") } if (dirName == "blocks") { // filter files that do not exist on the blockcodex dir.list() .filter { tgaFile -> tgaFile.extension() == "tga" && !tgaFile.isDirectory && (BlockCodex.getOrNull("$modname:${tgaFile.nameWithoutExtension()}") != null) } .sortedBy { it.nameWithoutExtension().toInt() } .forEach { tgaFile: FileHandle -> // toInt() to sort by the number, not lexicographically // tgaFile be like: ./assets/mods/basegame/blocks/32.tga (which is not always .tga) val newFile = ModMgr.GameRetextureLoader.altFilePaths.getOrDefault(tgaFile.path(), tgaFile) tgaList[dirName]!!.add(modname to newFile) // printdbg(this, "modname = $modname, file = $newFile") } } else { // TODO test dir.list().filter { tgaFile -> tgaFile.extension() == "tga" && !tgaFile.isDirectory }.sortedBy { it.nameWithoutExtension().toInt() }.forEach { tgaFile: FileHandle -> val newFile = ModMgr.GameRetextureLoader.altFilePaths.getOrDefault(tgaFile.path(), tgaFile) tgaList[dirName]!!.add(modname to newFile) } } } } // Sift through the file list for blocks, but TGA format first dirList.forEach { dirName -> tgaList[dirName]!!.forEach { (modname, filehandle) -> printdbg(this, "processing $dirName $modname:${filehandle.name()}") try { val glowFile = Gdx.files.internal( filehandle.path().dropLast(4) + "_glow.tga" ) // assuming strict ".tga" file for now... fileToAtlantes(modname, filehandle, if (glowFile.exists()) glowFile else null, if (dirName == "blocks") null else dirName) } catch (e: GdxRuntimeException) { System.err.println("Couldn't load file $filehandle from $modname, skipping...") } } } // test print // PixmapIO2.writeTGA(Gdx.files.absolute("${App.defaultDir}/atlas.tga"), atlas, false) // PixmapIO2.writeTGA(Gdx.files.absolute("${AppLoader.defaultDir}/atlasGlow.tga"), atlasGlow, false) // Sift throuth the file list, second TGA.GZ /*tgaList.filter { it.name().toUpperCase().endsWith(".TGA.GZ") }.forEach { try { fileToAtlantes(it) } catch (e: GdxRuntimeException) { System.err.println("Couldn't load file $it, skipping...") } }*/ // create item_wall images fun maskTypetoTileIDForItemImage(maskType: Int) = when(maskType) { CreateTileAtlas.RenderTag.MASK_47 -> 17 CreateTileAtlas.RenderTag.MASK_PLATFORM -> 7 else -> 0 } // val itemTerrainPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) // val itemWallPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) itemTerrainPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) itemTerrainPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) itemWallPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) itemWallPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) tags.toMap().forEach { id, tag -> val tilePosFromAtlas = tag.tileNumber + maskTypetoTileIDForItemImage(tag.maskType) val srcX = (tilePosFromAtlas % TILES_IN_X) * TILE_SIZE val srcY = (tilePosFromAtlas / TILES_IN_X) * TILE_SIZE val t = tileIDtoItemSheetNumber(id) val destX = (t % TILES_IN_X) * TILE_SIZE val destY = (t / TILES_IN_X) * TILE_SIZE itemTerrainPixmap.drawPixmap(atlas, srcX, srcY, TILE_SIZE, TILE_SIZE, destX, destY, TILE_SIZE, TILE_SIZE) itemTerrainPixmapGlow.drawPixmap(atlasGlow, srcX, srcY, TILE_SIZE, TILE_SIZE, destX, destY, TILE_SIZE, TILE_SIZE) itemWallPixmap.drawPixmap(atlas, srcX, srcY, TILE_SIZE, TILE_SIZE, destX, destY, TILE_SIZE, TILE_SIZE) itemWallPixmapGlow.drawPixmap(atlasGlow, srcX, srcY, TILE_SIZE, TILE_SIZE, destX, destY, TILE_SIZE, TILE_SIZE) } // darken things for the wall for (y in 0 until itemWallPixmap.height) { for (x in 0 until itemWallPixmap.width) { val c1 = Color(itemWallPixmap.getPixel(x, y)).mulAndAssign(WALL_OVERLAY_COLOUR).toRGBA() itemWallPixmap.drawPixel(x, y, c1) val c2 = Color(itemWallPixmapGlow.getPixel(x, y)).mulAndAssign(WALL_OVERLAY_COLOUR).toRGBA() itemWallPixmapGlow.drawPixel(x, y, c2) } } // create terrain colourmap terrainTileColourMap = HashMap() val pxCount = TILE_SIZE * TILE_SIZE for (id in itemSheetNumbers) { val tilenum = id.value val tx = (tilenum % TILES_IN_X) * TILE_SIZE val ty = (tilenum / TILES_IN_X) * TILE_SIZE var r = 0f; var g = 0f; var b = 0f; var a = 0f // average out the whole block for (y in ty until ty + TILE_SIZE) { for (x in tx until tx + TILE_SIZE) { val data = itemTerrainPixmap.getPixel(x, y) r += ((data ushr 24) and 255).div(255f) g += ((data ushr 16) and 255).div(255f) b += ((data ushr 8) and 255).div(255f) a += (data and 255).div(255f) } } terrainTileColourMap[id.key] = Cvec( (r / pxCount), (g / pxCount), (b / pxCount), (a / pxCount) ) } itemTerrainTexture = Texture(itemTerrainPixmap) itemTerrainTextureGlow = Texture(itemTerrainPixmapGlow) itemWallTexture = Texture(itemWallPixmap) itemWallTextureGlow = Texture(itemWallPixmapGlow) // itemTerrainPixmap.dispose() // itemWallPixmap.dispose() initialised = true } } fun getRenderTag(blockID: ItemID): RenderTag { return tags.getOrDefault(blockID, defaultRenderTag) } fun getRenderTag(tilenum: Int): RenderTag { return tagsByTileNum.getOrDefault(tilenum.toLong(), defaultRenderTag) } val nullTile = Pixmap(TILE_SIZE * 16, TILE_SIZE * 16, Pixmap.Format.RGBA8888) private fun fileToAtlantes(modname: String, matte: FileHandle, glow: FileHandle?, mode: String?) { val tilesPixmap = Pixmap(matte) val tilesGlowPixmap = if (glow != null) Pixmap(glow) else nullTile val blockName = matte.nameWithoutExtension().split('-').last().toInt() // basically a filename val blockID = if (mode != null) "$mode@$modname:$blockName" else "$modname:$blockName" // determine the type of the block (populate tags list) // predefined by the image dimension: 16x16 for (1,0) if (tilesPixmap.width == TILE_SIZE && tilesPixmap.height == TILE_SIZE) { addTag(blockID, RenderTag.CONNECT_SELF, RenderTag.MASK_NA) drawToAtlantes(tilesPixmap, tilesGlowPixmap, RenderTag.maskTypeToTileCount(RenderTag.MASK_NA)) } // predefined by the image dimension: 64x16 for (2,3) else if (tilesPixmap.width == TILE_SIZE * 4 && tilesPixmap.height == TILE_SIZE) { addTag(blockID, RenderTag.CONNECT_WALL_STICKER, RenderTag.MASK_TORCH) drawToAtlantes(tilesPixmap, tilesGlowPixmap, RenderTag.maskTypeToTileCount(RenderTag.MASK_TORCH)) } // predefined by the image dimension: 128x16 for (3,4) else if (tilesPixmap.width == TILE_SIZE * 8 && tilesPixmap.height == TILE_SIZE) { addTag(blockID, RenderTag.CONNECT_WALL_STICKER_CONNECT_SELF, RenderTag.MASK_PLATFORM) drawToAtlantes(tilesPixmap, tilesGlowPixmap, RenderTag.maskTypeToTileCount(RenderTag.MASK_PLATFORM)) } // predefined by the image dimension: 256x16 else if (tilesPixmap.width == TILE_SIZE * 16 && tilesPixmap.height == TILE_SIZE) { addTag(blockID, RenderTag.CONNECT_SELF, RenderTag.MASK_16) drawToAtlantes(tilesPixmap, tilesGlowPixmap, RenderTag.maskTypeToTileCount(RenderTag.MASK_16)) } // 112x112 or 224x224 else { if (tilesPixmap.width != tilesPixmap.height && tilesPixmap.width % (7 * TILE_SIZE) >= 2) { throw IllegalArgumentException("Unrecognized image dimension ${tilesPixmap.width}x${tilesPixmap.height} from $modname:${matte.name()}") } // figure out the tags var connectionType = 0 var maskType = 0 for (bit in 0 until TILE_SIZE) { val x = (7 * TILE_SIZE - 1) - bit val y1 = 5 * TILE_SIZE; val y2 = y1 + 1 val pixel1 = (tilesPixmap.getPixel(x, y1).and(255) >= 128).toInt() val pixel2 = (tilesPixmap.getPixel(x, y2).and(255) >= 128).toInt() connectionType += pixel1 shl bit maskType += pixel2 shl bit } addTag(blockID, connectionType, maskType) val tileCount = RenderTag.maskTypeToTileCount(maskType) drawToAtlantes(tilesPixmap, tilesGlowPixmap, tileCount) } itemSheetNumbers[blockID] = itemSheetCursor itemSheetCursor += 1 tilesPixmap.dispose() } fun tileIDtoAtlasNumber(tileID: ItemID) = tags[tileID]?.tileNumber ?: throw NullPointerException("AtlasNumbers mapping from $tileID does not exist") fun tileIDtoItemSheetNumber(tileID: ItemID) = itemSheetNumbers[tileID] ?: throw NullPointerException("ItemSheetNumber mapping from $tileID does not exist") /** * This function must precede the drawToAtlantes() function, as the marking requires the variable * 'atlasCursor' and the draw function modifies it! */ private fun addTag(id: ItemID, connectionType: Int, maskType: Int) { if (tags.containsKey(id)) { throw Error("Block $id already exists") } tags[id] = RenderTag(atlasCursor, connectionType, maskType) tagsByTileNum[atlasCursor.toLong()] = RenderTag(atlasCursor, connectionType, maskType) printdbg(this, "tileName ${id} ->> tileNumber ${atlasCursor}") } private fun drawToAtlantes(matte: Pixmap, glow: Pixmap, tilesCount: Int) { if (atlasCursor + tilesCount >= TOTAL_TILES) { // throw Error("Too much tiles for $MAX_TEX_SIZE texture size: $atlasCursor") println("[CreateTileAtlas] Too much tiles for atlas of ${MAX_TEX_SIZE}x$MAX_TEX_SIZE (tiles so far: $atlasCursor/${(MAX_TEX_SIZE*MAX_TEX_SIZE)/(TILE_SIZE* TILE_SIZE)}, tiles to be added: $tilesCount), trying to expand the atlas...") expandAtlantes() } val sixSeasonal = matte.width == 21 * TILE_SIZE && matte.height == 14 * TILE_SIZE val txOfPixmap = matte.width / TILE_SIZE val txOfPixmapGlow = glow.width / TILE_SIZE for (i in 0 until tilesCount) { //printdbg(this, "Rendering to atlas, tile# $atlasCursor, tilesCount = $tilesCount, seasonal = $seasonal") // different texture for different seasons (224x224) if (sixSeasonal) { val i = if (i < 41) i else i + 1 // to compensate the discontinuity between 40th and 41st tile _drawToAtlantes(matte, atlasCursor, i % 7 , i / 7, PREVERNAL) _drawToAtlantes(matte, atlasCursor, i % 7 + 7 , i / 7, VERNAL) _drawToAtlantes(matte, atlasCursor, i % 7 + 14, i / 7, AESTIVAL) _drawToAtlantes(matte, atlasCursor, i % 7 + 14, i / 7 + 7, SEROTINAL) _drawToAtlantes(matte, atlasCursor, i % 7 + 7 , i / 7 + 7, AUTUMNAL) _drawToAtlantes(matte, atlasCursor, i % 7 , i / 7 + 7, HIBERNAL) _drawToAtlantes(glow, atlasCursor, i % 7, i / 7, GLOW) atlasCursor += 1 } else { val i = if (i < 41) i else i + 1 // to compensate the discontinuity between 40th and 41st tile _drawToAtlantes(matte, atlasCursor, i % txOfPixmap, i / txOfPixmap, SIX_SEASONS) _drawToAtlantes(glow, atlasCursor, i % txOfPixmapGlow, i / txOfPixmapGlow, GLOW) atlasCursor += 1 } } } /** * mode: 0 for all the atlantes, 1-4 for summer/autumn/winter/spring atlas */ private fun _drawToAtlantes(pixmap: Pixmap, destTileNum: Int, srcTileX: Int, srcTileY: Int, source: AtlasSource) { if (source == SIX_SEASONS) { _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, PREVERNAL) _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, VERNAL) _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, AESTIVAL) _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, SEROTINAL) _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, AUTUMNAL) _drawToAtlantes(pixmap, destTileNum, srcTileX, srcTileY, HIBERNAL) } else { val atlasX = (destTileNum % TILES_IN_X) * TILE_SIZE val atlasY = (destTileNum / TILES_IN_X) * TILE_SIZE val sourceX = srcTileX * TILE_SIZE val sourceY = srcTileY * TILE_SIZE //if (mode == 1) printdbg(this, "atlaspos: ($atlasX, $atlasY), srcpos: ($sourceX, $sourceY), srcpixmap = $pixmap") when (source) { PREVERNAL -> atlasPrevernal.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) VERNAL -> atlasVernal.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) AESTIVAL -> atlasAestival.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) SEROTINAL -> atlasSerotinal.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) AUTUMNAL -> atlasAutumnal.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) HIBERNAL -> atlasHibernal.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) FLUID -> atlasFluid.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) GLOW -> atlasGlow.drawPixmap(pixmap, sourceX, sourceY, TILE_SIZE, TILE_SIZE, atlasX, atlasY, TILE_SIZE, TILE_SIZE) else -> throw IllegalArgumentException("Unknown draw source $source") } } } /** * @param tileNumber ordinal number of a tile in the texture atlas */ data class RenderTag(val tileNumber: Int, val connectionType: Int, val maskType: Int) { companion object { const val CONNECT_MUTUAL = 0 const val CONNECT_SELF = 1 const val CONNECT_WALL_STICKER = 2 const val CONNECT_WALL_STICKER_CONNECT_SELF = 3 const val MASK_NA = 0 const val MASK_16 = 1 const val MASK_47 = 2 const val MASK_TORCH = 3 const val MASK_PLATFORM = 4 fun maskTypeToTileCount(maskType: Int) = when (maskType) { MASK_NA -> 1 MASK_16 -> 16 MASK_47 -> 47 MASK_TORCH -> 4 MASK_PLATFORM -> 8 else -> throw IllegalArgumentException("Unknown maskType: $maskType") } } } fun dispose() { atlasPrevernal.dispose() atlasVernal.dispose() atlasAestival.dispose() atlasSerotinal.dispose() atlasAutumnal.dispose() atlasHibernal.dispose() atlasFluid.dispose() atlasGlow.dispose() //itemTerrainTexture.dispose() //BlocksDrawer will dispose of it as it disposes of 'tileItemTerrain (TextureRegionPack)' //itemTerrainTextureGlow.dispose() //BlocksDrawer will dispose of it as it disposes of 'tileItemTerrain (TextureRegionPack)' //itemWallTexture.dispose() //BlocksDrawer will dispose of it as it disposes of 'tileItemWall (TextureRegionPack)' //itemWallTextureGlow.dispose() //BlocksDrawer will dispose of it as it disposes of 'tileItemWall (TextureRegionPack)' itemTerrainPixmap.dispose() itemWallPixmap.dispose() nullTile.dispose() } private enum class AtlasSource { /*FOUR_SEASONS, SUMMER, AUTUMN, WINTER, SPRING,*/ FLUID, GLOW, SIX_SEASONS, PREVERNAL, VERNAL, AESTIVAL, SEROTINAL, AUTUMNAL, HIBERNAL, } private fun expandAtlantes() { if (MAX_TEX_SIZE >= App.glInfo.GL_MAX_TEXTURE_SIZE) { throw RuntimeException("Cannot expand atlas: texture size is already at its maximum possible size allowed by the graphics processor (${MAX_TEX_SIZE}x${MAX_TEX_SIZE})") } val oldTexSize = MAX_TEX_SIZE val newTexSize = oldTexSize * 2 MAX_TEX_SIZE = newTexSize TILES_IN_X = MAX_TEX_SIZE / TILE_SIZE SHADER_SIZE_KEYS = floatArrayOf(MAX_TEX_SIZE.toFloat(), MAX_TEX_SIZE.toFloat(), TILES_IN_X.toFloat(), TILES_IN_X.toFloat()) TOTAL_TILES = TILES_IN_X * TILES_IN_X val newAtlantes = Array(7) { Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None it.filter = Pixmap.Filter.NearestNeighbour } } listOf(atlasPrevernal, atlasVernal, atlasAestival, atlasSerotinal, atlasAutumnal, atlasHibernal, atlasGlow).forEachIndexed { index, pixmap -> /* How it works: old: new: AAAAAAAA AAAAAAAABBBBBBBB BBBBBBBB CCCCCCCCDDDDDDDD CCCCCCCC ... DDDDDDDD ... */ for (scantile in 0 until pixmap.height / TILE_SIZE) { val srcX = 0 val srcY = scantile * TILE_SIZE val destX = (scantile % 2) * oldTexSize val destY = (scantile / 2) * TILE_SIZE val scanW = pixmap.width val scanH = TILE_SIZE newAtlantes[index].drawPixmap(pixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH) } pixmap.dispose() } atlasPrevernal = newAtlantes[0] atlasVernal = newAtlantes[1] atlasAestival = newAtlantes[2] atlasSerotinal = newAtlantes[3] atlasAutumnal = newAtlantes[4] atlasHibernal = newAtlantes[5] atlasGlow = newAtlantes[6] App.setConfig("atlastexsize", newTexSize) } }