mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
Compare commits
2 Commits
8eff89a7cb
...
f3fb8a96f0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3fb8a96f0 | ||
|
|
281146dd92 |
@@ -280,6 +280,11 @@ public class App implements ApplicationListener {
|
|||||||
private static TerrarumGamescreen currentScreen;
|
private static TerrarumGamescreen currentScreen;
|
||||||
private static LoadScreenBase currentSetLoadScreen;
|
private static LoadScreenBase currentSetLoadScreen;
|
||||||
|
|
||||||
|
/** When set, App.render() will capture postProcessorOutFBO and then transition to this screen after the current frame finishes. */
|
||||||
|
public static TerrarumGamescreen pendingScreenWithCapture = null;
|
||||||
|
/** Populated by the deferred-capture path; consumed by TitleScreen.show(). */
|
||||||
|
public static Pixmap captureLastFrame = null;
|
||||||
|
|
||||||
private void initViewPort(int width, int height) {
|
private void initViewPort(int width, int height) {
|
||||||
// Set Y to point downwards
|
// Set Y to point downwards
|
||||||
camera.setToOrtho(true, width, height); // some elements are pre-flipped, while some are not. The statement itself is absolutely necessary to make edge of the screen as the origin
|
camera.setToOrtho(true, width, height); // some elements are pre-flipped, while some are not. The statement itself is absolutely necessary to make edge of the screen as the origin
|
||||||
@@ -767,6 +772,17 @@ public class App implements ApplicationListener {
|
|||||||
|
|
||||||
currentScreen.render(UPDATE_RATE);
|
currentScreen.render(UPDATE_RATE);
|
||||||
postProcessorOutFBO = TerrarumPostProcessor.INSTANCE.draw(Gdx.graphics.getDeltaTime(), camera.combined, renderFBO);
|
postProcessorOutFBO = TerrarumPostProcessor.INSTANCE.draw(Gdx.graphics.getDeltaTime(), camera.combined, renderFBO);
|
||||||
|
|
||||||
|
// Deferred transition: capture the just-rendered frame then switch screens.
|
||||||
|
// This fires AFTER the current screen has fully rendered so the capture is of the real last ingame frame.
|
||||||
|
if (pendingScreenWithCapture != null) {
|
||||||
|
TerrarumGamescreen next = pendingScreenWithCapture;
|
||||||
|
pendingScreenWithCapture = null;
|
||||||
|
FrameBufferManager.begin(postProcessorOutFBO);
|
||||||
|
captureLastFrame = Pixmap.createFromFrameBuffer(0, 0, postProcessorOutFBO.getWidth(), postProcessorOutFBO.getHeight());
|
||||||
|
FrameBufferManager.end();
|
||||||
|
setScreen(next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -930,6 +946,9 @@ public class App implements ApplicationListener {
|
|||||||
|
|
||||||
logoBatch.end();
|
logoBatch.end();
|
||||||
|
|
||||||
|
// draw loading bar and subtitle
|
||||||
|
SplashScreen.render(logoBatch, camera);
|
||||||
|
|
||||||
// draw health messages
|
// draw health messages
|
||||||
if (getConfigBoolean("showhealthmessageonstartup")) {
|
if (getConfigBoolean("showhealthmessageonstartup")) {
|
||||||
logoBatch.setShader(null);
|
logoBatch.setShader(null);
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ object CommonResourcePool {
|
|||||||
|
|
||||||
private val slowLoadingQueue = ConcurrentLinkedQueue<ResourceLoadingDescriptor>()
|
private val slowLoadingQueue = ConcurrentLinkedQueue<ResourceLoadingDescriptor>()
|
||||||
private val slowLoadingRemaining = AtomicInteger(0)
|
private val slowLoadingRemaining = AtomicInteger(0)
|
||||||
|
private val slowLoadingTotal = AtomicInteger(0)
|
||||||
|
|
||||||
|
/** 0.0 = not started yet, 1.0 = all done. Only meaningful during / after [loadAllSlowly]. */
|
||||||
|
val loadingProgress: Float
|
||||||
|
get() {
|
||||||
|
val total = slowLoadingTotal.get()
|
||||||
|
if (total == 0) return 0f
|
||||||
|
val remaining = slowLoadingRemaining.get()
|
||||||
|
return (total - remaining).toFloat() / total.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
fun setGLThread(thread: Thread) {
|
fun setGLThread(thread: Thread) {
|
||||||
glThread = thread
|
glThread = thread
|
||||||
@@ -152,6 +162,7 @@ object CommonResourcePool {
|
|||||||
val desc = loadingList.removeFirst()
|
val desc = loadingList.removeFirst()
|
||||||
slowLoadingQueue.add(desc)
|
slowLoadingQueue.add(desc)
|
||||||
slowLoadingRemaining.incrementAndGet()
|
slowLoadingRemaining.incrementAndGet()
|
||||||
|
slowLoadingTotal.incrementAndGet()
|
||||||
}
|
}
|
||||||
// Block until the GL thread has processed all slow items
|
// Block until the GL thread has processed all slow items
|
||||||
while (slowLoadingRemaining.get() > 0) {
|
while (slowLoadingRemaining.get() > 0) {
|
||||||
|
|||||||
63
src/net/torvald/terrarum/SplashScreen.kt
Normal file
63
src/net/torvald/terrarum/SplashScreen.kt
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package net.torvald.terrarum
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
|
import net.torvald.terrarum.ui.UIItemHorzSlider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the loading bar and subtitle on the cold-boot splash screen.
|
||||||
|
* Called from App.drawSplash() (Java static context) on the GL thread.
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2026-04-06.
|
||||||
|
*/
|
||||||
|
object SplashScreen {
|
||||||
|
|
||||||
|
private val stubParent = object : UICanvas() {
|
||||||
|
override var width = 0
|
||||||
|
override var height = 0
|
||||||
|
override fun updateImpl(delta: Float) {}
|
||||||
|
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {}
|
||||||
|
override fun dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var loadingBar: UIItemHorzSlider? = null
|
||||||
|
private var lastBarWidth = 0
|
||||||
|
|
||||||
|
private val subtitleCol = Color(0.75f, 0.75f, 0.75f, 1f)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun render(batch: SpriteBatch, camera: OrthographicCamera) {
|
||||||
|
val progress = CommonResourcePool.loadingProgress
|
||||||
|
val barWidth = (Toolkit.drawWidth * 0.4f).toInt().coerceAtLeast(200)
|
||||||
|
val barX = (Toolkit.drawWidth - barWidth) / 2
|
||||||
|
val barY = App.scr.height - 48
|
||||||
|
|
||||||
|
if (loadingBar == null || lastBarWidth != barWidth) {
|
||||||
|
lastBarWidth = barWidth
|
||||||
|
loadingBar = UIItemHorzSlider(stubParent, barX, barY, 0.0, 0.0, 1.0, barWidth).also {
|
||||||
|
it.suppressHaptic = true
|
||||||
|
it.isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bar = loadingBar ?: return
|
||||||
|
bar.posX = barX
|
||||||
|
bar.posY = barY
|
||||||
|
bar.handleWidth = (progress * barWidth).toInt().coerceIn(12, barWidth)
|
||||||
|
|
||||||
|
val subtitle = "${App.GAME_NAME} ${App.getVERSION_STRING()}"
|
||||||
|
val subtitleW = App.fontGame.getWidth(subtitle)
|
||||||
|
val subtitleX = (Toolkit.drawWidth - subtitleW) / 2f
|
||||||
|
val subtitleY = (barY - 20).toFloat() // leave gap above the bar
|
||||||
|
|
||||||
|
batch.begin()
|
||||||
|
batch.color = subtitleCol
|
||||||
|
App.fontGame.draw(batch, subtitle, subtitleX, subtitleY)
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
bar.render(0f, batch, camera)
|
||||||
|
batch.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -727,7 +727,7 @@ class MovableWorldCamera(val parent: BuildingMaker) : ActorHumanoid(0, physProp
|
|||||||
|
|
||||||
class YamlCommandExit : YamlInvokable {
|
class YamlCommandExit : YamlInvokable {
|
||||||
override fun invoke(args: Array<Any>) {
|
override fun invoke(args: Array<Any>) {
|
||||||
App.setScreen(TitleScreen(App.batch))
|
App.pendingScreenWithCapture = TitleScreen(App.batch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
private var splashCapture: Texture? = null
|
private var splashCapture: Texture? = null
|
||||||
private var settleFrames = 0
|
private var settleFrames = 0
|
||||||
private var transitionState = 0 // 0=loading, 1=settling, 2=fading, 3=done
|
private var transitionState = 0 // 0=loading, 1=settling, 2=fading, 3=done
|
||||||
|
private var enteringFromIngame = false
|
||||||
|
|
||||||
private lateinit var demoWorld: GameWorld
|
private lateinit var demoWorld: GameWorld
|
||||||
private lateinit var cameraNodes: FloatArray // camera Y-pos
|
private lateinit var cameraNodes: FloatArray // camera Y-pos
|
||||||
@@ -296,6 +297,15 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
override fun show() {
|
override fun show() {
|
||||||
printdbg(this, "show() called")
|
printdbg(this, "show() called")
|
||||||
|
|
||||||
|
// consume the pre-captured frame provided by App.render()'s deferred-capture path
|
||||||
|
val pixmap = App.captureLastFrame
|
||||||
|
App.captureLastFrame = null
|
||||||
|
if (pixmap != null) {
|
||||||
|
splashCapture = Texture(pixmap)
|
||||||
|
pixmap.dispose()
|
||||||
|
enteringFromIngame = true
|
||||||
|
}
|
||||||
|
|
||||||
for (k in Input.Keys.F1..Input.Keys.F12) {
|
for (k in Input.Keys.F1..Input.Keys.F12) {
|
||||||
KeyToggler.forceSet(k, false)
|
KeyToggler.forceSet(k, false)
|
||||||
}
|
}
|
||||||
@@ -325,15 +335,22 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
|
|
||||||
override fun renderImpl(updateRate: Float) {
|
override fun renderImpl(updateRate: Float) {
|
||||||
when (transitionState) {
|
when (transitionState) {
|
||||||
// Loading phase: show splash, advance GL load steps
|
// Loading phase: show splash or last ingame frame, advance GL load steps
|
||||||
0 -> {
|
0 -> {
|
||||||
App.drawSplash()
|
if (enteringFromIngame) {
|
||||||
|
// hold the last ingame frame while the title screen loads in the background
|
||||||
|
drawSplashCapture(1f)
|
||||||
|
} else {
|
||||||
|
App.drawSplash()
|
||||||
|
}
|
||||||
processGLLoadStep()
|
processGLLoadStep()
|
||||||
if (loadDone) {
|
if (loadDone) {
|
||||||
// capture the current framebuffer (splash) as a texture
|
if (!enteringFromIngame) {
|
||||||
val pixmap = Pixmap.createFromFrameBuffer(0, 0, App.scr.width, App.scr.height)
|
// capture the current splash screen frame for the crossfade
|
||||||
splashCapture = Texture(pixmap)
|
val pixmap = Pixmap.createFromFrameBuffer(0, 0, App.scr.width, App.scr.height)
|
||||||
pixmap.dispose()
|
splashCapture = Texture(pixmap)
|
||||||
|
pixmap.dispose()
|
||||||
|
}
|
||||||
transitionState = 1
|
transitionState = 1
|
||||||
settleFrames = 0
|
settleFrames = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
|||||||
when (new) {
|
when (new) {
|
||||||
2 -> {
|
2 -> {
|
||||||
areYouSureMainMenuButtons.deselect()
|
areYouSureMainMenuButtons.deselect()
|
||||||
App.setScreen(TitleScreen(App.batch))
|
App.pendingScreenWithCapture = TitleScreen(App.batch)
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
screen = 0; areYouSureMainMenuButtons.deselect()
|
screen = 0; areYouSureMainMenuButtons.deselect()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class UIItemHorzSlider(
|
|||||||
override fun update(delta: Float) {
|
override fun update(delta: Float) {
|
||||||
super.update(delta)
|
super.update(delta)
|
||||||
|
|
||||||
mouseOnHandle = itemRelativeMouseX in handlePos.roundToInt() until handlePos.roundToInt() + handleWidth && itemRelativeMouseY in 0 until height
|
mouseOnHandle = if (!isEnabled) false else itemRelativeMouseX in handlePos.roundToInt() until handlePos.roundToInt() + handleWidth && itemRelativeMouseY in 0 until height
|
||||||
|
|
||||||
// update handle position and value
|
// update handle position and value
|
||||||
if (mouseUp && Terrarum.mouseDown || mouseLatched) {
|
if (mouseUp && Terrarum.mouseDown || mouseLatched) {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class UIItemVertSlider(
|
|||||||
override fun update(delta: Float) {
|
override fun update(delta: Float) {
|
||||||
super.update(delta)
|
super.update(delta)
|
||||||
|
|
||||||
mouseOnHandle = itemRelativeMouseY in handlePos.roundToInt() until handlePos.roundToInt() + handleHeight && itemRelativeMouseX in 0 until width
|
mouseOnHandle = if (!isEnabled) false else itemRelativeMouseY in handlePos.roundToInt() until handlePos.roundToInt() + handleHeight && itemRelativeMouseX in 0 until width
|
||||||
|
|
||||||
// update handle position and value
|
// update handle position and value
|
||||||
if (mouseUp && Terrarum.mouseDown || mouseLatched) {
|
if (mouseUp && Terrarum.mouseDown || mouseLatched) {
|
||||||
|
|||||||
Reference in New Issue
Block a user