diff --git a/src/com/badlogic/gdx/graphics/PixmapIO2.java b/src/com/badlogic/gdx/graphics/PixmapIO2.java index e7f086552..848545b93 100644 --- a/src/com/badlogic/gdx/graphics/PixmapIO2.java +++ b/src/com/badlogic/gdx/graphics/PixmapIO2.java @@ -11,33 +11,33 @@ import java.io.OutputStream; */ public class PixmapIO2 { - public static void writeTGAHappy(FileHandle file, Pixmap pixmap) throws IOException { + // REMEMBER: to the GL's perspective, this game's FBOs are always Y-flipped. // + + public static void writeTGAHappy(FileHandle file, Pixmap pixmap, boolean flipY) throws IOException { OutputStream output = file.write(false); try { - _writeTGA(output, pixmap, false); + _writeTGA(output, pixmap, false, flipY); } finally { StreamUtils.closeQuietly(output); } } - public static void writeTGA(FileHandle file, Pixmap pixmap) throws IOException { + public static void writeTGA(FileHandle file, Pixmap pixmap, boolean flipY) throws IOException { OutputStream output = file.write(false); try { - _writeTGA(output, pixmap, true); + _writeTGA(output, pixmap, true, flipY); } finally { StreamUtils.closeQuietly(output); } } - private static void _writeTGA(OutputStream out, Pixmap pixmap, Boolean verbatim) throws IOException { + private static void _writeTGA(OutputStream out, Pixmap pixmap, boolean verbatim, boolean flipY) throws IOException { byte[] width = toShortLittle(pixmap.getWidth()); byte[] height = toShortLittle(pixmap.getHeight()); byte[] zero = toShortLittle(0); - byte[] zeroalpha = new byte[]{0,0,0,0}; - out.write(0); // ID field: empty out.write(0); // no colour map, but should be ignored anyway as it being unmapped RGB out.write(2); // 2 means unmapped RGB @@ -55,16 +55,17 @@ public class PixmapIO2 { // 1. BGRA order // 2. Y-Flipped but not X-Flipped - for (int y = pixmap.getHeight() - 1; y >= 0; y--) { - for (int x = 0; x < pixmap.getWidth(); x++) { - int color = pixmap.getPixel(x, y); - - // if alpha == 0, write special value instead - if (verbatim && (color & 0xFF) == 0) { - out.write(zeroalpha); + if (!flipY) { + for (int y = pixmap.getHeight() - 1; y >= 0; y--) { + for (int x = 0; x < pixmap.getWidth(); x++) { + writeTga(x, y, verbatim, pixmap, out); } - else { - out.write(RGBAtoBGRA(color)); + } + } + else { + for (int y = 0; y < pixmap.getHeight(); y++) { + for (int x = 0; x < pixmap.getWidth(); x++) { + writeTga(x, y, verbatim, pixmap, out); } } } @@ -84,6 +85,19 @@ public class PixmapIO2 { out.close(); } + private static byte[] zeroalpha = new byte[]{0,0,0,0}; + private static void writeTga(int x, int y, boolean verbatim, Pixmap pixmap, OutputStream out) throws IOException { + int color = pixmap.getPixel(x, y); + + // if alpha == 0, write special value instead + if (verbatim && (color & 0xFF) == 0) { + out.write(zeroalpha); + } + else { + out.write(RGBAtoBGRA(color)); + } + } + private static byte[] toShortLittle(int i) { return new byte[]{ (byte) (i & 0xFF), diff --git a/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt b/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt index bde80ac29..1513df663 100644 --- a/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt +++ b/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt @@ -272,7 +272,7 @@ class SpriteAssemblerPreview: Game() { if (doExport && image != null) { doExport = false - PixmapIO2.writeTGAHappy(Gdx.files.absolute(exportPath), image) + PixmapIO2.writeTGAHappy(Gdx.files.absolute(exportPath), image, false) } diff --git a/src/net/torvald/terrarum/AppLoader.java b/src/net/torvald/terrarum/AppLoader.java index a2cc37471..720f312ab 100644 --- a/src/net/torvald/terrarum/AppLoader.java +++ b/src/net/torvald/terrarum/AppLoader.java @@ -11,6 +11,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.FrameBuffer; import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.utils.ScreenUtils; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @@ -140,6 +141,7 @@ public class AppLoader implements ApplicationListener { private static boolean splashDisplayed = false; private static boolean postInitFired = false; + private static boolean screenshotRequested = false; public static LwjglApplicationConfiguration appConfig; public static GameFontBase fontGame; @@ -246,10 +248,6 @@ public class AppLoader implements ApplicationListener { FrameBufferManager.begin(renderFBO); gdxClearAndSetBlend(.094f, .094f, .094f, 0f); - FrameBufferManager.end(); - - - FrameBufferManager.begin(renderFBO); setCameraPosition(0, 0); // draw splash screen when predefined screen is null @@ -294,6 +292,20 @@ public class AppLoader implements ApplicationListener { PostProcessor.INSTANCE.draw(camera.combined, renderFBO); + // process screenshot request + if (screenshotRequested) { + screenshotRequested = false; + + try { + Pixmap p = ScreenUtils.getFrameBufferPixmap(0, 0, appConfig.width, appConfig.height); + PixmapIO2.writeTGA(Gdx.files.absolute(defaultDir + "/Screenshot.tga"), p, true); + p.dispose(); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + splashDisplayed = true; GLOBAL_RENDER_TIMER += 1; } @@ -417,13 +429,20 @@ public class AppLoader implements ApplicationListener { fullscreenQuad.setIndices(new short[]{0, 1, 2, 2, 3, 0}); } + public static void requestScreenshot() { + screenshotRequested = true; + } + // DEFAULT DIRECTORIES // public static String OSName = System.getProperty("os.name"); public static String OSVersion = System.getProperty("os.version"); public static String operationSystem; + /** %appdata%/Terrarum, without trailing slash */ public static String defaultDir; + /** defaultDir + "/Saves", without trailing slash */ public static String defaultSaveDir; + /** defaultDir + "/config.json" */ public static String configDir; public static RunningEnvironment environment; diff --git a/src/net/torvald/terrarum/console/CommandDict.kt b/src/net/torvald/terrarum/console/CommandDict.kt index 8e67236e6..fc23daace 100644 --- a/src/net/torvald/terrarum/console/CommandDict.kt +++ b/src/net/torvald/terrarum/console/CommandDict.kt @@ -43,6 +43,7 @@ object CommandDict { "setscale" to SetScale, "kill" to KillActor, "money" to MoneyDisp, + "screenshot" to TakeScreenshot, // Test codes "bulletintest" to SetBulletin, diff --git a/src/net/torvald/terrarum/console/CommandInterpreter.kt b/src/net/torvald/terrarum/console/CommandInterpreter.kt index ce48025a0..6021806df 100644 --- a/src/net/torvald/terrarum/console/CommandInterpreter.kt +++ b/src/net/torvald/terrarum/console/CommandInterpreter.kt @@ -1,10 +1,11 @@ package net.torvald.terrarum.console -import net.torvald.terrarum.* +import net.torvald.terrarum.ccG +import net.torvald.terrarum.ccW +import net.torvald.terrarum.ccY import net.torvald.terrarum.langpack.Lang import java.time.ZonedDateTime -import java.util.ArrayList -import java.util.Formatter +import java.util.* import java.util.regex.Pattern /** @@ -20,7 +21,8 @@ internal object CommandInterpreter { "getlocale", "help", "version", - "tips" + "tips", + "screenshot" ) internal fun execute(command: String) { diff --git a/src/net/torvald/terrarum/console/TakeScreenshot.kt b/src/net/torvald/terrarum/console/TakeScreenshot.kt new file mode 100644 index 000000000..331f39eae --- /dev/null +++ b/src/net/torvald/terrarum/console/TakeScreenshot.kt @@ -0,0 +1,13 @@ +package net.torvald.terrarum.console + +import net.torvald.terrarum.AppLoader + +object TakeScreenshot: ConsoleCommand { + override fun execute(args: Array) { + AppLoader.requestScreenshot() + } + + override fun printUsage() { + Echo("Takes screenshot and save it to the default directory as 'screenshot.tga'") + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt index 5da3d58ec..5f0ba24d9 100644 --- a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt +++ b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt @@ -22,7 +22,9 @@ import net.torvald.terrarum.worlddrawer.WorldCamera import javax.swing.JFileChooser /** - * This will be rendered to a postprocessor FBO + * This will be rendered to a postprocessor FBO. + * + * For the entire render path, see AppLoader. */ object IngameRenderer { /** for non-private use, use with care! */ @@ -282,7 +284,8 @@ object IngameRenderer { if (fileChooser.selectedFile != null) { fboRGB.inAction(null, null) { val p = ScreenUtils.getFrameBufferPixmap(0, 0, fboRGB.width, fboRGB.height) - PixmapIO2.writeTGA(Gdx.files.absolute(fileChooser.selectedFile.absolutePath), p) + PixmapIO2.writeTGA(Gdx.files.absolute(fileChooser.selectedFile.absolutePath), p, false) + p.dispose() } } }