Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
bd660c9
Add KorgeFleksExtension
Nov 21, 2025
a836323
Start to add CommonAssetConfig
jobe-m Nov 21, 2025
da3d0b7
Cleanup KorgeFleksExtension, move out AssetConfig into own file
jobe-m Nov 23, 2025
b3ed1f9
Add unit test for AseLayer in ASEInfo
jobe-m Nov 24, 2025
163b8a8
Implement addImageAse for all layer and tag combinations
jobe-m Nov 24, 2025
14f015f
Add build atlas and nine-patch functions
jobe-m Nov 25, 2025
fd17d81
Refactor packImages and add packTilesets function
jobe-m Nov 27, 2025
18c57d2
Update AssetConfig to test packTilesets function
jobe-m Nov 27, 2025
63c6903
Fix error in AssetConfig whem export dir does not exist
jobe-m Nov 28, 2025
1ae9fbf
[NewTexturePacker] Add TODO comment
jobe-m Nov 28, 2025
aeb3bc4
Cleanup NewTexturePacker
jobe-m Dec 1, 2025
91cdcfc
Start to add AssetInfo data class
jobe-m Dec 2, 2025
32817e4
Image tiles added to AssetInfo
jobe-m Dec 4, 2025
72e75a9
Write yaml assetInfo file
jobe-m Dec 5, 2025
cd6794a
Update putting images into textureAtlas
jobe-m Dec 9, 2025
4854180
Save for each frame index of tileset where it is located
jobe-m Dec 10, 2025
031d47c
Write texture index for frame into yaml atlas info
jobe-m Dec 11, 2025
f414125
Remove deprecated gradle properties
jobe-m Dec 12, 2025
a4bd0b9
Add frame duration into asssetInfo and related unit test
jobe-m Dec 12, 2025
74b4686
Get frame duration actually written into yaml asset config
jobe-m Dec 12, 2025
5be3b2c
Change asset info format to JSON
jobe-m Dec 13, 2025
4f5b3b2
Add nine-patch image loading into asset into and texture atlas
jobe-m Dec 14, 2025
dd13780
Add pixel fonts into texture atlas
jobe-m Dec 15, 2025
d6810a7
Add frame info for pixel fonts into asset config
jobe-m Dec 16, 2025
57089f7
Start adding parallax info config to korge-gradle-plugin
jobe-m Dec 17, 2025
6bdea52
Add parallax config into asset json info
jobe-m Dec 18, 2025
42e3a9c
Minimize serial names for image info in asset config
jobe-m Dec 19, 2025
4bc1915
Add parallax plane line textures to asset info
jobe-m Dec 19, 2025
986c885
Update export of parallax related images and slices
jobe-m Dec 23, 2025
420bebe
Finish parallax layer config saving into Asset JSON info
jobe-m Dec 25, 2025
f764b17
Cleanup ImageAseExporter
jobe-m Dec 26, 2025
75482f8
Add worldLevelChunkAssets type and make sure resource folder exists
jobe-m Dec 26, 2025
87cc1ac
Start to add building tilesets into atlas JSON info
jobe-m Dec 27, 2025
4144fdf
Add saving tileset into into asset atlas JSON info
jobe-m Dec 27, 2025
e01fdf4
Update asset tileset info
jobe-m Dec 31, 2025
de2448d
Identify empty tiles in tilesets
jobe-m Dec 31, 2025
d94f7ec
Start to work on LevelMap LDtk importer
jobe-m Jan 6, 2026
0cc0cd0
Fix bug with image size in Korge-gradle-plugin when loading parallax …
jobe-m Jan 17, 2026
d7a23d4
Start to add tile map LDtk loader
jobe-m Jan 28, 2026
8f746a0
Refactor asset pipeline and simplify it
jobe-m Jan 30, 2026
098f8f0
Add new Gradle task for loading LDtk levels - WIP
jobe-m Jan 30, 2026
84ebdf6
Fix unit tests
jobe-m Feb 1, 2026
ccc2a64
Merge branch 'main' into feature/Add-korge-fleks-extension-to-gradle-…
jobe-m Feb 1, 2026
e6c36b4
Fix review findings
jobe-m Feb 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ project.license.url=https://raw.githubusercontent.com/korlibs/korge-next/master/
#kotlin.mpp.enableCInteropCommonization.nowarn=true
#kotlin.mpp.enableCInteropCommonization=true
kotlin.mpp.stability.nowarn=true
kotlin.wasm.stability.nowarn=true
org.gradle.warning.mode=none

kotlin.native.ignoreIncorrectDependencies=true
kotlin.native.ignoreDisabledTargets=true
#kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.androidSourceSetLayoutVersion1.nowarn=true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
package korlibs.korge.gradle.korgefleks

import com.android.build.gradle.internal.cxx.json.jsonStringOf
import kotlinx.kover.util.json.readJsonArray
import java.io.File


/**
* Configuration for managing assets in a KorgeFleks project.
*
* This class is a convenience wrapper around AssetImageLoader.
*
* @param asepriteExe The path to the Aseprite executable.
* @param projectDir The root directory of the project.
* @param assetPath The relative path to the directory containing asset files.
* @param resourcePath The relative path to the directory for game resources.
*/
open class AssetConfig(
private val asepriteExe: String,
projectDir: File,
assetPath: String,
resourcePath: String
) {
var textureAtlasName: String = "texture"
var tilesetAtlasName: String = "tileset"
var simplifyJson: Boolean = true

var tileWidth: Int = 16
var tileHeight: Int = 16
var atlasWidth: Int = 2048
var atlasHeight: Int = 2048
var atlasPadding: Int = 1

companion object {
internal const val VERSION = "version"
internal const val TEXTURES = "textures"
internal const val TILESETS = "tilesets"
internal const val IMAGES = "images"
internal const val NINE_PATCHES = "ninePatches"
internal const val PIXEL_FONTS = "pixelFonts"
internal const val PARALLAX_LAYERS = "parallaxLayers"
internal const val TILES = "tiles"
}

// Directory where Aseprite files are located
protected val assetDir = projectDir.resolve(assetPath)
// Directory where exported tiles and tilesets will be stored
protected val exportTilesDir = projectDir.resolve("build/assets/imageAtlasInput")
// Directory where game resources are located
protected val gameResourcesDir = projectDir.resolve("src/commonMain/resources/${resourcePath}")

protected val assetInfo = linkedMapOf<String, Any>()

init {
// Make sure the export directories exist and that they are empty
if (exportTilesDir.exists()) exportTilesDir.deleteRecursively()
exportTilesDir.mkdirs()

// Set version info
val major = 1
val minor = 0
val build = 1
assetInfo[VERSION] = arrayOf(major, minor, build)

// Initialize maps and lists in asset info
assetInfo[TEXTURES] = arrayListOf<String>()
assetInfo[TILESETS] = arrayListOf<String>()
assetInfo[IMAGES] = linkedMapOf<String, Any>()
assetInfo[NINE_PATCHES] = linkedMapOf<String, Any>()
assetInfo[PIXEL_FONTS] = linkedMapOf<String, Any>()
assetInfo[PARALLAX_LAYERS] = linkedMapOf<String, Any>()
assetInfo[TILES] = linkedMapOf<String, Any>()
}

protected val assetImageAseExporter = AssetImageAseExporter(asepriteExe, assetDir, exportTilesDir, assetInfo)
protected val assetFileInstaller = AssetFileInstaller(assetDir, exportTilesDir, gameResourcesDir, assetInfo)
protected val assetImageAtlasBuilder = AssetImageAtlasBuilder(exportTilesDir, gameResourcesDir, assetInfo)
protected val assetLevelMapExporter = AssetLevelMapExporter(assetDir, gameResourcesDir, assetInfo)

/**
* Export specific layers and tags from Aseprite file as independent png images.
* Adds exported images to internal asset info list.
*/
fun addImageAse(filename: String, layers: List<String>, tags: List<String>, output: String) {
assetImageAseExporter.addImageAse(filename, layers, tags, output)
}

/** Export full image from Aseprite file
*/
fun addImageAse(filename: String, output: String) {
assetImageAseExporter.addImageAse(filename, emptyList(), emptyList(), output )
}

/** Export specific layer from Aseprite file
*/
fun addImageAse(filename: String, layer: String, output: String) {
assetImageAseExporter.addImageAse(filename, listOf(layer), emptyList(), output)
}

/** Export specific layer and tag from Aseprite file
*/
fun addImageAse(filename: String, layer: String, tag: String, output: String) {
assetImageAseExporter.addImageAse(filename, listOf(layer), listOf(tag), output)
}

/** Export specific layer and tags from Aseprite file
*/
fun addImageAse(filename: String, layer: String, tags: List<String>, output: String) {
assetImageAseExporter.addImageAse(filename, listOf(layer), tags, output)
}

/** Export specific layers from Aseprite file
*/
fun addImageAse(filename: String, layers: List<String>, output: String) {
assetImageAseExporter.addImageAse(filename, layers, emptyList(), output)
}

/** Export specific layers and tag from Aseprite file
*/
fun addImageAse(filename: String, layers: List<String>, tag: String, output: String) {
assetImageAseExporter.addImageAse(filename, layers, listOf(tag), output)
}

/**
* Export nine-patch image from Aseprite file.
* Adds exported nine-patch image to internal asset info list.
*/
fun addNinePatchImageAse(filename: String, output: String) {
assetImageAseExporter.addNinePatchImageAse(filename, emptyList(), emptyList(), output)
}

/**
* Export pixel font file and associated pixel font image.
* It copies font file to game resources and exports font image to assets folder for atlas packing.
*/
fun addPixelFont(filename: String) {
assetFileInstaller.addPixelFont(filename)
}

/**
* Export parallax layer images from Aseprite file.
* Adds exported parallax images to internal asset info list.
*/
fun addParallaxImageAse(filename: String, parallaxInfo: ParallaxInfo) {
assetImageAseExporter.addParallaxImageAse(filename, parallaxInfo)
}

/**
* Add a generic file to the asset configuration.
* Copies the file to the game resources' directory.
*/
fun addFile(filename: String) {
assetFileInstaller.addFile(filename)
}

/**
* Build texture and tileset atlases from exported images.
* Uses atlas names and sizes from the AssetConfig properties.
*
* This will always be called as last step after all assets have been added.
*/
internal open fun buildAssetStore() {
// First build the image and tileset atlases
assetImageAtlasBuilder.buildAtlases(
textureAtlasName,
atlasWidth,
atlasHeight,
atlasPadding
)
// Finally, write out the asset info as JSON file
writeAssetInfoJson()
}

protected fun writeAssetInfoJson() {
val assetInfoJsonFile = gameResourcesDir.resolve("${textureAtlasName}.atlas.json")
assetInfoJsonFile.parentFile?.let { parent ->
if (!parent.exists() && !parent.mkdirs()) error("Failed to create directory: ${parent.path}")
val jsonString = jsonStringOf(assetInfo)
// Simplify JSON string by removing unnecessary spaces and line breaks
val simplifiedJsonString = if (simplifyJson) jsonString.replace(Regex("\\s+"), "")
Comment thread
jobe-m marked this conversation as resolved.
else jsonString
assetInfoJsonFile.writeText(simplifiedJsonString)
}
}
}

class WorldClusterAssetConfig(
asepriteExe: String,
projectDir: File,
assetPath: String,
resourcePath: String
) : AssetConfig(asepriteExe, projectDir, assetPath, resourcePath) {

private val tileSetFiles: MutableList<File> = mutableListOf()
private val exportTilesetDir = projectDir.resolve("build/assets/tilesetAtlasInput")

// Directory with cluster asset info files (e.g. world.json, intro.json, etc.)
private val clusterAssetInfoDir = projectDir.resolve("gradle/worldClusterAssetInfo")

private val assetTilesetExporter = AssetTilesetExporter(assetDir, exportTilesetDir, tileSetFiles)
private val assetTilesetAtlasBuilder = AssetTilesetAtlasBuilder(exportTilesetDir, gameResourcesDir, assetInfo, tileSetFiles, clusterAssetInfoDir)

init {
// Make sure the export directories exist and that they are empty
if (exportTilesDir.exists()) exportTilesDir.deleteRecursively()
if (exportTilesetDir.exists()) exportTilesetDir.deleteRecursively()
exportTilesDir.mkdirs()
exportTilesetDir.mkdirs()
}

/**
* Export single tiles from a png tileset file and stores them in a tileset atlas.
* Adds exported tiles and tileset images to internal asset info list.
*/
fun addTilesetImagePng(filename: String) {
assetTilesetExporter.addTilesetImagePng(filename)
}

/**
* Export single level from LDtk file as an TileMap object.
* Adds exported level map to internal asset info list.
*
* @param filename The LDtk file containing the level data.
* @param levelName The name of the level to export.
*/
fun addTileMapLDtkFile(filename: String, levelName: String) {
// TODO Get data for all clusters and extract used tilesets
val tileSetsPerClusterMap = mapOf<String, List<String>>()

assetLevelMapExporter.exportTileMapLDtk(filename, levelName, tileSetsPerClusterMap)
}

/**
* Build texture and tileset atlases from exported images.
* Uses atlas names and sizes from the AssetConfig properties.
*
* This will always be called as last step after all assets have been added.
*
* @param assetResourcePath The relative path to the directory for game resources. The name of the last folder is the cluster name.
*/
fun buildAssetStore(assetResourcePath: String) {
// First build the image and tileset atlases
assetImageAtlasBuilder.buildAtlases(
textureAtlasName,
atlasWidth,
atlasHeight,
atlasPadding
)
assetTilesetAtlasBuilder.buildTilesetAtlas(
assetResourcePath,
tilesetAtlasName,
tileWidth,
tileHeight,
atlasWidth,
atlasHeight,
atlasPadding
)
// Finally, write out the asset info as JSON file
writeAssetInfoJson()
}
}

class WorldLDtkLevelMapAssetConfig(
projectDir: File,
world: Int,
private val levelMapFilePath: String,
resourcePath: String
) {
// Directory with cluster asset info files (e.g. world.json, intro.json, etc.)
private val clusterAssetInfoDir = projectDir.resolve("gradle/worldClusterAssetInfo/world_${world}")
// Directory where game resources are located
private val gameResourcesDir = projectDir.resolve("src/commonMain/resources/${resourcePath}")
private val assetLevelMapExporter = AssetLevelMapExporter(projectDir, gameResourcesDir, linkedMapOf())

/**
* Export level map from LDtk file as chunked level map.
* Adds exported level map to internal asset info list.
*/
fun buildAssetStore() {
// Get the info which tileset are available in each processed asset cluster
if (clusterAssetInfoDir.listFiles() != null && clusterAssetInfoDir.listFiles().isNotEmpty()) {
val clusterAssetInfoFiles = clusterAssetInfoDir.listFiles()!!.filter { it.extension == "json" }

val tileSetsPerClusterMap: Map<String, List<String>> = clusterAssetInfoFiles.associate { file ->
val clusterName = file.nameWithoutExtension
val tileSetsList: List<String> = file.readJsonArray().let { jsonArray ->
jsonArray.map { it as String }
}
clusterName to tileSetsList
}

println("Tilesets per cluster: $tileSetsPerClusterMap")

// TODO change to support world chunks
assetLevelMapExporter.exportLevelMapLDtk(levelMapFilePath, tileSetsPerClusterMap)
} else error("ERROR: No cluster asset info files found. Please run first all world cluster asset tasks before running level map asset task.")
}
}
Loading
Loading