mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-07-08 23:07:33 +07:00
Texture overrides / Potential mod texture binding optimizations
This commit is contained in:
@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
[](https://travis-ci.org/Anuken/Mindustry)
|
||||
[](https://discord.gg/mindustry)
|
||||
|
BIN
core/assets-raw/sprites/ui/logo.png
Normal file
BIN
core/assets-raw/sprites/ui/logo.png
Normal file
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 |
@ -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){
|
||||
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
|
56
core/src/io/anuke/mindustry/graphics/MultiPacker.java
Normal file
56
core/src/io/anuke/mindustry/graphics/MultiPacker.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1,3 +1,3 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||
archash=5b8474248a6631f8f889153785c5f1202803651e
|
||||
archash=ed17a2654951d1131bb6af71db6dea588df55b19
|
||||
|
Reference in New Issue
Block a user