mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
new debug cmd ExportFBO
This commit is contained in:
@@ -3,6 +3,7 @@ CheatWarnTest
|
||||
CodexEdictis
|
||||
ExportAtlas
|
||||
ExportCodices
|
||||
ExportFBO
|
||||
ExportMap
|
||||
ExportMap2
|
||||
ExportWorld
|
||||
|
||||
|
@@ -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) {
|
||||
|
||||
@@ -67,7 +67,7 @@ class SavegameCracker(
|
||||
private val cmds: HashMap<String, KFunction<*>> = HashMap()
|
||||
init {
|
||||
SavegameCracker::class.declaredFunctions
|
||||
.filter { it.findAnnotation<Command>() != null }
|
||||
.filter { it.findAnnotation<SavegameCrackerCommand>() != 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<Command>()!!
|
||||
val annot = it.findAnnotation<SavegameCrackerCommand>()!!
|
||||
// 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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
cmds.forEach { name, it ->
|
||||
println("$ccNoun${name.padStart(8)}$cc0 - ${it.findAnnotation<Command>()!!.help}")
|
||||
println("$ccNoun${name.padStart(8)}$cc0 - ${it.findAnnotation<SavegameCrackerCommand>()!!.help}")
|
||||
}
|
||||
}
|
||||
|
||||
@Command("Exits the program")
|
||||
@SavegameCrackerCommand("Exits the program")
|
||||
fun exit(args: List<String>) { this.exit = true }
|
||||
@Command("Exits the program")
|
||||
@SavegameCrackerCommand("Exits the program")
|
||||
fun quit(args: List<String>) = 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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
SavegameCracker(args).invoke()
|
||||
|
||||
@@ -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
|
||||
|
||||
107
src/net/torvald/terrarum/modulebasegame/console/ExportFBO.kt
Normal file
107
src/net/torvald/terrarum/modulebasegame/console/ExportFBO.kt
Normal file
@@ -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<String, KFunction<*>> = HashMap()
|
||||
private val helpMessages: List<Pair<String, String>>
|
||||
|
||||
init {
|
||||
val helpMsgs: HashMap<String, String> = HashMap()
|
||||
|
||||
ExportFBO::class.declaredFunctions.filter { it.hasAnnotation<ExportFBOCmd>() }.forEach {
|
||||
cmds[it.name.lowercase()] = it
|
||||
helpMsgs[it.name.lowercase()] = it.findAnnotation<ExportFBOCmd>()!!.description
|
||||
}
|
||||
|
||||
helpMessages = helpMsgs.keys.toList().sorted().map {
|
||||
it to helpMsgs[it]!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
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 <identifier> <filename without extension>")
|
||||
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<Float16FrameBuffer>("fboRGB")!!
|
||||
}
|
||||
|
||||
@ExportFBOCmd("Main A channel of the IngameRenderer without lighting")
|
||||
fun fboa(): FrameBuffer {
|
||||
return IngameRenderer.extortField<Float16FrameBuffer>("fboA")!!
|
||||
}
|
||||
|
||||
@ExportFBOCmd("Main Emissive channel of the IngameRenderer without lighting")
|
||||
fun fboemissive(): FrameBuffer {
|
||||
return IngameRenderer.extortField<Float16FrameBuffer>("fboEmissive")!!
|
||||
}
|
||||
}
|
||||
|
||||
internal annotation class ExportFBOCmd(val description: String)
|
||||
@@ -40,12 +40,12 @@ class ConsoleWindow : UICanvas() {
|
||||
private var commandHistory = CircularArray<String>(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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user