feat(renderer): add new font resources and update theme font settings

This commit is contained in:
extio-de
2025-12-28 16:23:07 +01:00
parent 8636ddd6dd
commit a156423dfb
24 changed files with 135 additions and 77 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,7 @@
package de.extio.game_engine.renderer;
import de.extio.game_engine.renderer.model.options.UiOptions;
import de.extio.game_engine.renderer.model.options.VideoOptions;
import de.extio.game_engine.spatial2.model.CoordI2;
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
@@ -16,9 +18,13 @@ public interface RendererControl {
void setTitle(String title);
VideoOptions getVideoOptions();
void applyVideoOptions();
long getVideoOptionsAppliedAt();
UiOptions getUiOptions();
CoordI2 getAbsoluteViewportDimension();

View File

@@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory;
import de.extio.game_engine.keyboard.KeyModifiers;
import de.extio.game_engine.keyboard.KeycodeRegistration;
import de.extio.game_engine.renderer.g2d.theme.Theme;
import de.extio.game_engine.renderer.g2d.theme.Theme;
import de.extio.game_engine.renderer.model.options.VideoOptions;
public class RendererLoop implements Runnable {
@@ -32,9 +34,12 @@ public class RendererLoop implements Runnable {
this.rendererData.getStorageService()
.loadByPath(VideoOptions.class, List.of("gameEngine"), "videoOptions")
.ifPresent(options -> this.rendererData.getVideoOptions().apply(options));
this.rendererData.getRenderer().show();
this.rendererData.getThemeManager().setCurrentTheme((Theme)null);
LOGGER.info("Renderer loop started");
int exceptionCount = 0;
while (!this.shutdown) {

View File

@@ -31,6 +31,16 @@ import de.extio.game_engine.storage.StorageService;
@ConditionalOnProperty(name = "game-engine.renderer.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty(name = "game-engine.renderer.strategy", havingValue = "g2d", matchIfMissing = true)
public class G2DAutoConfiguration {
@Bean
G2DThemeManager g2dThemeManager(
final StaticResourceService staticResourceService,
final StorageService storageService,
final List<PatternRenderer> patternRendererList,
final Map<String, Theme> themes,
@Value("${game-engine.renderer.default-theme:urbanTheme}") final String defaultThemeName) {
return new G2DThemeManager(staticResourceService, storageService, patternRendererList, themes, defaultThemeName);
}
@Bean
G2DRenderer g2dRenderer(
@@ -46,8 +56,10 @@ public class G2DAutoConfiguration {
}
@Bean
G2DRendererControl g2dRendererControl(final G2DRenderer renderer) {
return new G2DRendererControl(renderer);
G2DRendererControl g2dRendererControl(final G2DRenderer renderer, final G2DThemeManager themeManager) {
var control = new G2DRendererControl(renderer);
themeManager.setG2dRendererControl(control);
return control;
}
@Bean
@@ -110,14 +122,4 @@ public class G2DAutoConfiguration {
return new BlueprintThemeFactoryBean();
}
@Bean
G2DThemeManager g2dThemeManager(
final StaticResourceService staticResourceService,
final StorageService storageService,
final List<PatternRenderer> patternRendererList,
final Map<String, Theme> themes,
@Value("${game-engine.renderer.default-theme:urbanTheme}") final String defaultThemeName) {
return new G2DThemeManager(staticResourceService, storageService, patternRendererList, themes, defaultThemeName);
}
}

View File

@@ -165,7 +165,7 @@ public class G2DRenderer implements Renderer {
throw new G2DRendererWindowNotInitializedException();
}
if (this.previousDefaultFont == null && this.rendererData.getUiOptions().getFontResource() != null && !this.rendererData.getUiOptions().getFontResource().equals(this.previousDefaultFont)) {
if (this.previousDefaultFont == null || (this.rendererData.getUiOptions().getFontResource() != null && !this.rendererData.getUiOptions().getFontResource().equals(this.previousDefaultFont))) {
this.previousDefaultFont = this.rendererData.getUiOptions().getFontResource();
G2DDrawFont.updateDefaultFont(this.rendererData.getStaticResourceService(), this.rendererData.getUiOptions().getFontResource());
}

View File

@@ -11,6 +11,8 @@ import org.slf4j.LoggerFactory;
import de.extio.game_engine.renderer.RendererControl;
import de.extio.game_engine.renderer.RendererData;
import de.extio.game_engine.renderer.model.event.ViewportResizeEvent;
import de.extio.game_engine.renderer.model.options.UiOptions;
import de.extio.game_engine.renderer.model.options.VideoOptions;
import de.extio.game_engine.renderer.model.options.VideoOptions.VideoOptionsVideoMode;
import de.extio.game_engine.spatial2.model.CoordI2;
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
@@ -286,6 +288,16 @@ public class G2DRendererControl implements RendererControl {
this.rendererData = rendererData;
}
@Override
public UiOptions getUiOptions() {
return this.rendererData.getUiOptions();
}
@Override
public VideoOptions getVideoOptions() {
return this.rendererData.getVideoOptions();
}
private static record G2DRendererControlOptions(double scaleFactorModifier) {
}
}

View File

@@ -8,6 +8,7 @@ public class BevelDarkThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Bevel Dark")
.font("Roboto-Regular.ttf")
.patternRendererName("bevelPatternRenderer")
// Highlight (Light Grey)
.borderOuter(new HSBColor(0.0f, 0.0f, 0.6f))

View File

@@ -8,6 +8,7 @@ public class BevelLightThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Bevel Light")
.font("Roboto-Regular.ttf")
.patternRendererName("bevelPatternRenderer")
// Highlight (White)
.borderOuter(new HSBColor(0.0f, 0.0f, 1.0f))

View File

@@ -8,6 +8,7 @@ public class BlueprintThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Blueprint")
.font("Inter_24pt-Regular.ttf")
.patternRendererName("blueprintPatternRenderer")
// Highlight/Border (White/Cyan)
.borderOuter(new HSBColor(0.5f, 0.0f, 1.0f))

View File

@@ -8,6 +8,7 @@ public class ContemporaryThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Contemporary")
.font("Lato-Regular.ttf")
.patternRendererName("modernPatternRenderer")
.borderOuter(new HSBColor(0.88f, 0.12f, 0.75f))
.borderInner(new HSBColor(0.88f, 0.10f, 0.92f))

View File

@@ -8,6 +8,7 @@ public class DreamThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Dream")
.font("Lora-Regular.ttf")
.patternRendererName("dreamPatternRenderer")
.borderOuter(new HSBColor(0.74f, 0.22f, 0.80f))
.borderInner(new HSBColor(0.83f, 0.18f, 0.95f))

View File

@@ -8,6 +8,7 @@ public class FantasyThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Fantasy")
.font("LibreBaskerville-Regular.ttf")
.patternRendererName("fantasyPatternRenderer")
.borderOuter(new HSBColor(0.12f, 0.55f, 0.48f))
.borderInner(new HSBColor(0.14f, 0.25f, 0.72f))

View File

@@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory;
import de.extio.game_engine.renderer.ThemeManager;
import de.extio.game_engine.renderer.g2d.G2DRenderer;
import de.extio.game_engine.renderer.g2d.G2DRendererControl;
import de.extio.game_engine.resource.StaticResource;
import de.extio.game_engine.resource.StaticResourceService;
import de.extio.game_engine.storage.StorageService;
@@ -25,6 +26,7 @@ public class G2DThemeManager implements ThemeManager {
private static final String THEME_STORAGE_NAME = "lastTheme";
private G2DRenderer g2dRenderer;
private G2DRendererControl g2dRendererControl;
private final StaticResourceService staticResourceService;
private final StorageService storageService;
@@ -52,33 +54,6 @@ public class G2DThemeManager implements ThemeManager {
LOGGER.info("G2DThemeManager initialized with {} pattern renderers: {}", this.patternRenderers.size(), this.patternRenderers.keySet());
LOGGER.info("G2DThemeManager initialized with {} themes: {}", this.themes.size(), this.themes.keySet());
this.currentTheme = this.defaultTheme;
this.loadLastUsedTheme();
}
private void registerTheme(final Theme theme) {
if (theme == null || theme.getName() == null || theme.getName().isBlank()) {
return;
}
this.themes.put(theme.getName(), theme);
}
private void loadLastUsedTheme() {
if (this.storageService == null) {
return;
}
try {
final Optional<Theme> persistedTheme = this.storageService.loadByPath(Theme.class, THEME_STORAGE_PATH, THEME_STORAGE_NAME);
if (persistedTheme.isPresent()) {
this.currentTheme = persistedTheme.get();
this.registerTheme(this.currentTheme);
LOGGER.info("Loaded last used theme: {}", this.currentTheme.getName());
return;
}
}
catch (final Exception e) {
LOGGER.warn("Could not load last used theme. Using default.", e);
}
}
@Override
@@ -131,7 +106,8 @@ public class G2DThemeManager implements ThemeManager {
@Override
public void setCurrentTheme(final String themeName) {
if (themeName == null || themeName.isBlank()) {
throw new IllegalArgumentException("themeName cannot be null/blank");
this.applyLastUsedOrDefault();
return;
}
final var direct = this.themes.get(themeName);
@@ -151,45 +127,26 @@ public class G2DThemeManager implements ThemeManager {
this.setCurrentTheme(this.loadTheme(themeName));
}
@Override
public void setCurrentTheme(final Theme theme) {
if (theme == null) {
this.applyLastUsedOrDefault();
return;
}
this.applyTheme(theme);
this.registerTheme(theme);
LOGGER.info("Theme switched to: {}", theme.getName());
this.persistLastUsedTheme(theme);
}
@Override
public Theme getCurrentTheme() {
return this.currentTheme;
}
public PatternRenderer getPatternRenderer(final String rendererName) {
return this.patternRenderers.get(rendererName);
}
@Override
public void setCurrentTheme(final Theme theme) {
if (theme == null) {
throw new IllegalArgumentException("Theme cannot be null");
}
this.currentTheme = theme;
this.registerTheme(theme);
LOGGER.info("Theme switched to: {}", theme.getName());
this.persistLastUsedTheme(theme);
if (this.g2dRenderer != null) {
this.g2dRenderer.reset();
}
}
private void persistLastUsedTheme(final Theme theme) {
if (this.storageService == null) {
return;
}
try {
this.storageService.store(THEME_STORAGE_PATH, THEME_STORAGE_NAME, theme);
}
catch (final Exception e) {
LOGGER.warn("Could not persist last used theme", e);
}
}
public Theme loadTheme(final String themeName) {
private Theme loadTheme(final String themeName) {
try {
final Optional<Theme> fromStorage = this.loadThemeFromStorage(themeName);
if (fromStorage.isPresent()) {
@@ -205,7 +162,57 @@ public class G2DThemeManager implements ThemeManager {
return this.defaultTheme;
}
private void applyLastUsedOrDefault() {
try {
final Optional<Theme> persistedTheme = this.storageService.loadByPath(Theme.class, THEME_STORAGE_PATH, THEME_STORAGE_NAME);
if (persistedTheme.isPresent()) {
this.applyTheme(persistedTheme.get());
this.registerTheme(this.currentTheme);
LOGGER.info("Loaded last used theme: {}", this.currentTheme.getName());
}
this.applyTheme(this.defaultTheme);
LOGGER.info("Loaded default theme: {}", this.currentTheme.getName());
}
catch (final Exception e) {
LOGGER.warn("Could not load last used theme. Using default.", e);
}
}
private void applyTheme(Theme theme) {
this.currentTheme = theme;
this.g2dRendererControl.getUiOptions().setFontResource(new StaticResource(List.of("renderer"), this.currentTheme.getFont()));
this.g2dRenderer.reset();
}
private void registerTheme(final Theme theme) {
if (theme == null || theme.getName() == null || theme.getName().isBlank()) {
return;
}
this.themes.put(theme.getName(), theme);
}
private void persistLastUsedTheme(final Theme theme) {
if (this.storageService == null) {
return;
}
try {
this.storageService.store(THEME_STORAGE_PATH, THEME_STORAGE_NAME, theme);
}
catch (final Exception e) {
LOGGER.warn("Could not persist last used theme", e);
}
}
public void setG2dRenderer(final G2DRenderer g2dRenderer) {
this.g2dRenderer = g2dRenderer;
}
public void setG2dRendererControl(G2DRendererControl g2dRendererControl) {
this.g2dRendererControl = g2dRendererControl;
}
public PatternRenderer getPatternRenderer(final String rendererName) {
return this.patternRenderers.get(rendererName);
}
}

View File

@@ -8,6 +8,7 @@ public class ModernThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Modern")
.font("Inter_24pt-Regular.ttf")
.patternRendererName("modernPatternRenderer")
.borderOuter(new HSBColor(0.58f, 0.08f, 0.82f))
.borderInner(new HSBColor(0.58f, 0.10f, 0.92f))

View File

@@ -8,6 +8,7 @@ public class NeonThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Neon")
.font("Geist-Regular.ttf")
.patternRendererName("dreamPatternRenderer")
.borderOuter(new HSBColor(0.83f, 0.80f, 0.95f))
.borderInner(new HSBColor(0.52f, 0.85f, 0.92f))

View File

@@ -8,6 +8,7 @@ public class NoirThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Noir")
.font("Lora-Regular.ttf")
.patternRendererName("urbanPatternRenderer")
.borderOuter(new HSBColor(0.00f, 0.00f, 0.18f))
.borderInner(new HSBColor(0.00f, 0.00f, 0.45f))

View File

@@ -8,6 +8,7 @@ public class SpacecraftThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Spacecraft")
.font("Roboto-Regular.ttf")
.patternRendererName("spacecraftPatternRenderer")
.borderOuter(new HSBColor(0.71f, 0.12f, 0.50f))
.borderInner(new HSBColor(0.71f, 0.12f, 0.80f))

View File

@@ -11,7 +11,8 @@ public class Theme {
private final String name;
private final String patternRendererName;
private final String font;
// Element colors - Borders
private final HSBColor borderOuter;
private final HSBColor borderInner;
@@ -40,6 +41,7 @@ public class Theme {
public Theme(
@JsonProperty("name") final String name,
@JsonProperty("patternRendererName") final String patternRendererName,
@JsonProperty("font") final String font,
@JsonProperty("borderOuter") final HSBColor borderOuter,
@JsonProperty("borderInner") final HSBColor borderInner,
@JsonProperty("borderInnerDisabled") final HSBColor borderInnerDisabled,
@@ -54,6 +56,7 @@ public class Theme {
@JsonProperty("pressedBrightnessAdjustment") final float pressedBrightnessAdjustment) {
this.name = name;
this.patternRendererName = patternRendererName;
this.font = font;
this.borderOuter = borderOuter;
this.borderInner = borderInner;
this.borderInnerDisabled = borderInnerDisabled;
@@ -72,6 +75,7 @@ public class Theme {
this(
builder.name,
builder.patternRendererName,
builder.font,
builder.borderOuter,
builder.borderInner,
builder.borderInnerDisabled,
@@ -94,6 +98,10 @@ public class Theme {
return this.patternRendererName;
}
public String getFont() {
return this.font;
}
public HSBColor getBorderOuter() {
return this.borderOuter;
}
@@ -149,6 +157,7 @@ public class Theme {
public static class Builder {
private String name;
private String patternRendererName;
private String font;
private HSBColor borderOuter;
private HSBColor borderInner;
private HSBColor borderInnerDisabled;
@@ -172,6 +181,11 @@ public class Theme {
return this;
}
public Builder font(final String font) {
this.font = font;
return this;
}
public Builder borderOuter(final HSBColor borderOuter) {
this.borderOuter = borderOuter;
return this;

View File

@@ -8,6 +8,7 @@ public class UrbanThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Urban")
.font("Geist-Regular.ttf")
.patternRendererName("metroPatternRenderer")
.borderOuter(new HSBColor(0.58f, 0.08f, 0.30f))
.borderInner(new HSBColor(0.58f, 0.06f, 0.72f))

View File

@@ -8,6 +8,7 @@ public class VintageThemeFactoryBean implements FactoryBean<Theme> {
public Theme getObject() {
return Theme.builder()
.name("Vintage")
.font("LibreBaskerville-Regular.ttf")
.patternRendererName("vintagePatternRenderer")
.borderOuter(new HSBColor(0.08f, 0.55f, 0.22f))
.borderInner(new HSBColor(0.10f, 0.30f, 0.55f))