mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
trying to get smooth delta (because fuck you GDX)
This commit is contained in:
@@ -21,10 +21,11 @@ class CircularArray<T>(val size: Int) {
|
||||
|
||||
val lastIndex = size - 1
|
||||
|
||||
/** elemCount == size means it has the exact elements and no more.
|
||||
* If elemCount is greater by 1 against the size, it means it started to circle, elemCount won't increment at this point. */
|
||||
/**
|
||||
* Number of elements that forEach() or fold() would iterate.
|
||||
*/
|
||||
val elemCount: Int
|
||||
get() = unreliableAddCount
|
||||
get() = minOf(unreliableAddCount, size)
|
||||
|
||||
fun add(item: T) {
|
||||
if (unreliableAddCount <= size) unreliableAddCount += 1
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.torvald.dataclass.ArrayListMap;
|
||||
import net.torvald.dataclass.CircularArray;
|
||||
import net.torvald.terrarum.modulebasegame.IngameRenderer;
|
||||
import net.torvald.terrarum.utils.JsonFetcher;
|
||||
import net.torvald.terrarum.utils.JsonWriter;
|
||||
@@ -24,6 +25,7 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
@@ -162,8 +164,6 @@ public class AppLoader implements ApplicationListener {
|
||||
//appConfig.useGL30 = true; // used: loads GL 3.2, unused: loads GL 4.6; what the fuck?
|
||||
appConfig.vSyncEnabled = false;
|
||||
appConfig.resizable = false;//true;
|
||||
//appConfig.width = 1072; // IMAX ratio
|
||||
//appConfig.height = 742; // IMAX ratio
|
||||
appConfig.width = 1110; // photographic ratio (1.5:1)
|
||||
appConfig.height = 740; // photographic ratio (1.5:1)
|
||||
appConfig.backgroundFPS = 9999;
|
||||
@@ -237,6 +237,99 @@ public class AppLoader implements ApplicationListener {
|
||||
updateFullscreenQuad(appConfig.width, appConfig.height);
|
||||
}
|
||||
|
||||
private static double _kalman_xhat_k = 1.0 / 60.0;
|
||||
private static double _kalman_p_k = 1.0;
|
||||
private static final double _kalman_R = 0.1;
|
||||
private static boolean _kalman_discard_requested = true;
|
||||
private static int _kalman_discard_frame_counter = 0;
|
||||
private final int _KALMAN_FRAMES_TO_DISCARD = 3;
|
||||
private final double _KALMAN_UPDATE_THRE = 0.1;
|
||||
|
||||
private static final int _DELTA_ITER_AVR_SAMPLESIZE = 25;
|
||||
private static CircularArray<Double> deltaHistory = new CircularArray<>(_DELTA_ITER_AVR_SAMPLESIZE);
|
||||
private static double deltaAvr = 0.0;
|
||||
|
||||
/**
|
||||
* Because fuck you GDX. (No, really; take a look at LwjglGraphics.java, getDeltaTime() and rawDeltaTime() are exactly the same)
|
||||
* @return Render delta that is smoothed out.
|
||||
*/
|
||||
public static double getSmoothDelta() {
|
||||
// kalman filter is calculated but not actually being used.
|
||||
//return deltaAvr;
|
||||
|
||||
|
||||
// below is the kalman part
|
||||
if (_kalman_discard_requested)
|
||||
return Gdx.graphics.getRawDeltaTime();
|
||||
|
||||
return _kalman_xhat_k;
|
||||
}
|
||||
|
||||
public static void resetDeltaSmoothingHistory() {
|
||||
_kalman_xhat_k = 1.0 / 60.0;
|
||||
_kalman_p_k = 1.0;
|
||||
_kalman_discard_requested = true;
|
||||
|
||||
|
||||
deltaHistory = new CircularArray<>(_DELTA_ITER_AVR_SAMPLESIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @link http://bilgin.esme.org/BitsAndBytes/KalmanFilterforDummies
|
||||
*/
|
||||
private void updateKalmanRenderDelta() {
|
||||
|
||||
// TODO implement nonlinear kalman filter or n-frames average
|
||||
|
||||
// kalman filter is calculated but not actually being used.
|
||||
// the problem with this kalman filter is that it assumes most simplistic situation:
|
||||
// 1. the actual delta (measured delta - noise) is constant
|
||||
// 2. everything is linear
|
||||
// we may need to implement Extended Kalman Filter but wtf is Jacobian, I suck at maths.
|
||||
|
||||
if (_kalman_discard_requested) {
|
||||
_kalman_discard_frame_counter += 1;
|
||||
if (_kalman_discard_frame_counter >= _KALMAN_FRAMES_TO_DISCARD) {
|
||||
_kalman_discard_requested = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// measurement value
|
||||
double _kalman_zed_k = Gdx.graphics.getRawDeltaTime();
|
||||
|
||||
if (_kalman_zed_k <= _KALMAN_UPDATE_THRE) {
|
||||
// time update
|
||||
double _kalman_xhatminus_k = _kalman_xhat_k;
|
||||
double _kalman_pminus_k = _kalman_p_k;
|
||||
|
||||
// measurement update
|
||||
double _kalman_gain = _kalman_pminus_k / (_kalman_pminus_k + _kalman_R);
|
||||
double _kalman_xhat_kNew = _kalman_xhatminus_k + _kalman_gain * (_kalman_zed_k - _kalman_xhatminus_k);
|
||||
double _kalman_p_kNew = (1.0 - _kalman_gain) * _kalman_pminus_k;
|
||||
|
||||
_kalman_xhat_k = _kalman_xhat_kNew;
|
||||
_kalman_p_k = _kalman_p_kNew;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// below is the averaging part
|
||||
if (Gdx.graphics.getRawDeltaTime() <= _KALMAN_UPDATE_THRE) {
|
||||
deltaHistory.add(((double) Gdx.graphics.getRawDeltaTime()));
|
||||
ArrayList<Double> middleVals = new ArrayList<>();
|
||||
deltaHistory.forEach((it) -> {
|
||||
middleVals.add(it);
|
||||
return null;
|
||||
});
|
||||
for (int k = deltaHistory.getElemCount() - 2; k >= 0; k--) {
|
||||
for (int l = 0; l <= k; l++) {
|
||||
middleVals.set(l, (middleVals.get(l) + middleVals.get(l + 1)) / 2.0);
|
||||
}
|
||||
}
|
||||
deltaAvr = middleVals.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
|
||||
@@ -245,6 +338,8 @@ public class AppLoader implements ApplicationListener {
|
||||
postInit();
|
||||
}
|
||||
|
||||
// update smooth delta AFTER postInit
|
||||
updateKalmanRenderDelta();
|
||||
|
||||
FrameBufferManager.begin(renderFBO);
|
||||
gdxClearAndSetBlend(.094f, .094f, .094f, 0f);
|
||||
@@ -341,6 +436,8 @@ public class AppLoader implements ApplicationListener {
|
||||
updateFullscreenQuad(screenW, screenH);
|
||||
|
||||
printdbg(this, "Resize event");
|
||||
|
||||
resetDeltaSmoothingHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,6 +482,8 @@ public class AppLoader implements ApplicationListener {
|
||||
}
|
||||
|
||||
printdbg(this, "Screen transisiton complete: " + this.screen.getClass().getCanonicalName());
|
||||
|
||||
resetDeltaSmoothingHistory();
|
||||
}
|
||||
|
||||
private void postInit() {
|
||||
|
||||
@@ -411,7 +411,8 @@ object Terrarum : Screen {
|
||||
}
|
||||
|
||||
override fun render(delta: Float) {
|
||||
AppLoader.debugTimers["GDX.delta"] = delta.times(1000_000_000f).toLong()
|
||||
AppLoader.debugTimers["GDX.rawDelta"] = Gdx.graphics.rawDeltaTime.times(1000_000_000f).toLong()
|
||||
AppLoader.debugTimers["GDX.smtDelta"] = AppLoader.getSmoothDelta().times(1000_000_000f).toLong()
|
||||
AppLoader.getINSTANCE().screen.render(deltaTime)
|
||||
//GLOBAL_RENDER_TIMER += 1
|
||||
// moved to AppLoader; global event must be place at the apploader to prevent ACCIDENTAL forgot-to-update type of bug.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
@@ -358,7 +359,10 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
|
||||
override fun update(delta: Float) {
|
||||
if (isUpdate && !flagDespawn) {
|
||||
|
||||
val ddelta = delta.toDouble()
|
||||
val ddelta = Gdx.graphics.rawDeltaTime.toDouble()
|
||||
//val ddelta = AppLoader.getSmoothDelta()
|
||||
//println("${Gdx.graphics.rawDeltaTime.toDouble()}\t${AppLoader.getSmoothDelta()}")
|
||||
|
||||
|
||||
if (!assertPrinted) assertInit()
|
||||
|
||||
|
||||
@@ -408,7 +408,6 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
||||
itemOnGrip?.endSecondaryUse(delta)
|
||||
}
|
||||
|
||||
protected var updateDeltaCounter = 0.0
|
||||
protected val renderRate = Terrarum.renderRate
|
||||
|
||||
private var firstTimeRun = true
|
||||
@@ -439,6 +438,7 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
||||
|
||||
|
||||
gameFullyLoaded = true
|
||||
AppLoader.resetDeltaSmoothingHistory()
|
||||
}
|
||||
|
||||
|
||||
@@ -446,7 +446,6 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
||||
|
||||
|
||||
/** UPDATE CODE GOES HERE */
|
||||
updateDeltaCounter += delta
|
||||
|
||||
|
||||
|
||||
@@ -460,19 +459,6 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
||||
// else, NOP;
|
||||
}
|
||||
else {
|
||||
updateDeltaCounter += delta
|
||||
/*if (delta < 1f / 10f) { // discard async if measured FPS <= 10
|
||||
var updateTries = 0
|
||||
while (updateDeltaCounter >= renderRate && updateTries < 6) {
|
||||
AppLoader.debugTimers["Ingame.update"] = measureNanoTime { updateGame(delta) }
|
||||
updateDeltaCounter -= renderRate
|
||||
updateTries++
|
||||
}
|
||||
}
|
||||
else {
|
||||
AppLoader.debugTimers["Ingame.update"] = measureNanoTime { updateGame(delta) }
|
||||
}*/
|
||||
|
||||
AppLoader.debugTimers["Ingame.update"] = measureNanoTime { updateGame(delta) }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user