From 415402e61b3d50f07d0fd972f7b25e7fe67d0657 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 19 Apr 2022 12:21:54 -0400 Subject: [PATCH] Better mod sprite packing --- core/src/mindustry/graphics/MultiPacker.java | 20 ++++++++- core/src/mindustry/mod/Mods.java | 45 ++++++++++++++++++-- gradle.properties | 2 +- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/graphics/MultiPacker.java b/core/src/mindustry/graphics/MultiPacker.java index c8e7dd0e88..aa4989e6a8 100644 --- a/core/src/mindustry/graphics/MultiPacker.java +++ b/core/src/mindustry/graphics/MultiPacker.java @@ -26,6 +26,20 @@ public class MultiPacker implements Disposable{ return null; } + public void printStats(){ + for(PageType type : PageType.all){ + var packer = packers[type.ordinal()]; + Log.debug("[Atlas] [&ly@&fr]", type); + Log.debug("[Atlas] - " + (packer.getPages().size > 1 ? "&fb&lr" : "&lg") + "@ page@&r", packer.getPages().size, packer.getPages().size > 1 ? "s" : ""); + int i = 0; + for(var page : packer.getPages()){ + Log.debug("[Atlas] - [@] @x@", i, page.getPixmap().width, page.getPixmap().height); + + i ++; + } + } + } + public PixmapPacker getPacker(PageType type){ return packers[type.ordinal()]; } @@ -78,10 +92,12 @@ public class MultiPacker implements Disposable{ //rubble page - scorch textures for unit deaths & wrecks //ui page (sprites5.png) - content icons, white icons, fonts and UI elements public enum PageType{ - main(4096), + //main page can be massive. + main(8192), + environment(4096, 2048), editor(4096, 2048), - rubble, + rubble(4096, 2048), ui(4096); public static final PageType[] all = values(); diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 0ab4b7b8c4..f5f87f476f 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -182,8 +182,15 @@ public class Mods implements Loadable{ boolean linear = Core.settings.getBool("linear", true); for(Fi file : sprites){ + String name = file.nameWithoutExtension(); + + //TODO !!! document this on the wiki !!! + //do not allow packing standard outline sprites for now, they are no longer necessary and waste space! + if(prefix && name.endsWith("-outline")) continue; + //read and bleed pixmaps in parallel tasks.add(mainExecutor.submit(() -> { + try{ Pixmap pix = new Pixmap(file.readBytes()); //only bleeds when linear filtering is on at startup @@ -192,7 +199,7 @@ public class Mods implements Loadable{ } //this returns a *runnable* which actually packs the resulting pixmap; this has to be done synchronously outside the method return () -> { - packer.add(getPage(file), (prefix ? mod.name + "-" : "") + file.nameWithoutExtension(), new PixmapRegion(pix)); + packer.add(getPage(file), (prefix ? mod.name + "-" : "") + name, new PixmapRegion(pix)); pix.dispose(); }; }catch(Exception e){ @@ -213,11 +220,41 @@ public class Mods implements Loadable{ //get textures packed if(totalSprites > 0){ + class RegionEntry{ + String name; + PixmapRegion region; + int[] splits, pads; + + RegionEntry(String name, PixmapRegion region, int[] splits, int[] pads){ + this.name = name; + this.region = region; + this.splits = splits; + this.pads = pads; + } + } + + Seq[] entries = new Seq[PageType.all.length]; + for(int i = 0; i < PageType.all.length; i++){ + entries[i] = new Seq<>(); + } + for(AtlasRegion region : Core.atlas.getRegions()){ - //TODO PageType completely breaks down with multiple pages. PageType type = getPage(region); + if(!packer.has(type, region.name)){ - packer.add(type, region.name, Core.atlas.getPixmap(region), region.splits, region.pads); + entries[type.ordinal()].add(new RegionEntry(region.name, Core.atlas.getPixmap(region), region.splits, region.pads)); + } + } + + //sort each page type by size first, for optimal packing + for(int i = 0; i < PageType.all.length; i++){ + var rects = entries[i]; + var type = PageType.all[i]; + //TODO is this in reverse order? + rects.sort(Structs.comparingInt(o -> -Math.max(o.region.width, o.region.height))); + + for(var entry : rects){ + packer.add(type, entry.name, entry.region, entry.splits, entry.pads); } } @@ -286,6 +323,8 @@ public class Mods implements Loadable{ } Log.debug("Time to generate icons: @", Time.elapsed()); + packer.printStats(); + //dispose old atlas data Core.atlas = packer.flush(filter, new TextureAtlas()); diff --git a/gradle.properties b/gradle.properties index ca51e02948..bcb2d9e093 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=705de3b8ee +archash=e4b6af5949