mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
better splash
This commit is contained in:
@@ -265,10 +265,10 @@ public class App implements ApplicationListener {
|
|||||||
public static Hq2x hq2x;
|
public static Hq2x hq2x;
|
||||||
|
|
||||||
public static Mesh fullscreenQuad;
|
public static Mesh fullscreenQuad;
|
||||||
private static OrthographicCamera camera;
|
public static OrthographicCamera camera;
|
||||||
private static FlippingSpriteBatch logoBatch;
|
public static FlippingSpriteBatch logoBatch;
|
||||||
public static TextureRegion splashScreenLogo;
|
public static TextureRegion splashScreenLogo;
|
||||||
private static TextureRegion splashBackdrop;
|
public static TextureRegion splashBackdrop;
|
||||||
public static AudioDevice audioDevice;
|
public static AudioDevice audioDevice;
|
||||||
|
|
||||||
public static FlippingSpriteBatch batch;
|
public static FlippingSpriteBatch batch;
|
||||||
@@ -550,7 +550,7 @@ public class App implements ApplicationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Color splashTextCol = new Color(0x282828FF);
|
public static Color splashTextCol = new Color(0x282828FF);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create() {
|
public void create() {
|
||||||
@@ -752,6 +752,7 @@ public class App implements ApplicationListener {
|
|||||||
// hand over the scene control to this single class; Terrarum must call
|
// hand over the scene control to this single class; Terrarum must call
|
||||||
// 'AppLoader.getINSTANCE().screen.render(delta)', this is not redundant at all!
|
// 'AppLoader.getINSTANCE().screen.render(delta)', this is not redundant at all!
|
||||||
|
|
||||||
|
SplashScreen.loadingComplete = true;
|
||||||
IngameInstance title = ModMgr.INSTANCE.getTitleScreen(batch);
|
IngameInstance title = ModMgr.INSTANCE.getTitleScreen(batch);
|
||||||
|
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
@@ -882,107 +883,7 @@ public class App implements ApplicationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void drawSplash() {
|
public static void drawSplash() {
|
||||||
setCameraPosition(0f, 0f);
|
|
||||||
|
|
||||||
logoBatch.setColor(Color.WHITE);
|
|
||||||
logoBatch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
|
|
||||||
int drawWidth = Toolkit.INSTANCE.getDrawWidth();
|
|
||||||
int safetyTextLen = fontGame.getWidth(Lang.INSTANCE.get("APP_WARNING_HEALTH_AND_SAFETY", true));
|
|
||||||
int logoPosX0 = (drawWidth - splashScreenLogo.getRegionWidth() - safetyTextLen) >>> 1;
|
|
||||||
int logoPosY = Math.round(scr.getHeight() / 15f);
|
|
||||||
int textY = logoPosY + splashScreenLogo.getRegionHeight() - 16;
|
|
||||||
|
|
||||||
// draw custom backdrop (if exists)
|
|
||||||
if (splashBackdrop != null) {
|
|
||||||
logoBatch.setShader(null);
|
|
||||||
logoBatch.begin();
|
|
||||||
|
|
||||||
|
|
||||||
var size = ((float) Math.max(scr.getWidth(), scr.getHeight()));
|
|
||||||
var x = 0f;
|
|
||||||
var y = 0f;
|
|
||||||
if (scr.getWidth() > scr.getHeight()) {
|
|
||||||
y = (scr.getHeight() - size) / 2f;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = (scr.getWidth() - size) / 2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
logoBatch.draw(splashBackdrop, x, y, size, size);
|
|
||||||
|
|
||||||
|
|
||||||
logoBatch.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
int logoPosX = logoPosX0;//(int)(logoPosX0 + Math.round(100 * Math.sin(GLOBAL_RENDER_TIMER / 50.0)));
|
|
||||||
|
|
||||||
// draw logo reflection
|
|
||||||
logoBatch.setShader(shaderReflect);
|
|
||||||
logoBatch.setColor(Color.WHITE);
|
|
||||||
logoBatch.begin();
|
|
||||||
if (getConfigBoolean("showhealthmessageonstartup")) {
|
|
||||||
logoBatch.draw(splashScreenLogo, logoPosX, logoPosY + splashScreenLogo.getRegionHeight());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logoBatch.draw(splashScreenLogo, (drawWidth - splashScreenLogo.getRegionWidth()) / 2f,
|
|
||||||
(scr.getHeight() - splashScreenLogo.getRegionHeight() * 2) / 2f + splashScreenLogo.getRegionHeight()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
logoBatch.end();
|
|
||||||
logoBatch.setShader(null);
|
|
||||||
logoBatch.begin();
|
|
||||||
if (getConfigBoolean("showhealthmessageonstartup")) {
|
|
||||||
logoBatch.draw(splashScreenLogo, logoPosX, logoPosY);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logoBatch.draw(splashScreenLogo, (drawWidth - splashScreenLogo.getRegionWidth()) / 2f,
|
|
||||||
(scr.getHeight() - splashScreenLogo.getRegionHeight() * 2) / 2f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logoBatch.end();
|
|
||||||
|
|
||||||
// draw loading bar and subtitle
|
|
||||||
SplashScreen.render(logoBatch, camera);
|
SplashScreen.render(logoBatch, camera);
|
||||||
|
|
||||||
// draw health messages
|
|
||||||
if (getConfigBoolean("showhealthmessageonstartup")) {
|
|
||||||
logoBatch.setShader(null);
|
|
||||||
logoBatch.begin();
|
|
||||||
|
|
||||||
logoBatch.setColor(splashTextCol);
|
|
||||||
fontGame.draw(logoBatch, Lang.INSTANCE.get("APP_WARNING_HEALTH_AND_SAFETY", true),
|
|
||||||
logoPosX + splashScreenLogo.getRegionWidth(),
|
|
||||||
textY
|
|
||||||
);
|
|
||||||
|
|
||||||
// some chinese stuff
|
|
||||||
if (GAME_LOCALE.contentEquals("zhCN")) {
|
|
||||||
for (int i = 1; i <= 4; i++) {
|
|
||||||
String s = Lang.INSTANCE.get("APP_CHINESE_HEALTHY_GAME_MSG_" + i, true);
|
|
||||||
|
|
||||||
fontGame.draw(logoBatch, s,
|
|
||||||
(drawWidth - fontGame.getWidth(s)) >>> 1,
|
|
||||||
Math.round(scr.getHeight() * 12f / 15f + fontGame.getLineHeight() * (i - 1))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture tex1 = CommonResourcePool.INSTANCE.getAsTexture("title_health1");
|
|
||||||
Texture tex2 = CommonResourcePool.INSTANCE.getAsTexture("title_health2");
|
|
||||||
int virtualHeight = scr.getHeight() - logoPosY - splashScreenLogo.getRegionHeight() / 4;
|
|
||||||
int virtualHeightOffset = scr.getHeight() - virtualHeight;
|
|
||||||
logoBatch.drawFlipped(tex1, (drawWidth - tex1.getWidth()) >>> 1, virtualHeightOffset + (virtualHeight >>> 1) - 16, tex1.getWidth(), -tex1.getHeight());
|
|
||||||
logoBatch.drawFlipped(tex2, (drawWidth - tex2.getWidth()) >>> 1, virtualHeightOffset + (virtualHeight >>> 1) + 16 + tex2.getHeight(), tex2.getWidth(), -tex2.getHeight());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
logoBatch.end();
|
|
||||||
batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1471,7 +1372,7 @@ public class App implements ApplicationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void setCameraPosition(float newX, float newY) {
|
public static void setCameraPosition(float newX, float newY) {
|
||||||
camera.position.set((-newX + scr.getWidth() / 2), (-newY + scr.getHeight() / 2), 0f); // deliberate integer division
|
camera.position.set((-newX + scr.getWidth() / 2), (-newY + scr.getHeight() / 2), 0f); // deliberate integer division
|
||||||
camera.update();
|
camera.update();
|
||||||
logoBatch.setProjectionMatrix(camera.combined);
|
logoBatch.setProjectionMatrix(camera.combined);
|
||||||
|
|||||||
@@ -31,16 +31,6 @@ 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
|
||||||
@@ -162,7 +152,6 @@ 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) {
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
package net.torvald.terrarum
|
package net.torvald.terrarum
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.graphics.GL20
|
||||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
|
import com.badlogic.gdx.graphics.Texture
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.ui.Toolkit
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
import net.torvald.terrarum.ui.UICanvas
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
import net.torvald.terrarum.ui.UIItemHorzSlider
|
import net.torvald.terrarum.ui.UIItemHorzSlider
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the loading bar and subtitle on the cold-boot splash screen.
|
* Draws the cold-boot splash screen: backdrop, logo, loading bar, subtitle, and health messages.
|
||||||
* Called from App.drawSplash() (Java static context) on the GL thread.
|
* Consolidated from App.drawSplash() so all splash rendering lives here.
|
||||||
|
*
|
||||||
|
* Progress is time-based: p = t / (t + HALF_TIME_MS), snapping to 1.0 when
|
||||||
|
* [loadingComplete] is set to true by App right before transitioning to the title screen.
|
||||||
*
|
*
|
||||||
* Created by minjaesong on 2026-04-06.
|
* Created by minjaesong on 2026-04-06.
|
||||||
*/
|
*/
|
||||||
object SplashScreen {
|
object SplashScreen {
|
||||||
|
|
||||||
|
/** Set to true by App when loading completes, to snap the bar to 100%. */
|
||||||
|
@JvmField @Volatile var loadingComplete = false
|
||||||
|
|
||||||
|
private const val HALF_TIME_MS = 1500L // progress = t / (t + HALF_TIME_MS)
|
||||||
|
|
||||||
private val stubParent = object : UICanvas() {
|
private val stubParent = object : UICanvas() {
|
||||||
override var width = 0
|
override var width = 0
|
||||||
override var height = 0
|
override var height = 0
|
||||||
@@ -25,15 +37,74 @@ object SplashScreen {
|
|||||||
|
|
||||||
private var loadingBar: UIItemHorzSlider? = null
|
private var loadingBar: UIItemHorzSlider? = null
|
||||||
private var lastBarWidth = 0
|
private var lastBarWidth = 0
|
||||||
|
private var startTime = 0L
|
||||||
|
|
||||||
private val subtitleCol = Color(0.75f, 0.75f, 0.75f, 1f)
|
private val subtitleCol = Color(0.75f, 0.75f, 0.75f, 1f)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic fun render(batch: SpriteBatch, camera: OrthographicCamera) {
|
||||||
fun render(batch: SpriteBatch, camera: OrthographicCamera) {
|
App.setCameraPosition(0f, 0f)
|
||||||
val progress = CommonResourcePool.loadingProgress
|
|
||||||
|
val batch = App.logoBatch
|
||||||
|
val scr = App.scr
|
||||||
|
val drawWidth = Toolkit.drawWidth
|
||||||
|
val showHealth = App.getConfigBoolean("showhealthmessageonstartup")
|
||||||
|
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
|
||||||
|
val safetyTextLen = if (showHealth) App.fontGame.getWidth(Lang["APP_WARNING_HEALTH_AND_SAFETY", true]) else 0
|
||||||
|
val logoPosX0 = (drawWidth - App.splashScreenLogo.regionWidth - safetyTextLen) ushr 1
|
||||||
|
val logoPosY = Math.round(scr.height / 15f)
|
||||||
|
val textY = logoPosY + App.splashScreenLogo.regionHeight - 16
|
||||||
|
|
||||||
|
// backdrop
|
||||||
|
App.splashBackdrop?.let { backdrop ->
|
||||||
|
batch.setShader(null)
|
||||||
|
batch.inUse {
|
||||||
|
val size = max(scr.width, scr.height).toFloat()
|
||||||
|
val x = if (scr.width > scr.height) 0f else (scr.width - size) / 2f
|
||||||
|
val y = if (scr.width > scr.height) (scr.height - size) / 2f else 0f
|
||||||
|
batch.draw(backdrop, x, y, size, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val logoPosX = logoPosX0
|
||||||
|
|
||||||
|
// logo reflection
|
||||||
|
batch.setShader(App.shaderReflect)
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
batch.inUse {
|
||||||
|
if (showHealth) {
|
||||||
|
batch.draw(App.splashScreenLogo, logoPosX.toFloat(), (logoPosY + App.splashScreenLogo.regionHeight).toFloat())
|
||||||
|
} else {
|
||||||
|
batch.draw(App.splashScreenLogo,
|
||||||
|
(drawWidth - App.splashScreenLogo.regionWidth) / 2f,
|
||||||
|
(scr.height - App.splashScreenLogo.regionHeight * 2) / 2f + App.splashScreenLogo.regionHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logo
|
||||||
|
batch.setShader(null)
|
||||||
|
batch.inUse {
|
||||||
|
if (showHealth) {
|
||||||
|
batch.draw(App.splashScreenLogo, logoPosX.toFloat(), logoPosY.toFloat())
|
||||||
|
} else {
|
||||||
|
batch.draw(App.splashScreenLogo,
|
||||||
|
(drawWidth - App.splashScreenLogo.regionWidth) / 2f,
|
||||||
|
(scr.height - App.splashScreenLogo.regionHeight * 2) / 2f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loading bar+subtitle
|
||||||
|
if (startTime == 0L) startTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
val elapsed = System.currentTimeMillis() - startTime
|
||||||
|
val progress = if (loadingComplete) 1f
|
||||||
|
else elapsed.toFloat() / (elapsed + HALF_TIME_MS).toFloat()
|
||||||
|
|
||||||
val barWidth = (Toolkit.drawWidth * 0.4f).toInt().coerceAtLeast(200)
|
val barWidth = (Toolkit.drawWidth * 0.4f).toInt().coerceAtLeast(200)
|
||||||
val barX = (Toolkit.drawWidth - barWidth) / 2
|
val barX = (Toolkit.drawWidth - barWidth) / 2
|
||||||
val barY = App.scr.height - 48
|
val barY = App.scr.height - App.scr.tvSafeGraphicsHeight - 24
|
||||||
|
|
||||||
if (loadingBar == null || lastBarWidth != barWidth) {
|
if (loadingBar == null || lastBarWidth != barWidth) {
|
||||||
lastBarWidth = barWidth
|
lastBarWidth = barWidth
|
||||||
@@ -48,16 +119,36 @@ object SplashScreen {
|
|||||||
bar.posY = barY
|
bar.posY = barY
|
||||||
bar.handleWidth = (progress * barWidth).toInt().coerceIn(12, barWidth)
|
bar.handleWidth = (progress * barWidth).toInt().coerceIn(12, barWidth)
|
||||||
|
|
||||||
val subtitle = "${App.GAME_NAME} ${App.getVERSION_STRING()}"
|
val subtitle = "Reticulating Splines..."
|
||||||
val subtitleW = App.fontGame.getWidth(subtitle)
|
val subtitleW = App.fontGame.getWidth(subtitle)
|
||||||
val subtitleX = (Toolkit.drawWidth - subtitleW) / 2f
|
val subtitleX = (Toolkit.drawWidth - subtitleW) / 2f
|
||||||
val subtitleY = (barY - 20).toFloat() // leave gap above the bar
|
val subtitleY = (barY - 32).toFloat()
|
||||||
|
|
||||||
|
batch.inUse {
|
||||||
|
batch.color = subtitleCol
|
||||||
|
App.fontGame.draw(batch, subtitle, subtitleX, subtitleY)
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
bar.render(0f, batch, camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
// health messages
|
||||||
|
if (showHealth) {
|
||||||
|
batch.setShader(null)
|
||||||
|
batch.inUse {
|
||||||
|
batch.color = App.splashTextCol
|
||||||
|
App.fontGame.draw(batch, Lang["APP_WARNING_HEALTH_AND_SAFETY", true],
|
||||||
|
(logoPosX + App.splashScreenLogo.regionWidth).toFloat(), textY.toFloat())
|
||||||
|
|
||||||
|
val tex1 = CommonResourcePool.getAsTexture("title_health1")
|
||||||
|
val tex2 = CommonResourcePool.getAsTexture("title_health2")
|
||||||
|
val virtualHeight = scr.height - logoPosY - App.splashScreenLogo.regionHeight / 4
|
||||||
|
val virtualHeightOffset = scr.height - virtualHeight
|
||||||
|
batch.drawFlipped(tex1, ((drawWidth - tex1.width) ushr 1).toFloat(), (virtualHeightOffset + (virtualHeight ushr 1) - 16).toFloat(), tex1.width.toFloat(), (-tex1.height).toFloat())
|
||||||
|
batch.drawFlipped(tex2, ((drawWidth - tex2.width) ushr 1).toFloat(), (virtualHeightOffset + (virtualHeight ushr 1) + 16 + tex2.height).toFloat(), tex2.width.toFloat(), (-tex2.height).toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
|
||||||
batch.begin()
|
|
||||||
batch.color = subtitleCol
|
|
||||||
App.fontGame.draw(batch, subtitle, subtitleX, subtitleY)
|
|
||||||
batch.color = Color.WHITE
|
|
||||||
bar.render(0f, batch, camera)
|
|
||||||
batch.end()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user