diff --git a/assets/mods/basegame/commands.csv b/assets/mods/basegame/commands.csv index 79dc94703..310e6a01b 100644 --- a/assets/mods/basegame/commands.csv +++ b/assets/mods/basegame/commands.csv @@ -3,6 +3,7 @@ CheatWarnTest CodexEdictis ExportAtlas ExportCodices +ExportFBO ExportMap ExportMap2 ExportWorld diff --git a/src/net/torvald/terrarum/App.java b/src/net/torvald/terrarum/App.java index 9ead2b552..8b65e7a7a 100644 --- a/src/net/torvald/terrarum/App.java +++ b/src/net/torvald/terrarum/App.java @@ -60,6 +60,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.stream.Stream; +import java.util.zip.Deflater; import static java.lang.Thread.MAX_PRIORITY; import static net.torvald.terrarum.TerrarumKt.*; @@ -766,7 +767,7 @@ public class App implements ApplicationListener { FrameBufferManager.begin(fb); try { Pixmap p = Pixmap.createFromFrameBuffer(0, 0, fb.getWidth(), fb.getHeight()); - PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true); + PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, Deflater.DEFAULT_COMPRESSION, true); p.dispose(); } catch (Throwable e) { diff --git a/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt b/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt index ea1e403a5..1c328ee6a 100644 --- a/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt +++ b/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt @@ -67,7 +67,7 @@ class SavegameCracker( private val cmds: HashMap> = HashMap() init { SavegameCracker::class.declaredFunctions - .filter { it.findAnnotation() != null } + .filter { it.findAnnotation() != null } // .forEach { it.isAccessible = true; cmds[it.name] = it } .forEach { cmds[it.name] = it } } @@ -100,7 +100,7 @@ class SavegameCracker( printerrln("${args[0]}: command not found") else { try { - val annot = it.findAnnotation()!! + val annot = it.findAnnotation()!! // check arguments val synopsis = annot.synopsis.split(' ').filter { it.isNotBlank() } // print out synopsis @@ -185,14 +185,14 @@ class SavegameCracker( return this + sb.toString() } - @Command("Loads a disk archive", "path-to-file") + @SavegameCrackerCommand("Loads a disk archive", "path-to-file") fun load(args: List) { file = File(args[1]) disk = VDUtil.readDiskArchive(file!!, Level.INFO) { printerrln("# Warning: $it") } file!!.copyTo(File(file!!.absolutePath + ".bak"), true) } - @Command("Lists contents of the disk") + @SavegameCrackerCommand("Lists contents of the disk") fun ls(args: List) { letdisk { it.entries.toSortedMap().forEach { (i, entry) -> @@ -207,19 +207,19 @@ class SavegameCracker( } } - @Command("Prints out available commands and their usage") + @SavegameCrackerCommand("Prints out available commands and their usage") fun help(args: List) { cmds.forEach { name, it -> - println("$ccNoun${name.padStart(8)}$cc0 - ${it.findAnnotation()!!.help}") + println("$ccNoun${name.padStart(8)}$cc0 - ${it.findAnnotation()!!.help}") } } - @Command("Exits the program") + @SavegameCrackerCommand("Exits the program") fun exit(args: List) { this.exit = true } - @Command("Exits the program") + @SavegameCrackerCommand("Exits the program") fun quit(args: List) = exit(args) - @Command("Exports contents of the entry into a real file", "entry-id output-file") + @SavegameCrackerCommand("Exports contents of the entry into a real file", "entry-id output-file") fun export(args: List) { letdisk { val entryID = args[1].toLong(10) @@ -228,7 +228,7 @@ class SavegameCracker( } } - @Command("Changes one entry-ID into another", "change-from change-to") + @SavegameCrackerCommand("Changes one entry-ID into another", "change-from change-to") fun renum(args: List) { letdisk { val id0 = args[1].toLong(10) @@ -244,7 +244,7 @@ class SavegameCracker( } } - @Command("Imports a real file onto the savefile", "input-file entry-id") + @SavegameCrackerCommand("Imports a real file onto the savefile", "input-file entry-id") fun import(args: List) { letdisk { val file = File(args[1]) @@ -257,7 +257,7 @@ class SavegameCracker( } } - @Command("Removes a file within the savefile", "entry-id") + @SavegameCrackerCommand("Removes a file within the savefile", "entry-id") fun rm(args: List) { letdisk { val id = args[1].toLong(10) @@ -266,14 +266,14 @@ class SavegameCracker( } } - @Command("Saves changes onto the savefile") + @SavegameCrackerCommand("Saves changes onto the savefile") fun save(args: List) { letdisk { VDUtil.dumpToRealMachine(it, file!!) } } - @Command("Retrieves all UUIDs found (player UUID, current world UUID, etc.") + @SavegameCrackerCommand("Retrieves all UUIDs found (player UUID, current world UUID, etc.") fun uuid(args: List) { letdisk { val jsonFile = it.getFile(-1) ?: throw FileNotFoundException("savegameinfo.json (entry ID -1) not found") @@ -294,7 +294,7 @@ class SavegameCracker( } } - @Command("Removes the specified chunk(s) completely", "IDs") + @SavegameCrackerCommand("Removes the specified chunk(s) completely", "IDs") fun discardchunk(args: List) { letdisk { disk -> val ids = args[1] @@ -320,7 +320,7 @@ class SavegameCracker( } } -internal annotation class Command(val help: String = "", val synopsis: String = "") +internal annotation class SavegameCrackerCommand(val help: String = "", val synopsis: String = "") fun main(args: Array) { SavegameCracker(args).invoke() diff --git a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt index bee7e4783..2bf0f5c06 100644 --- a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt +++ b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt @@ -75,6 +75,7 @@ object IngameRenderer : Disposable { private lateinit var fboA_lightMixed: Float16FrameBuffer private lateinit var fboEmissive: Float16FrameBuffer private lateinit var fboMixedOut: Float16FrameBuffer + private lateinit var rgbTex: TextureRegion private lateinit var aTex: TextureRegion private lateinit var mixedOutTex: TextureRegion diff --git a/src/net/torvald/terrarum/modulebasegame/console/ExportFBO.kt b/src/net/torvald/terrarum/modulebasegame/console/ExportFBO.kt new file mode 100644 index 000000000..4e12c5a98 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/console/ExportFBO.kt @@ -0,0 +1,107 @@ +package net.torvald.terrarum.modulebasegame.console + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.GL20 +import com.badlogic.gdx.graphics.GL30 +import com.badlogic.gdx.graphics.Pixmap +import com.badlogic.gdx.graphics.PixmapIO +import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer +import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.utils.BufferUtils +import net.torvald.reflection.extortField +import net.torvald.terrarum.App +import net.torvald.terrarum.ccG +import net.torvald.terrarum.ccO +import net.torvald.terrarum.ccW +import net.torvald.terrarum.console.ConsoleCommand +import net.torvald.terrarum.console.Echo +import net.torvald.terrarum.console.EchoError +import net.torvald.terrarum.modulebasegame.IngameRenderer +import net.torvald.unicode.BULLET +import net.torvald.unicode.EMDASH +import java.nio.ByteBuffer +import java.util.zip.Deflater +import kotlin.reflect.KFunction +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.hasAnnotation + + +/** + * Created by minjaesong on 2024-11-24. + */ +internal object ExportFBO : ConsoleCommand { + + private val cmds: HashMap> = HashMap() + private val helpMessages: List> + + init { + val helpMsgs: HashMap = HashMap() + + ExportFBO::class.declaredFunctions.filter { it.hasAnnotation() }.forEach { + cmds[it.name.lowercase()] = it + helpMsgs[it.name.lowercase()] = it.findAnnotation()!!.description + } + + helpMessages = helpMsgs.keys.toList().sorted().map { + it to helpMsgs[it]!! + } + } + + override fun execute(args: Array) { + if (args.size != 3) { printUsage(); return } + val fn = cmds[args[1].lowercase()] + if (fn == null) { printUsage(); return } + + try { + val filename = "${args[2]}-${args[1]}.png" + val fileHandle = Gdx.files.absolute("${App.defaultDir}/Exports/$filename") + + val fbo = fn.call(this) as FrameBuffer + + // cannot use createFromFrameBuffer because the specific FBO must be bound in a way we can control + val pixels: ByteBuffer = BufferUtils.newByteBuffer(fbo.width * fbo.height * 4) + Gdx.gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fbo.framebufferHandle) + Gdx.gl.glReadPixels(0, 0, fbo.width, fbo.height, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, pixels) + Gdx.gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0) + + val pixmap: Pixmap = Pixmap(fbo.width, fbo.height, Pixmap.Format.RGBA8888) + BufferUtils.copy(pixels, pixmap.pixels, pixels.capacity()) + + PixmapIO.writePNG(fileHandle, pixmap, Deflater.DEFAULT_COMPRESSION, true) + + Echo("Framebuffer exported to$ccG Exports/$filename") + } + catch (e: Throwable) { + EchoError("Could not retrieve the framebuffer: ${e.message}") + System.err.println(e) + return + } + } + + override fun printUsage() { + Echo("Usage: exportfbo ") + Echo("Available identifiers are:") + + helpMessages.forEach { (name, desc) -> + Echo(" $BULLET $ccG$name $ccW$EMDASH $ccO$desc") + } + } + + @ExportFBOCmd("Main RGB channel of the IngameRenderer without lighting") + fun fborgb(): FrameBuffer { + return IngameRenderer.extortField("fboRGB")!! + } + + @ExportFBOCmd("Main A channel of the IngameRenderer without lighting") + fun fboa(): FrameBuffer { + return IngameRenderer.extortField("fboA")!! + } + + @ExportFBOCmd("Main Emissive channel of the IngameRenderer without lighting") + fun fboemissive(): FrameBuffer { + return IngameRenderer.extortField("fboEmissive")!! + } +} + +internal annotation class ExportFBOCmd(val description: String) \ No newline at end of file diff --git a/src/net/torvald/terrarum/ui/ConsoleWindow.kt b/src/net/torvald/terrarum/ui/ConsoleWindow.kt index c0e9d9621..73cac0989 100644 --- a/src/net/torvald/terrarum/ui/ConsoleWindow.kt +++ b/src/net/torvald/terrarum/ui/ConsoleWindow.kt @@ -40,12 +40,12 @@ class ConsoleWindow : UICanvas() { private var commandHistory = CircularArray(COMMAND_HISTORY_MAX, true) private val LINE_HEIGHT = 20 - private val MESSAGES_DISPLAY_COUNT = 11 + private val MESSAGES_DISPLAY_COUNT = 12 private val inputToMsgboxGap = 3 override var width: Int = App.scr.width - override var height: Int = LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT + 1) + inputToMsgboxGap + override var height: Int = LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT + 1) + inputToMsgboxGap + 4 override var openCloseTime = 0f