Texture overrides / Potential mod texture binding optimizations

This commit is contained in:
Anuken
2019-11-25 20:50:32 -05:00
parent 0dd8267e21
commit a5fbc07561
15 changed files with 1643 additions and 1529 deletions

View File

@ -1,4 +1,4 @@
![Logo](core/assets/sprites/logo.png)
![Logo](core/assets-raw/sprites/ui/logo.png)
[![Build Status](https://travis-ci.org/Anuken/Mindustry.svg?branch=master)](https://travis-ci.org/Anuken/Mindustry)
[![Discord](https://img.shields.io/discord/391020510269669376.svg)](https://discord.gg/mindustry)

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 KiB

After

Width:  |  Height:  |  Size: 924 KiB

View File

@ -5,6 +5,7 @@ import io.anuke.arc.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.ui.Cicon;
/** Base interface for an unlockable content type. */
@ -25,7 +26,7 @@ public abstract class UnlockableContent extends MappableContent{
/** Generate any special icons for this content. Called asynchronously.*/
@CallSuper
public void createIcons(PixmapPacker out, PixmapPacker editor){
public void createIcons(MultiPacker packer){
}

View File

@ -15,6 +15,7 @@ import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.EventType.BlockDestroyEvent;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.consumers.*;
import io.anuke.mindustry.world.modules.*;
import java.io.*;
@ -89,7 +90,7 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
/** Base efficiency. If this entity has non-buffered power, returns the power %, otherwise returns 1. */
public float efficiency(){
return power != null && !block.consumes.getPower().buffered ? power.status : 1f;
return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f;
}
/** Call when nothing is happening to the entity. This increments the internal sleep timer. */

View File

@ -0,0 +1,56 @@
package io.anuke.mindustry.graphics;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Pixmap.*;
import io.anuke.arc.graphics.Texture.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.util.*;
public class MultiPacker implements Disposable{
private PixmapPacker[] packers = new PixmapPacker[PageType.all.length];
public MultiPacker(){
//TODO 4096 may be a better choice
int pageSize = 2048;
for(int i = 0; i < packers.length; i++){
packers[i] = new PixmapPacker(pageSize, pageSize, Format.RGBA8888, 2, true);
}
}
public boolean has(PageType type, String name){
return packers[type.ordinal()].getRect(name) != null;
}
public void add(PageType type, String name, PixmapRegion region){
packers[type.ordinal()].pack(name, region);
}
public void add(PageType type, String name, Pixmap pix){
packers[type.ordinal()].pack(name, pix);
}
public TextureAtlas flush(TextureFilter filter, TextureAtlas atlas){
for(PixmapPacker p : packers){
p.updateTextureAtlas(atlas, filter, filter, false, false);
}
return atlas;
}
@Override
public void dispose(){
for(PixmapPacker packer : packers){
packer.dispose();
}
}
public enum PageType{
main,
environment,
editor,
zone,
ui;
public static final PageType[] all = values();
}
}

View File

@ -228,6 +228,9 @@ public class ContentParser{
postreads.add(() -> {
TechNode parnode = TechTree.all.find(t -> t.block == parent);
if(parnode == null){
throw new ModLoadException("Block '" + parent.name + "' isn't in the tech tree, but '" + block.name + "' requires it to be researched.", block);
}
if(!parnode.children.contains(baseNode)){
parnode.children.add(baseNode);
}

View File

@ -6,7 +6,6 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.func.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Pixmap.*;
import io.anuke.arc.graphics.Texture.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.graphics.g2d.TextureAtlas.*;
@ -19,6 +18,8 @@ import io.anuke.mindustry.core.*;
import io.anuke.mindustry.ctype.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.graphics.MultiPacker.*;
import io.anuke.mindustry.plugin.*;
import io.anuke.mindustry.type.*;
@ -34,7 +35,7 @@ public class Mods implements Loadable{
private ObjectSet<String> specialFolders = ObjectSet.with("bundles", "sprites");
private int totalSprites;
private PixmapPacker packer;
private MultiPacker packer;
private Array<LoadedMod> loaded = new Array<>();
private Array<LoadedMod> disabled = new Array<>();
@ -80,68 +81,71 @@ public class Mods implements Loadable{
if(loaded.isEmpty()) return;
Time.mark();
packer = new PixmapPacker(2048, 2048, Format.RGBA8888, 2, true);
packer = new MultiPacker();
for(LoadedMod mod : loaded){
int[] packed = {0};
boolean[] failed = {false};
mod.root.child("sprites").walk(file -> {
if(failed[0]) return;
if(file.extension().equals("png")){
try(InputStream stream = file.read()){
byte[] bytes = Streams.copyStreamToByteArray(stream, Math.max((int)file.length(), 512));
Pixmap pixmap = new Pixmap(bytes, 0, bytes.length);
packer.pack(mod.name + "-" + file.nameWithoutExtension(), pixmap);
pixmap.dispose();
packed[0] ++;
totalSprites ++;
}catch(IOException e){
failed[0] = true;
Core.app.post(() -> {
Log.err("Error packing images for mod: {0}", mod.meta.name);
e.printStackTrace();
if(!headless) ui.showException(e);
});
}
}
});
Log.info("Packed {0} images for mod '{1}'.", packed[0], mod.meta.name);
Array<FileHandle> sprites = mod.root.child("sprites").findAll(f -> f.extension().equals("png"));
Array<FileHandle> overrides = mod.root.child("sprites-override").findAll(f -> f.extension().equals("png"));
packSprites(sprites, mod, true);
packSprites(overrides, mod, false);
Log.info("Packed {0} images for mod '{1}'.", sprites.size + overrides.size, mod.meta.name);
totalSprites += sprites.size + overrides.size;
}
for(AtlasRegion region : Core.atlas.getRegions()){
PageType type = getPage(region);
if(!packer.has(type, region.name)){
packer.add(type, region.name, Core.atlas.getPixmap(region));
}
}
Log.info("Time to pack textures: {0}", Time.elapsed());
}
private void packSprites(Array<FileHandle> sprites, LoadedMod mod, boolean prefix){
for(FileHandle file : sprites){
try(InputStream stream = file.read()){
byte[] bytes = Streams.copyStreamToByteArray(stream, Math.max((int)file.length(), 512));
Pixmap pixmap = new Pixmap(bytes, 0, bytes.length);
packer.add(getPage(file), (prefix ? mod.name + "-" : "") + file.nameWithoutExtension(), new PixmapRegion(pixmap));
pixmap.dispose();
}catch(IOException e){
Core.app.post(() -> {
Log.err("Error packing images for mod: {0}", mod.meta.name);
e.printStackTrace();
if(!headless) ui.showException(e);
});
break;
}
}
totalSprites += sprites.size;
}
@Override
public void loadSync(){
if(packer == null) return;
Time.mark();
Texture editor = Core.atlas.find("clear-editor").getTexture();
PixmapPacker editorPacker = new PixmapPacker(2048, 2048, Format.RGBA8888, 2, true);
for(AtlasRegion region : Core.atlas.getRegions()){
if(region.getTexture() == editor){
editorPacker.pack(region.name, Core.atlas.getPixmap(region).crop());
}
}
//get textures packed
if(totalSprites > 0){
TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.Linear : TextureFilter.Nearest;
packer.updateTextureAtlas(Core.atlas, filter, filter, false);
//flush so generators can use these sprites
packer.flush(filter, Core.atlas);
//generate new icons
for(Array<Content> arr : content.getContentMap()){
arr.each(c -> {
if(c instanceof UnlockableContent && c.mod != null){
UnlockableContent u = (UnlockableContent)c;
u.createIcons(packer, editorPacker);
u.createIcons(packer);
}
});
}
editorPacker.updateTextureAtlas(Core.atlas, filter, filter, false);
packer.updateTextureAtlas(Core.atlas, filter, filter, false);
Core.atlas = packer.flush(filter, new TextureAtlas());
Core.atlas.setErrorRegion("error");
Log.info("Total pages: {0}", Core.atlas.getTextures().size);
}
packer.dispose();
@ -149,6 +153,33 @@ public class Mods implements Loadable{
Log.info("Time to update textures: {0}", Time.elapsed());
}
//There are several pages for sprites.
//main page (sprites.png) - all sprites for units, weapons, placeable blocks, effects, bullets, etc
//environment page (sprites2.png) - all sprites for things in the environmental cache layer
//editor page (sprites3.png) - all sprites needed for rendering in the editor, including block icons and a few minor sprites
//zone page (sprites4.png) - zone previews
//ui page (sprites5.png) - content icons, white icons and UI elements
private PageType getPage(AtlasRegion region){
return
region.getTexture() == Core.atlas.find("white").getTexture() ? PageType.main :
region.getTexture() == Core.atlas.find("stone1").getTexture() ? PageType.environment :
region.getTexture() == Core.atlas.find("clear-editor").getTexture() ? PageType.editor :
region.getTexture() == Core.atlas.find("zone-groundZero").getTexture() ? PageType.zone :
region.getTexture() == Core.atlas.find("whiteui").getTexture() ? PageType.ui :
PageType.main;
}
private PageType getPage(FileHandle file){
String parent = file.parent().name();
return
parent.equals("environment") ? PageType.environment :
parent.equals("editor") ? PageType.editor :
parent.equals("zones") ? PageType.zone :
parent.equals("ui") || file.parent().parent().name().equals("ui") ? PageType.ui :
PageType.main;
}
/** Removes a mod file and marks it for requiring a restart. */
public void removeMod(LoadedMod mod){
if(mod.root instanceof ZipFileHandle){
@ -679,5 +710,13 @@ public class Mods implements Loadable{
this.mod = content.mod;
}
}
public ModLoadException(String message, @Nullable Content content){
super(message);
this.content = content;
if(content != null){
this.mod = content.mod;
}
}
}
}

View File

@ -17,7 +17,6 @@ import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.ui.*;
import static io.anuke.arc.Core.assets;
import static io.anuke.mindustry.Vars.*;
public class MenuFragment extends Fragment{
@ -26,8 +25,6 @@ public class MenuFragment extends Fragment{
private MenuRenderer renderer;
public MenuFragment(){
assets.load("sprites/logo.png", Texture.class);
assets.finishLoading();
Events.on(DisposeEvent.class, event -> {
renderer.dispose();
});
@ -67,7 +64,7 @@ public class MenuFragment extends Fragment{
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));
parent.fill((x, y, w, h) -> {
Texture logo = Core.assets.get("sprites/logo.png");
TextureRegion logo = Core.atlas.find("logo");
float logoscl = Scl.scl(1);
float logow = Math.min(logo.getWidth() * logoscl, Core.graphics.getWidth() - Scl.scl(20));
float logoh = logow * (float)logo.getHeight() / logo.getWidth();
@ -76,7 +73,7 @@ public class MenuFragment extends Fragment{
float fy = (int)(Core.graphics.getHeight() - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f);
Draw.color();
Draw.rect(Draw.wrap(logo), fx, fy, logow, logoh);
Draw.rect(logo, fx, fy, logow, logoh);
Fonts.def.setColor(Color.white);
Fonts.def.draw(versionText, fx, fy - logoh/2f, Align.center);

View File

@ -24,6 +24,7 @@ import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.graphics.MultiPacker.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.world.blocks.*;
@ -755,10 +756,10 @@ public class Block extends BlockStorage{
}
@Override
public void createIcons(PixmapPacker packer, PixmapPacker editor){
super.createIcons(packer, editor);
public void createIcons(MultiPacker packer){
super.createIcons(packer);
editor.pack(name + "-icon-editor", Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)).crop());
packer.add(PageType.editor, name + "-icon-editor", Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)));
if(!synthetic()){
PixmapRegion image = Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full));
@ -798,7 +799,7 @@ public class Block extends BlockStorage{
}
last = out;
packer.pack(name, out);
packer.add(PageType.main, name, out);
}
if(generatedIcons.length > 1){
@ -810,7 +811,7 @@ public class Block extends BlockStorage{
base.draw(Core.atlas.getPixmap(generatedIcons[i]));
}
}
packer.pack("block-" + name + "-full", base);
packer.add(PageType.main, "block-" + name + "-full", base);
generatedIcons = null;
Arrays.fill(cicons, null);
}

View File

@ -9,8 +9,10 @@ import io.anuke.arc.math.*;
import io.anuke.arc.math.geom.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.Effects.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.graphics.MultiPacker.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.Cicon;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.world.*;
import static io.anuke.mindustry.Vars.tilesize;
@ -86,9 +88,9 @@ public class Floor extends Block{
}
@Override
public void createIcons(PixmapPacker out, PixmapPacker editor){
super.createIcons(out, editor);
editor.pack("editor-" + name, Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)).crop());
public void createIcons(MultiPacker packer){
super.createIcons(packer);
packer.add(PageType.editor, "editor-" + name, Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)).crop());
if(blendGroup != this){
return;
@ -97,7 +99,7 @@ public class Floor extends Block{
if(variants > 0){
for(int i = 0; i < variants; i++){
String rname = name + (i + 1);
editor.pack("editor-" + rname, Core.atlas.getPixmap(rname).crop());
packer.add(PageType.editor, "editor-" + rname, Core.atlas.getPixmap(rname).crop());
}
}
@ -114,7 +116,7 @@ public class Floor extends Block{
}
}
out.pack(name + "-edge", result);
packer.add(PageType.environment, name + "-edge", result);
}
@Override

View File

@ -4,6 +4,8 @@ import io.anuke.annotations.Annotations.*;
import io.anuke.arc.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.graphics.MultiPacker.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
@ -34,7 +36,7 @@ public class OreBlock extends OverlayFloor{
@Override
@OverrideCallSuper
public void createIcons(PixmapPacker out, PixmapPacker editor){
public void createIcons(MultiPacker packer){
for(int i = 0; i < variants; i++){
Pixmap image = new Pixmap(32, 32);
PixmapRegion shadow = Core.atlas.getPixmap(itemDrop.name + (i + 1));
@ -55,12 +57,12 @@ public class OreBlock extends OverlayFloor{
image.draw(shadow);
out.pack(name + (i + 1), image);
editor.pack("editor-" + name + (i + 1), image);
packer.add(PageType.environment, name + (i + 1), image);
packer.add(PageType.editor, "editor-" + name + (i + 1), image);
if(i == 0){
editor.pack("editor-block-" + name + "-full", image);
out.pack("block-" + name + "-full", image);
packer.add(PageType.editor, "editor-block-" + name + "-full", image);
packer.add(PageType.main, "block-" + name + "-full", image);
}
}
}

View File

@ -134,6 +134,11 @@ public class Drill extends Block{
return tile.entity.items.total() < itemCapacity;
}
@Override
public boolean shouldIdleSound(Tile tile){
return tile.entity.efficiency() > 0.01f;
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Tile tile = world.tile(x, y);

View File

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=5b8474248a6631f8f889153785c5f1202803651e
archash=ed17a2654951d1131bb6af71db6dea588df55b19