feat(theme): implement background pattern rendering for various pattern renderers

This commit is contained in:
extio-de
2025-12-28 17:08:15 +01:00
parent a156423dfb
commit d0f5b26286
13 changed files with 364 additions and 73 deletions

View File

@@ -17,6 +17,7 @@ import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.RendererControl; import de.extio.game_engine.renderer.RendererControl;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.renderer.g2d.G2DRendererControl; import de.extio.game_engine.renderer.g2d.G2DRendererControl;
import de.extio.game_engine.renderer.g2d.theme.G2DThemeManager;
import de.extio.game_engine.renderer.model.RenderingBo; import de.extio.game_engine.renderer.model.RenderingBo;
import de.extio.game_engine.renderer.model.RenderingBoLayer; import de.extio.game_engine.renderer.model.RenderingBoLayer;
import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor; import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor;
@@ -39,12 +40,6 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
private final static int[] BUILTIN_SCROLL_OFFSET_X = new int[2]; private final static int[] BUILTIN_SCROLL_OFFSET_X = new int[2];
private final static List<Star> STARS = new ArrayList<>();
private final static List<Star> REFERENCE_STARS = new ArrayList<>();
private static CoordI2 STARS_LAST_VIEWPORT = ImmutableCoordI2.create();
private final CoordI2 bgrOffset = MutableCoordI2.create(); private final CoordI2 bgrOffset = MutableCoordI2.create();
private final CoordI2 sourceOffset = MutableCoordI2.create(); private final CoordI2 sourceOffset = MutableCoordI2.create();
@@ -97,7 +92,7 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
this.bgrOffset.setXY(this.rendererData.getUiOptions().getBackgroundOffset0()).add(BUILTIN_SCROLL_OFFSET_X[0], 0), windowDim); this.bgrOffset.setXY(this.rendererData.getUiOptions().getBackgroundOffset0()).add(BUILTIN_SCROLL_OFFSET_X[0], 0), windowDim);
this.scroll(1, this.rendererData.getUiOptions().isBackgroundScrolling1(), this.rendererData.getUiOptions().isBackgroundScrollingReverse1(), windowDim); this.scroll(1, this.rendererData.getUiOptions().isBackgroundScrolling1(), this.rendererData.getUiOptions().isBackgroundScrollingReverse1(), windowDim);
this.drawStars(graphics, this.bgrOffset.setXY(this.rendererData.getUiOptions().getBackgroundOffset1()).add(BUILTIN_SCROLL_OFFSET_X[1], 0), windowDim); ((G2DThemeManager) this.rendererData.getThemeManager()).getCurrentPatternRenderer().drawBackgroundPattern(graphics, this.bgrOffset.setXY(this.rendererData.getUiOptions().getBackgroundOffset1()).add(BUILTIN_SCROLL_OFFSET_X[1], 0), windowDim);
} }
private void tileBackground(final Graphics2D graphics, final int index, final StaticResource backgroundResource, final CoordI2 offset, final CoordI2 viewPort) { private void tileBackground(final Graphics2D graphics, final int index, final StaticResource backgroundResource, final CoordI2 offset, final CoordI2 viewPort) {
@@ -146,19 +141,6 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
} while (this.destPosition.getY() < viewPort.getY()); } while (this.destPosition.getY() < viewPort.getY());
} }
private void drawStars(final Graphics2D graphics, final CoordI2 offset, final CoordI2 viewPort) {
this.generateStars(viewPort);
for (final Star element : STARS) {
graphics.setColor(element.color.toAwtColor());
graphics.fillOval(
Math.floorMod(element.position.getX() - offset.getX(), viewPort.getX()),
Math.floorMod(element.position.getY() - offset.getY(), viewPort.getY()),
element.radius.getX(),
element.radius.getY());
}
}
private void scroll(final int index, final boolean scrolling, final boolean reverse, final CoordI2 viewPort) { private void scroll(final int index, final boolean scrolling, final boolean reverse, final CoordI2 viewPort) {
if (!scrolling) { if (!scrolling) {
BUILTIN_SCROLL_OFFSET_X[index] = 0; BUILTIN_SCROLL_OFFSET_X[index] = 0;
@@ -235,50 +217,4 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
} }
} }
private void generateStars(final CoordI2 viewPort) {
if (REFERENCE_STARS.isEmpty()) {
final var rand = ThreadLocalXorShift128Random.current();
for (var i = 0; i < 350; i++) {
final var position = ImmutableCoordI2.create(rand.nextInt(RendererControl.REFERENCE_RESOLUTION.getX()), rand.nextInt(RendererControl.REFERENCE_RESOLUTION.getY()));
final var radius = ImmutableCoordI2.create(rand.nextInt(3) + 2, rand.nextInt(3) + 2);
final var color = new ImmutableRgbaColor(rand.nextInt(30) + 100, rand.nextInt(30) + 100, rand.nextInt(30) + 130);
REFERENCE_STARS.add(new Star(position, radius, color));
STARS.add(new Star(position, radius, color));
}
}
if (!viewPort.equals(STARS_LAST_VIEWPORT)) {
final double scaleX = (double) viewPort.getX() / RendererControl.REFERENCE_RESOLUTION.getX();
final double scaleY = (double) viewPort.getY() / RendererControl.REFERENCE_RESOLUTION.getY();
for (int i = 0; i < REFERENCE_STARS.size(); i++) {
final var refStar = REFERENCE_STARS.get(i);
final var star = STARS.get(i);
star.position = ImmutableCoordI2.create(
(int) (refStar.position.getX() * scaleX),
(int) (refStar.position.getY() * scaleY));
}
STARS_LAST_VIEWPORT = viewPort.toImmutableCoordI2();
}
}
private static class Star {
CoordI2 position;
CoordI2 radius;
RgbaColor color;
Star(final CoordI2 position, final CoordI2 radius, final RgbaColor color) {
super();
this.position = position;
this.radius = radius;
this.color = color;
}
}
} }

View File

@@ -7,6 +7,7 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.spatial2.model.CoordI2;
/** /**
* Implementation of PatternRenderer that draws borders and panels in a classic "Bevel" style. * Implementation of PatternRenderer that draws borders and panels in a classic "Bevel" style.
@@ -27,11 +28,12 @@ public class BevelPatternRenderer implements PatternRenderer {
if (hsb[2] < 0.5f) { if (hsb[2] < 0.5f) {
light = color.brighter(); light = color.brighter();
dark = color; dark = color;
} else { }
else {
light = color; light = color;
dark = color.darker(); dark = color.darker();
} }
// Outer bevel (raised) // Outer bevel (raised)
drawBevel(g2d, x, y, width, height, strength + 1, true, light, dark); drawBevel(g2d, x, y, width, height, strength + 1, true, light, dark);
} }
@@ -71,14 +73,16 @@ public class BevelPatternRenderer implements PatternRenderer {
// Button bevel // Button bevel
int bevelStrength = (int) (2 * scaleFactor); int bevelStrength = (int) (2 * scaleFactor);
if (bevelStrength < 1) bevelStrength = 1; if (bevelStrength < 1)
bevelStrength = 1;
final Color highlight = theme.getBorderOuter().toColor(); final Color highlight = theme.getBorderOuter().toColor();
final Color shadow = theme.getBorderInner().toColor(); final Color shadow = theme.getBorderInner().toColor();
if (pressed) { if (pressed) {
drawBevel(g2d, 0, 0, width, height, bevelStrength, false, highlight, shadow); drawBevel(g2d, 0, 0, width, height, bevelStrength, false, highlight, shadow);
} else { }
else {
drawBevel(g2d, 0, 0, width, height, bevelStrength, true, highlight, shadow); drawBevel(g2d, 0, 0, width, height, bevelStrength, true, highlight, shadow);
} }
@@ -88,14 +92,14 @@ public class BevelPatternRenderer implements PatternRenderer {
final int offset = pressed ? 1 : 0; final int offset = pressed ? 1 : 0;
// Draw a simple X // Draw a simple X
final int padding = (int)(4 * scaleFactor); final int padding = (int) (4 * scaleFactor);
final int x1 = padding + offset; final int x1 = padding + offset;
final int y1 = padding + offset; final int y1 = padding + offset;
final int x2 = width - padding + offset; final int x2 = width - padding + offset;
final int y2 = height - padding + offset; final int y2 = height - padding + offset;
final Graphics2D g2 = (Graphics2D) g2d.create(); final Graphics2D g2 = (Graphics2D) g2d.create();
g2.setStroke(new java.awt.BasicStroke((float)(2 * scaleFactor))); g2.setStroke(new java.awt.BasicStroke((float) (2 * scaleFactor)));
g2.drawLine(x1, y1, x2, y2); g2.drawLine(x1, y1, x2, y2);
g2.drawLine(x1, y2, x2, y1); g2.drawLine(x1, y2, x2, y1);
g2.dispose(); g2.dispose();
@@ -118,4 +122,18 @@ public class BevelPatternRenderer implements PatternRenderer {
g2d.drawLine(x + width - 1 - i, y + i, x + width - 1 - i, y + height - 1 - i); // Right g2d.drawLine(x + width - 1 - i, y + i, x + width - 1 - i, y + height - 1 - i); // Right
} }
} }
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
g2d.setColor(new Color(255, 255, 255, 35));
final int spacing = 20;
final int offX = Math.floorMod(offset.getX(), spacing);
final int offY = Math.floorMod(offset.getY(), spacing);
for (int x = -spacing; x < viewPort.getX() + spacing; x += spacing) {
for (int y = -spacing; y < viewPort.getY() + spacing; y += spacing) {
g2d.fillRect(x + offX, y + offY, 2, 2);
}
}
}
} }

View File

@@ -4,12 +4,14 @@ import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Stroke; import java.awt.Stroke;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
import de.extio.game_engine.spatial2.model.CoordI2;
/** /**
* Implementation of PatternRenderer that draws a technical "Blueprint" style with grids and measurement markers. * Implementation of PatternRenderer that draws a technical "Blueprint" style with grids and measurement markers.
@@ -126,4 +128,41 @@ public class BlueprintPatternRenderer implements PatternRenderer {
g2.dispose(); g2.dispose();
} }
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int gridSize = 50;
this.patternCache = new BufferedImage(gridSize, gridSize, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, gridSize, gridSize);
g2d.setColor(new Color(255, 255, 255, 35));
g2d.setStroke(new BasicStroke(1f));
g2d.drawLine(0, 0, 0, gridSize);
g2d.drawLine(0, 0, gridSize, 0);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int gridSize = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), gridSize);
final int offY = Math.floorMod(offset.getY(), gridSize);
for (int x = -gridSize; x <= viewPort.getX() + gridSize; x += gridSize) {
for (int y = -gridSize; y <= viewPort.getY() + gridSize; y += gridSize) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -2,12 +2,14 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -96,4 +98,41 @@ public class DreamPatternRenderer implements PatternRenderer {
final var textDim = G2DDrawFont.getTextDimensions("X", g2d, 15, scaleFactor); final var textDim = G2DDrawFont.getTextDimensions("X", g2d, 15, scaleFactor);
G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), fontSize, "X"); G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), fontSize, "X");
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int cellSize = 100;
this.patternCache = new BufferedImage(cellSize, cellSize, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
// Clear background (transparent)
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, cellSize, cellSize);
// Draw pattern
g2d.setColor(new Color(255, 255, 255, 35));
g2d.fillOval(20, 20, 40, 40);
g2d.fillOval(70, 80, 15, 15);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int cellSize = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), cellSize);
final int offY = Math.floorMod(offset.getY(), cellSize);
for (int x = -cellSize; x < viewPort.getX() + cellSize; x += cellSize) {
for (int y = -cellSize; y < viewPort.getY() + cellSize; y += cellSize) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -2,11 +2,13 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -105,4 +107,40 @@ public class FantasyPatternRenderer implements PatternRenderer {
g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t); g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t);
g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize); g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize);
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int cellSize = 80;
this.patternCache = new BufferedImage(cellSize, cellSize, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, cellSize, cellSize);
g2d.setColor(new Color(255, 255, 255, 35));
g2d.fillRect(10, 10, 2, 2);
g2d.fillRect(50, 60, 3, 3);
g2d.fillRect(30, 50, 1, 1);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int cellSize = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), cellSize);
final int offY = Math.floorMod(offset.getY(), cellSize);
for (int x = -cellSize; x < viewPort.getX() + cellSize; x += cellSize) {
for (int y = -cellSize; y < viewPort.getY() + cellSize; y += cellSize) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -169,6 +169,7 @@ public class G2DThemeManager implements ThemeManager {
this.applyTheme(persistedTheme.get()); this.applyTheme(persistedTheme.get());
this.registerTheme(this.currentTheme); this.registerTheme(this.currentTheme);
LOGGER.info("Loaded last used theme: {}", this.currentTheme.getName()); LOGGER.info("Loaded last used theme: {}", this.currentTheme.getName());
return;
} }
this.applyTheme(this.defaultTheme); this.applyTheme(this.defaultTheme);
@@ -215,4 +216,8 @@ public class G2DThemeManager implements ThemeManager {
public PatternRenderer getPatternRenderer(final String rendererName) { public PatternRenderer getPatternRenderer(final String rendererName) {
return this.patternRenderers.get(rendererName); return this.patternRenderers.get(rendererName);
} }
public PatternRenderer getCurrentPatternRenderer() {
return this.patternRenderers.get(this.currentTheme.getPatternRendererName());
}
} }

View File

@@ -2,11 +2,13 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -109,4 +111,38 @@ public class MetroPatternRenderer implements PatternRenderer {
g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t); g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t);
g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize); g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize);
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int spacing = 40;
this.patternCache = new BufferedImage(spacing, spacing, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, spacing, spacing);
g2d.setColor(new Color(255, 255, 255, 35));
g2d.drawLine(0, 0, spacing, spacing);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int spacing = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), spacing);
final int offY = Math.floorMod(offset.getY(), spacing);
for (int x = -spacing; x < viewPort.getX() + spacing; x += spacing) {
for (int y = -spacing; y < viewPort.getY() + spacing; y += spacing) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -2,12 +2,14 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -96,4 +98,42 @@ public class ModernPatternRenderer implements PatternRenderer {
final var textDim = G2DDrawFont.getTextDimensions("X", g2d, 14, scaleFactor); final var textDim = G2DDrawFont.getTextDimensions("X", g2d, 14, scaleFactor);
G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), fontSize, "X"); G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), fontSize, "X");
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int spacing = 60;
this.patternCache = new BufferedImage(spacing, spacing, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, spacing, spacing);
g2d.setColor(new Color(255, 255, 255, 35));
final int len = 10;
final int cx = spacing / 2;
final int cy = spacing / 2;
g2d.drawLine(cx - len, cy, cx + len, cy);
g2d.drawLine(cx, cy - len, cx, cy + len);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int spacing = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), spacing);
final int offY = Math.floorMod(offset.getY(), spacing);
for (int x = -spacing; x < viewPort.getX() + spacing; x += spacing) {
for (int y = -spacing; y < viewPort.getY() + spacing; y += spacing) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -3,6 +3,8 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import de.extio.game_engine.spatial2.model.CoordI2;
/** /**
* Interface for rendering common UI patterns like borders, backgrounds, shadows, etc. * Interface for rendering common UI patterns like borders, backgrounds, shadows, etc.
* Different implementations can provide different visual styles. * Different implementations can provide different visual styles.
@@ -64,4 +66,13 @@ public interface PatternRenderer {
* @param theme Theme to use for colors * @param theme Theme to use for colors
*/ */
void drawCloseButton(Graphics2D g2d, int width, int height, boolean enabled, int state, Color backgroundColor, double scaleFactor, Theme theme); void drawCloseButton(Graphics2D g2d, int width, int height, boolean enabled, int state, Color backgroundColor, double scaleFactor, Theme theme);
/**
* Draws a background pattern for the UI on layer BACKGROUND1
*
* @param g2d The graphics context
* @param offset Offset for pattern alignment
* @param viewPort Viewport size
*/
void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort);
} }

View File

@@ -2,13 +2,20 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.RendererControl;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor;
import de.extio.game_engine.renderer.model.color.RgbaColor; import de.extio.game_engine.renderer.model.color.RgbaColor;
import de.extio.game_engine.spatial2.model.CoordI2;
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
import de.extio.game_engine.util.rng.ThreadLocalXorShift128Random;
/** /**
* Default implementation of PatternRenderer that draws borders and panels in a decorative style. * Default implementation of PatternRenderer that draws borders and panels in a decorative style.
@@ -18,6 +25,12 @@ import de.extio.game_engine.renderer.model.color.RgbaColor;
@Component @Component
public class SpacecraftPatternRenderer implements PatternRenderer { public class SpacecraftPatternRenderer implements PatternRenderer {
private final static List<Star> STARS = new ArrayList<>();
private final static List<Star> REFERENCE_STARS = new ArrayList<>();
private static CoordI2 STARS_LAST_VIEWPORT = ImmutableCoordI2.create();
@Override @Override
public void drawDecorativeBorder(final Graphics2D g2d, final int x, final int y, final int width, final int height, final int strength, final Color color) { public void drawDecorativeBorder(final Graphics2D g2d, final int x, final int y, final int width, final int height, final int strength, final Color color) {
final var strength2 = strength * 2; final var strength2 = strength * 2;
@@ -99,4 +112,65 @@ public class SpacecraftPatternRenderer implements PatternRenderer {
G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), (int) (16 * scaleFactor), "X"); G2DDrawFont.renderText(g2d, textDim, 1.0, ((width - textDim.getX()) / 2), ((height - textDim.getY()) / 2), (int) (16 * scaleFactor), "X");
} }
} }
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
this.generateStars(viewPort);
for (final Star element : STARS) {
g2d.setColor(element.color.toAwtColor());
g2d.fillOval(
Math.floorMod(element.position.getX() - offset.getX(), viewPort.getX()),
Math.floorMod(element.position.getY() - offset.getY(), viewPort.getY()),
element.radius.getX(),
element.radius.getY());
}
}
private void generateStars(final CoordI2 viewPort) {
if (REFERENCE_STARS.isEmpty()) {
final var rand = ThreadLocalXorShift128Random.current();
for (var i = 0; i < 350; i++) {
final var position = ImmutableCoordI2.create(rand.nextInt(RendererControl.REFERENCE_RESOLUTION.getX()), rand.nextInt(RendererControl.REFERENCE_RESOLUTION.getY()));
final var radius = ImmutableCoordI2.create(rand.nextInt(3) + 2, rand.nextInt(3) + 2);
final var color = new ImmutableRgbaColor(rand.nextInt(30) + 100, rand.nextInt(30) + 100, rand.nextInt(30) + 130);
REFERENCE_STARS.add(new Star(position, radius, color));
STARS.add(new Star(position, radius, color));
}
}
if (!viewPort.equals(STARS_LAST_VIEWPORT)) {
final double scaleX = (double) viewPort.getX() / RendererControl.REFERENCE_RESOLUTION.getX();
final double scaleY = (double) viewPort.getY() / RendererControl.REFERENCE_RESOLUTION.getY();
for (int i = 0; i < REFERENCE_STARS.size(); i++) {
final var refStar = REFERENCE_STARS.get(i);
final var star = STARS.get(i);
star.position = ImmutableCoordI2.create(
(int) (refStar.position.getX() * scaleX),
(int) (refStar.position.getY() * scaleY));
}
STARS_LAST_VIEWPORT = viewPort.toImmutableCoordI2();
}
}
private static class Star {
CoordI2 position;
CoordI2 radius;
RgbaColor color;
Star(final CoordI2 position, final CoordI2 radius, final RgbaColor color) {
super();
this.position = position;
this.radius = radius;
this.color = color;
}
}
} }

View File

@@ -7,6 +7,7 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -110,4 +111,20 @@ public class UrbanPatternRenderer implements PatternRenderer {
g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t); g2d.fillRect(centerX - crossSize / 2, centerY - t / 2, crossSize, t);
g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize); g2d.fillRect(centerX - t / 2, centerY - crossSize / 2, t, crossSize);
} }
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
g2d.setColor(new Color(255, 255, 255, 35));
final int cellSize = 60;
final int offX = Math.floorMod(offset.getX(), cellSize);
final int offY = Math.floorMod(offset.getY(), cellSize);
for (int x = -cellSize; x < viewPort.getX() + cellSize; x += cellSize) {
for (int y = -cellSize; y < viewPort.getY() + cellSize; y += cellSize) {
// Vertical streaks
g2d.fillRect(x + offX + 10, y + offY + 5, 2, 15);
g2d.fillRect(x + offX + 40, y + offY + 35, 2, 10);
}
}
}
} }

View File

@@ -2,11 +2,13 @@ package de.extio.game_engine.renderer.g2d.theme;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.extio.game_engine.renderer.g2d.G2DRendererCondition; import de.extio.game_engine.renderer.g2d.G2DRendererCondition;
import de.extio.game_engine.spatial2.model.CoordI2;
@Conditional(G2DRendererCondition.class) @Conditional(G2DRendererCondition.class)
@Component @Component
@@ -146,4 +148,40 @@ public class VintagePatternRenderer implements PatternRenderer {
g2d.fillRect(centerX - crossThickness / 2, centerY - crossSize / 2, crossThickness, crossSize); g2d.fillRect(centerX - crossThickness / 2, centerY - crossSize / 2, crossThickness, crossSize);
} }
} }
private java.awt.image.BufferedImage patternCache;
private java.awt.image.BufferedImage getPatternCache() {
if (this.patternCache == null) {
final int cellSize = 150;
this.patternCache = new BufferedImage(cellSize, cellSize, java.awt.image.BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = this.patternCache.createGraphics();
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, cellSize, cellSize);
g2d.setColor(new Color(255, 255, 255, 35));
g2d.drawLine(10, 10, 20, 30);
g2d.drawLine(60, 50, 55, 70);
g2d.fillOval(80, 20, 2, 2);
g2d.dispose();
}
return this.patternCache;
}
@Override
public void drawBackgroundPattern(final Graphics2D g2d, final CoordI2 offset, final CoordI2 viewPort) {
final var cache = this.getPatternCache();
final int cellSize = cache.getWidth();
final int offX = Math.floorMod(offset.getX(), cellSize);
final int offY = Math.floorMod(offset.getY(), cellSize);
for (int x = -cellSize; x < viewPort.getX() + cellSize; x += cellSize) {
for (int y = -cellSize; y < viewPort.getY() + cellSize; y += cellSize) {
g2d.drawImage(cache, x + offX, y + offY, null);
}
}
}
} }

View File

@@ -16,7 +16,7 @@ public final class UiOptions {
private boolean backgroundScrollingReverse0; private boolean backgroundScrollingReverse0;
private boolean backgroundScrolling1 = true; private boolean backgroundScrolling1;
private boolean backgroundScrollingReverse1; private boolean backgroundScrollingReverse1;