option for screen filtering mode

This commit is contained in:
minjaesong
2023-07-21 13:14:02 +09:00
parent 9d7bd37394
commit fbce707cac
10 changed files with 250 additions and 47 deletions

View File

@@ -31,7 +31,6 @@ import net.torvald.terrarum.langpack.Lang;
import net.torvald.terrarum.modulebasegame.IngameRenderer;
import net.torvald.terrarum.modulebasegame.TerrarumIngame;
import net.torvald.terrarum.modulebasegame.ui.ItemSlotImageFactory;
import net.torvald.terrarum.savegame.DiskSkimmer;
import net.torvald.terrarum.serialise.WriteConfig;
import net.torvald.terrarum.ui.Toolkit;
import net.torvald.terrarum.utils.JsonFetcher;
@@ -240,8 +239,12 @@ public class App implements ApplicationListener {
public static ShaderProgram shaderColLUT;
public static ShaderProgram shaderReflect;
public static ShaderProgram shaderGhastlyWhite;
public static ShaderProgram shaderHQ2x;
private static Texture hq2xLut;
public static Mesh fullscreenQuad;
public static Mesh fullscreenQuad2x;
private static OrthographicCamera camera;
private static FlippingSpriteBatch logoBatch;
public static TextureRegion logo;
@@ -392,6 +395,7 @@ public class App implements ApplicationListener {
appConfig.useVsync(getConfigBoolean("usevsync"));
appConfig.setResizable(false);
appConfig.setWindowedMode(width, height);
appConfig.setTransparentFramebuffer(false);
int fpsActive = Math.min(GLOBAL_FRAMERATE_LIMIT, getConfigInt("displayfps"));
if (fpsActive <= 0) fpsActive = GLOBAL_FRAMERATE_LIMIT;
int fpsBack = Math.min(GLOBAL_FRAMERATE_LIMIT, getConfigInt("displayfpsidle"));
@@ -473,6 +477,9 @@ public class App implements ApplicationListener {
);
shaderPassthruRGBA = loadShaderFromClasspath("shaders/gl32spritebatch.vert", "shaders/gl32spritebatch.frag");
shaderReflect = loadShaderFromClasspath("shaders/default.vert", "shaders/reflect.frag");
shaderHQ2x = loadShaderFromClasspath("shaders/hq2x.vert", "shaders/hq2x.frag");
hq2xLut = new Texture(Gdx.files.classpath("shaders/hq2x.tga"));
fullscreenQuad = new Mesh(
true, 4, 6,
@@ -480,7 +487,14 @@ public class App implements ApplicationListener {
VertexAttribute.ColorUnpacked(),
VertexAttribute.TexCoords(0)
);
updateFullscreenQuad(scr.getWidth(), scr.getHeight());
fullscreenQuad2x = new Mesh(
true, 4, 6,
VertexAttribute.Position(),
VertexAttribute.ColorUnpacked(),
VertexAttribute.TexCoords(0)
);
updateFullscreenQuad(fullscreenQuad, scr.getWidth(), scr.getHeight());
updateFullscreenQuad(fullscreenQuad2x, scr.getWidth(), scr.getHeight());
// set up renderer info variables
renderer = Gdx.graphics.getGLVersion().getRendererString();
@@ -571,11 +585,39 @@ public class App implements ApplicationListener {
}
shaderPassthruRGBA.bind();
shaderPassthruRGBA.setUniformMatrix("u_projTrans", camera.combined);
shaderPassthruRGBA.setUniformi("u_texture", 0);
postProcessorOutFBO.getColorBufferTexture().bind(0);
fullscreenQuad.render(shaderPassthruRGBA, GL20.GL_TRIANGLES);
if (getConfigString("screenmagnifyingfilter").equals("hq2x")) {
int canvasWidth = scr.getWidth() * 2;
int canvasHeight = scr.getHeight() * 2;
shaderHQ2x.bind();
shaderHQ2x.setUniformMatrix("u_projTrans", camera.combined);
shaderHQ2x.setUniformi("u_lut", 1);
shaderHQ2x.setUniformi("u_texture", 0);
shaderHQ2x.setUniformf("u_textureSize", canvasWidth / 2f, canvasHeight / 2f);
hq2xLut.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
hq2xLut.bind(1);
postProcessorOutFBO.getColorBufferTexture().setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
postProcessorOutFBO.getColorBufferTexture().bind(0);
fullscreenQuad2x.render(shaderHQ2x, GL20.GL_TRIANGLES);
}
else if (getConfigDouble("screenmagnifying") < 1.01 || getConfigString("screenmagnifyingfilter").equals("none")) {
shaderPassthruRGBA.bind();
shaderPassthruRGBA.setUniformMatrix("u_projTrans", camera.combined);
shaderPassthruRGBA.setUniformi("u_texture", 0);
postProcessorOutFBO.getColorBufferTexture().setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
postProcessorOutFBO.getColorBufferTexture().bind(0);
fullscreenQuad.render(shaderPassthruRGBA, GL20.GL_TRIANGLES);
}
else if (getConfigString("screenmagnifyingfilter").equals("bilinear")) {
shaderPassthruRGBA.bind();
shaderPassthruRGBA.setUniformMatrix("u_projTrans", camera.combined);
shaderPassthruRGBA.setUniformi("u_texture", 0);
postProcessorOutFBO.getColorBufferTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
postProcessorOutFBO.getColorBufferTexture().bind(0);
fullscreenQuad.render(shaderPassthruRGBA, GL20.GL_TRIANGLES);
}
// process resize request
if (resizeRequested) {
@@ -708,7 +750,8 @@ public class App implements ApplicationListener {
if (currentScreen != null) currentScreen.resize(scr.getWidth(), scr.getHeight());
TerrarumPostProcessor.INSTANCE.resize(scr.getWidth(), scr.getHeight());
updateFullscreenQuad(scr.getWidth(), scr.getHeight());
updateFullscreenQuad(fullscreenQuad, scr.getWidth(), scr.getHeight());
updateFullscreenQuad(fullscreenQuad2x, scr.getWidth(), scr.getHeight());
if (renderFBO == null ||
@@ -772,9 +815,13 @@ public class App implements ApplicationListener {
shaderColLUT.dispose();
shaderReflect.dispose();
shaderGhastlyWhite.dispose();
shaderHQ2x.dispose();
hq2xLut.dispose();
CommonResourcePool.INSTANCE.dispose();
fullscreenQuad.dispose();
fullscreenQuad2x.dispose();
logoBatch.dispose();
batch.dispose();
// shapeRender.dispose();
@@ -882,7 +929,6 @@ public class App implements ApplicationListener {
shaderColLUT = loadShaderFromClasspath("shaders/default.vert", "shaders/passthrurgb.frag");
shaderGhastlyWhite = loadShaderFromClasspath("shaders/default.vert", "shaders/ghastlywhite.frag");
// make gamepad(s)
if (App.getConfigBoolean("usexinput")) {
try {
@@ -1036,14 +1082,14 @@ public class App implements ApplicationListener {
logoBatch.setProjectionMatrix(camera.combined);
}
private void updateFullscreenQuad(int WIDTH, int HEIGHT) { // NOT y-flipped quads!
fullscreenQuad.setVertices(new float[]{
private void updateFullscreenQuad(Mesh mesh, int WIDTH, int HEIGHT) { // NOT y-flipped quads!
mesh.setVertices(new float[]{
0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f, 1f,
WIDTH, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f,
WIDTH, HEIGHT, 0f, 1f, 1f, 1f, 1f, 1f, 0f,
0f, HEIGHT, 0f, 1f, 1f, 1f, 1f, 0f, 0f
});
fullscreenQuad.setIndices(new short[]{0, 1, 2, 2, 3, 0});
mesh.setIndices(new short[]{0, 1, 2, 2, 3, 0});
}
public static void setGamepadButtonLabels() {

View File

@@ -112,6 +112,7 @@ object DefaultConfig {
"inputmethod" to "none",
"screenmagnifying" to 1.0,
"screenmagnifyingfilter" to "none", // "none", "bilinear", "hq2x"
"fx_newlight" to false,

View File

@@ -196,35 +196,35 @@ object TerrarumPostProcessor : Disposable {
private fun Double.format(digits: Int) = "%.${digits}f".format(this)
private fun Float.format(digits: Int) = "%.${digits}f".format(this)
private val swizzler = intArrayOf(
1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1,
1,0,0,0, 0,1,0,0, 0,0,0,1, 0,0,1,0,
1,0,0,0, 0,0,1,0, 0,1,0,0, 0,0,0,1,
1,0,0,0, 0,0,1,0, 0,0,0,1, 0,1,0,0,
1,0,0,0, 0,0,0,1, 0,1,0,0, 0,0,1,0,
1,0,0,0, 0,0,0,1, 0,0,1,0, 0,1,0,0,
private val swizzler = floatArrayOf(
1f,0f,0f,0f, 0f,1f,0f,0f, 0f,0f,1f,0f, 0f,0f,0f,1f,
1f,0f,0f,0f, 0f,1f,0f,0f, 0f,0f,0f,1f, 0f,0f,1f,0f,
1f,0f,0f,0f, 0f,0f,1f,0f, 0f,1f,0f,0f, 0f,0f,0f,1f,
1f,0f,0f,0f, 0f,0f,1f,0f, 0f,0f,0f,1f, 0f,1f,0f,0f,
1f,0f,0f,0f, 0f,0f,0f,1f, 0f,1f,0f,0f, 0f,0f,1f,0f,
1f,0f,0f,0f, 0f,0f,0f,1f, 0f,0f,1f,0f, 0f,1f,0f,0f,
0,1,0,0, 1,0,0,0, 0,0,1,0, 0,0,0,1,
0,1,0,0, 1,0,0,0, 0,0,0,1, 0,0,1,0,
0,1,0,0, 0,0,1,0, 1,0,0,0, 0,0,0,1,
0,1,0,0, 0,0,1,0, 0,0,0,1, 1,0,0,0,
0,1,0,0, 0,0,0,1, 1,0,0,0, 0,0,1,0,
0,1,0,0, 0,0,0,1, 0,0,1,0, 1,0,0,0,
0f,1f,0f,0f, 1f,0f,0f,0f, 0f,0f,1f,0f, 0f,0f,0f,1f,
0f,1f,0f,0f, 1f,0f,0f,0f, 0f,0f,0f,1f, 0f,0f,1f,0f,
0f,1f,0f,0f, 0f,0f,1f,0f, 1f,0f,0f,0f, 0f,0f,0f,1f,
0f,1f,0f,0f, 0f,0f,1f,0f, 0f,0f,0f,1f, 1f,0f,0f,0f,
0f,1f,0f,0f, 0f,0f,0f,1f, 1f,0f,0f,0f, 0f,0f,1f,0f,
0f,1f,0f,0f, 0f,0f,0f,1f, 0f,0f,1f,0f, 1f,0f,0f,0f,
0,0,1,0, 1,0,0,0, 0,1,0,0, 0,0,0,1,
0,0,1,0, 1,0,0,0, 0,0,0,1, 0,1,0,0,
0,0,1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1,
0,0,1,0, 0,1,0,0, 0,0,0,1, 1,0,0,0,
0,0,1,0, 0,0,0,1, 1,0,0,0, 0,1,0,0,
0,0,1,0, 0,0,0,1, 0,1,0,0, 1,0,0,0,
0f,0f,1f,0f, 1f,0f,0f,0f, 0f,1f,0f,0f, 0f,0f,0f,1f,
0f,0f,1f,0f, 1f,0f,0f,0f, 0f,0f,0f,1f, 0f,1f,0f,0f,
0f,0f,1f,0f, 0f,1f,0f,0f, 1f,0f,0f,0f, 0f,0f,0f,1f,
0f,0f,1f,0f, 0f,1f,0f,0f, 0f,0f,0f,1f, 1f,0f,0f,0f,
0f,0f,1f,0f, 0f,0f,0f,1f, 1f,0f,0f,0f, 0f,1f,0f,0f,
0f,0f,1f,0f, 0f,0f,0f,1f, 0f,1f,0f,0f, 1f,0f,0f,0f,
0,0,0,1, 1,0,0,0, 0,1,0,0, 0,0,1,0,
0,0,0,1, 1,0,0,0, 0,0,1,0, 0,1,0,0,
0,0,0,1, 0,1,0,0, 1,0,0,0, 0,0,1,0,
0,0,0,1, 0,1,0,0, 0,0,1,0, 1,0,0,0,
0,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0,
0,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,0,
).map { it.toFloat() }.toFloatArray()
0f,0f,0f,1f, 1f,0f,0f,0f, 0f,1f,0f,0f, 0f,0f,1f,0f,
0f,0f,0f,1f, 1f,0f,0f,0f, 0f,0f,1f,0f, 0f,1f,0f,0f,
0f,0f,0f,1f, 0f,1f,0f,0f, 1f,0f,0f,0f, 0f,0f,1f,0f,
0f,0f,0f,1f, 0f,1f,0f,0f, 0f,0f,1f,0f, 1f,0f,0f,0f,
0f,0f,0f,1f, 0f,0f,1f,0f, 1f,0f,0f,0f, 0f,1f,0f,0f,
0f,0f,0f,1f, 0f,0f,1f,0f, 0f,1f,0f,0f, 1f,0f,0f,0f,
)
private fun postShader(projMat: Matrix4, fbo: FrameBuffer) {

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.*
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
@@ -26,9 +27,14 @@ object ControlPanelCommon {
var CONFIG_SPINNER_WIDTH = 140
var CONFIG_TYPEIN_WIDTH = 240
var CONFIG_SLIDER_WIDTH = 240
var CONFIG_TEXTSEL_WIDTH = 240
// @return Pair of <UIItem, Init job for the item>
fun makeButton(parent: UICanvas, args: String, x: Int, y: Int, optionName: String): Pair<UIItem, (UIItem, String) -> Unit> {
fun makeButton(parent: UICanvas, args: String, x: Int, y: Int, optionNames0: String): Pair<UIItem, (UIItem, String) -> Unit> {
val optionNames = optionNames0.split(",")
val optionName = optionNames.first()
val arg = args.split(',')
return if (args.equals("h1") || args.equals("p")) {
(object : UIItem(parent, x, y) {
override val width = 1
@@ -44,8 +50,26 @@ object ControlPanelCommon {
}
}
}
else if (args.startsWith("textsel,")) {
val labelFuns = arg.subList(1, arg.size).map { { Lang[it.substringAfter("=")] } }
val optionsList = arg.subList(1, arg.size).map { it.substringBefore("=") }
val initialSel = optionsList.indexOf(App.getConfigString(optionName))
println("labelFuns = ${labelFuns.map { it.invoke() }}")
println("optionsList = $optionsList")
println("optionName = $optionName; value = ${App.getConfigString(optionName)}")
println("initialSel = $initialSel")
if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigString(optionName)}' for option '$optionName' is not found on the options list")
UIItemTextSelector(parent, x, y, labelFuns, initialSel, CONFIG_TEXTSEL_WIDTH, clickToShowPalette = false) to { it: UIItem, optionStr: String ->
(it as UIItemTextSelector).selectionChangeListener = {
App.setConfig(optionStr, optionsList[it])
}
}
}
else if (args.startsWith("spinner,")) {
val arg = args.split(',')
UIItemSpinner(parent, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = {
App.setConfig(optionStr, it)
@@ -53,7 +77,6 @@ object ControlPanelCommon {
}
}
else if (args.startsWith("spinnerd,")) {
val arg = args.split(',')
UIItemSpinner(parent, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), arg[3].toDouble(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${((it as Double)*100).toInt()}%" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = {
App.setConfig(optionStr, it)
@@ -61,7 +84,6 @@ object ControlPanelCommon {
}
}
else if (args.startsWith("sliderd,")) {
val arg = args.split(',')
UIItemHorzSlider(parent, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), CONFIG_SLIDER_WIDTH) to { it: UIItem, optionStr: String ->
(it as UIItemHorzSlider).selectionChangeListener = {
App.setConfig(optionStr, it)
@@ -69,7 +91,6 @@ object ControlPanelCommon {
}
}
else if (args.startsWith("spinnerimul,")) {
val arg = args.split(',')
val mult = arg[4].toInt()
UIItemSpinner(parent, x, y, App.getConfigInt(optionName) / mult, arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = {
@@ -90,8 +111,8 @@ object ControlPanelCommon {
}
}
else if (args.startsWith("typeinres")) {
val keyWidth = optionName.substringBefore(',')
val keyHeight = optionName.substringAfter(',')
val keyWidth = optionNames[0]
val keyHeight = optionNames[1]
UIItemTextLineInput(parent, x, y, CONFIG_SPINNER_WIDTH,
defaultValue = { "${App.getConfigInt(keyWidth)}x${App.getConfigInt(keyHeight)}" },
maxLen = InputLenCap(9, InputLenCap.CharLenUnit.CODEPOINTS),

View File

@@ -30,6 +30,7 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
arrayOf("", { Lang["MENU_OPTIONS_DISPLAY"] }, "h1"),
arrayOf("screenwidth,screenheight", { Lang["MENU_OPTIONS_RESOLUTION"] }, "typeinres"),
arrayOf("screenmagnifying", { Lang["GAME_ACTION_ZOOM"] }, "spinnerd,1.0,2.0,0.05"),
arrayOf("screenmagnifyingfilter", { Lang["MENU_OPTIONS_FILTERING_MODE"] }, "textsel,none=MENU_OPTIONS_NONE,bilinear=MENU_OPTIONS_FILTERING_BILINEAR,hq2x=MENU_OPTIONS_FILTERING_HQ2X_DNT"),
arrayOf("displayfps", { Lang["MENU_LABEL_FRAMESPERSEC"] }, "spinner,0,300,2"),
arrayOf("usevsync", { Lang["MENU_OPTIONS_VSYNC"] }, "toggle"),
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),