Atlas reorg - packer to loader via json (#5014)

This commit is contained in:
SomeTroglodyte
2021-08-29 22:29:24 +02:00
committed by GitHub
parent 3b5489a3b4
commit 9c3e621b15
11 changed files with 62 additions and 36 deletions

View File

Before

Width:  |  Height:  |  Size: 951 B

After

Width:  |  Height:  |  Size: 951 B

View File

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 631 B

View File

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View File

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 832 B

View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

View File

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

View File

@ -0,0 +1,12 @@
{
"paddingX":8,
"paddingY":8,
"duplicatePadding":true,
"maxWidth":2048,
"maxHeight":2048,
"filterMin":"Linear",
"filterMag":"Linear",
"fast":false,
"limitMemory":false,
"combineSubdirectories":true
}

View File

@ -0,0 +1 @@
[Construction,Tech,Skin,Flags]

View File

@ -4,42 +4,42 @@ size: 256, 64
format: RGBA8888
filter: Linear, Linear
repeat: none
checkbox
Skin/checkbox
rotate: false
xy: 160, 23
size: 31, 31
orig: 31, 31
offset: 0, 0
index: -1
checkbox-pressed
Skin/checkbox-pressed
rotate: false
xy: 199, 23
size: 31, 31
orig: 31, 31
offset: 0, 0
index: -1
rectangleWithOutline
Skin/rectangleWithOutline
rotate: false
xy: 64, 13
size: 3, 3
orig: 3, 3
offset: 0, 0
index: -1
roundedEdgeRectangle
Skin/roundedEdgeRectangle
rotate: false
xy: 4, 4
size: 52, 50
orig: 52, 50
offset: 0, 0
index: -1
select-box
Skin/select-box
rotate: false
xy: 64, 24
size: 40, 30
orig: 40, 30
offset: 0, 0
index: -1
select-box-pressed
Skin/select-box-pressed
rotate: false
xy: 112, 24
size: 40, 30

View File

@ -17,6 +17,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.GameSaver
import com.unciv.models.ruleset.Era
import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.Ruleset
@ -42,7 +43,7 @@ object ImageGetter {
// We then shove all the drawables into a hashmap, because the atlas specifically tells us
// that the search on it is inefficient
internal val textureRegionDrawables = HashMap<String, TextureRegionDrawable>()
private val textureRegionDrawables = HashMap<String, TextureRegionDrawable>()
fun resetAtlases() {
atlases.values.forEach { it.dispose() }
@ -61,8 +62,8 @@ object ImageGetter {
textureRegionDrawables[region.name] = drawable
}
// This is a quickfix for #4993, since you can't .list() on a jar file. This should be fixed in a nicer way.
val fileNames = listOf("game","Flags","Tech","Skin","Construction")
// See #4993 - you can't .list() on a jar file, so the ImagePacker leaves us the list of actual atlases.
val fileNames = GameSaver.json().fromJson(Array<String>::class.java, Gdx.files.internal("Atlases.json"))
for (fileName in fileNames) {
val file = Gdx.files.internal("$fileName.atlas")
val extraAtlas = file.nameWithoutExtension()
@ -70,10 +71,9 @@ object ImageGetter {
?: TextureAtlas(file.name()).apply { // load if not
atlases[extraAtlas] = this // cache the freshly loaded
}
val prefix = if (extraAtlas == "Skin") "Skin/" else "" // Only Skin is packed without folder prefix
for (region in tempAtlas.regions) {
val drawable = TextureRegionDrawable(region)
textureRegionDrawables[prefix + region.name] = drawable
textureRegionDrawables[region.name] = drawable
}
}
@ -114,7 +114,7 @@ object ImageGetter {
* getLayeredImageColored("TileSets/FantasyHex/Units/Warrior", null, Color.GOLD, Color.RED)
*
* All images in the atlas that match the pattern "TileSets/FantasyHex/Units/Warrior" or
* "TileSets/FantasyHex/Units/Warrior-NUMBER" are retrieved. NUMBERs must start from 1 and
* "TileSets/FantasyHex/Units/Warrior-NUMBER" are retrieved. NUMBER must start from 1 and
* be incremented by 1 per layer. If the n-th NUMBER is missing, the (n-1)-th layer is the
* last one retrieved:
* Given the layer names:

View File

@ -2,9 +2,12 @@ package com.unciv.app.desktop
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.tools.texturepacker.TexturePacker
import com.badlogic.gdx.utils.Json
import java.io.File
/**
* Entry point: _ImagePacker.[packImages] ()_
*
* Re-packs our texture assets into atlas + png File pairs, which will be loaded by the game.
* With the exception of the ExtraImages folder and the Font system these are the only
* graphics used (The source Image folders are unused at run time except here).
@ -12,11 +15,7 @@ import java.io.File
* [TexturePacker] documentation is [here](https://github.com/libgdx/libgdx/wiki/Texture-packer)
*/
internal object ImagePacker {
fun packImages() {
val startTime = System.currentTimeMillis()
val settings = TexturePacker.Settings()
private fun getDefaultSettings() = TexturePacker.Settings().apply {
// Apparently some chipsets, like NVIDIA Tegra 3 graphics chipset (used in Asus TF700T tablet),
// don't support non-power-of-two texture sizes - kudos @yuroller!
// https://github.com/yairm210/UnCiv/issues/1340
@ -35,33 +34,38 @@ internal object ImagePacker {
*
* TL;DR this should be 2048.
*/
settings.maxWidth = 2048
settings.maxHeight = 2048
maxWidth = 2048
maxHeight = 2048
// Trying to disable the subdirectory combine lead to even worse results. Don't.
settings.combineSubdirectories = true
settings.pot = true // powers of two only for width/height
settings.fast = true // with pot on this just resorts by width
combineSubdirectories = true
pot = true // powers of two only for width/height
fast = true // with pot on this just sorts by width
// settings.rotation - do not set. Allows rotation, potentially packing tighter.
// Proper rendering is mostly automatic - except borders which overwrite rotation.
// Set some additional padding and enable duplicatePadding to prevent image edges from bleeding into each other due to mipmapping
settings.paddingX = 8
settings.paddingY = 8
settings.duplicatePadding = true
settings.filterMin = Texture.TextureFilter.MipMapLinearLinear
settings.filterMag = Texture.TextureFilter.MipMapLinearLinear // I'm pretty sure this doesn't make sense for magnification, but setting it to Linear gives strange results
paddingX = 8
paddingY = 8
duplicatePadding = true
filterMin = Texture.TextureFilter.MipMapLinearLinear
filterMag = Texture.TextureFilter.MipMapLinearLinear // I'm pretty sure this doesn't make sense for magnification, but setting it to Linear gives strange results
}
fun packImages() {
val startTime = System.currentTimeMillis()
val defaultSettings = getDefaultSettings()
// Scan for Image folders and build one atlas each
if (File("../Images").exists()) { // So we don't run this from within a fat JAR
val atlasList = mutableListOf<String>()
for ((file, packFileName) in imageFolders()) {
packImagesIfOutdated(settings, file, ".", packFileName)
atlasList += packFileName
packImagesIfOutdated(defaultSettings, file, ".", packFileName)
}
}
if (File("../Skin").exists()) {
settings.filterMag = Texture.TextureFilter.Linear
settings.filterMin = Texture.TextureFilter.Linear
packImagesIfOutdated(settings, "../Skin", ".", "Skin")
atlasList.remove("game")
File("Atlases.json").writeText(atlasList.joinToString(",","[","]"))
}
// pack for mods as well
@ -69,7 +73,7 @@ internal object ImagePacker {
if (modDirectory.exists()) {
for (mod in modDirectory.listFiles()!!) {
if (!mod.isHidden && File(mod.path + "/Images").exists())
packImagesIfOutdated(settings, mod.path + "/Images", mod.path, "game")
packImagesIfOutdated(defaultSettings, mod.path + "/Images", mod.path, "game")
}
}
@ -77,22 +81,31 @@ internal object ImagePacker {
println("Packing textures - " + texturePackingTime + "ms")
}
private fun packImagesIfOutdated(settings: TexturePacker.Settings, input: String, output: String, packFileName: String) {
// Process one Image folder, checking for atlas older than contained images first
private fun packImagesIfOutdated(defaultSettings: TexturePacker.Settings, input: String, output: String, packFileName: String) {
fun File.listTree(): Sequence<File> = when {
this.isFile -> sequenceOf(this)
this.isDirectory -> this.listFiles()!!.asSequence().flatMap { it.listTree() }
else -> sequenceOf()
}
// Check if outdated
val atlasFile = File("$output${File.separator}$packFileName.atlas")
if (atlasFile.exists() && File("$output${File.separator}$packFileName.png").exists()) {
val atlasModTime = atlasFile.lastModified()
if (File(input).listTree().none { it.extension in listOf("png", "jpg", "jpeg") && it.lastModified() > atlasModTime }) return
}
// An image folder can optionally have a TexturePacker settings file
val settingsFile = File("$input${File.separator}TexturePacker.settings")
val settings = if (settingsFile.exists())
Json().fromJson(TexturePacker.Settings::class.java, settingsFile.reader())
else defaultSettings
TexturePacker.process(settings, input, output, packFileName)
}
// Iterator providing all Image folders to process with the destination atlas name
private data class ImageFolderResult(val folder: String, val atlasName: String)
private fun imageFolders() = sequence {
val parent = File("..")