Menü Buttons angepasst, Screenshot Funktion optimiert
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
@@ -1477,6 +1477,7 @@ public class EditorApp extends Application {
|
||||
saveItem.setAccelerator(javafx.scene.input.KeyCombination.keyCombination("Ctrl+S"));
|
||||
saveItem.setOnAction(e -> { input.saveRequested = true; setStatus("Speichern…"); });
|
||||
MenuItem screenshotItem = new MenuItem("Screenshot");
|
||||
screenshotItem.setAccelerator(javafx.scene.input.KeyCombination.keyCombination("Ctrl+F12"));
|
||||
screenshotItem.setOnAction(e -> takeEditorScreenshot());
|
||||
MenuItem settingsItem = new MenuItem("Einstellungen…");
|
||||
settingsItem.setOnAction(e -> openSettings());
|
||||
@@ -6442,6 +6443,7 @@ public class EditorApp extends Application {
|
||||
}
|
||||
|
||||
private void takeEditorScreenshot() {
|
||||
playScreenshotClick();
|
||||
WritableImage image = primaryStage.getScene().snapshot(null);
|
||||
String timestamp = java.time.LocalDateTime.now()
|
||||
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"));
|
||||
@@ -6456,6 +6458,34 @@ public class EditorApp extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private void playScreenshotClick() {
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
int sr = 44100;
|
||||
int frames = (int) (sr * 0.08);
|
||||
byte[] buf = new byte[frames * 2];
|
||||
java.util.Random rng = new java.util.Random();
|
||||
for (int i = 0; i < frames; i++) {
|
||||
double env = Math.exp(-i / (sr * 0.012));
|
||||
double s = (rng.nextGaussian() * 0.55 + Math.sin(2 * Math.PI * 3200.0 * i / sr) * 0.45) * env * 0.85;
|
||||
short val = (short) Math.max(-32767, Math.min(32767, s * 32767));
|
||||
buf[i * 2] = (byte) (val & 0xff);
|
||||
buf[i * 2 + 1] = (byte) ((val >> 8) & 0xff);
|
||||
}
|
||||
javax.sound.sampled.AudioFormat fmt = new javax.sound.sampled.AudioFormat(sr, 16, 1, true, false);
|
||||
javax.sound.sampled.DataLine.Info info = new javax.sound.sampled.DataLine.Info(javax.sound.sampled.SourceDataLine.class, fmt);
|
||||
javax.sound.sampled.SourceDataLine line = (javax.sound.sampled.SourceDataLine) javax.sound.sampled.AudioSystem.getLine(info);
|
||||
line.open(fmt);
|
||||
line.start();
|
||||
line.write(buf, 0, buf.length);
|
||||
line.drain();
|
||||
line.close();
|
||||
} catch (Exception ignored) {}
|
||||
}, "screenshot-click");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void handleTextureImport(javafx.stage.Window owner) {
|
||||
FileChooser fc = new FileChooser();
|
||||
fc.setTitle("Texturen importieren (nicht-PNG wird automatisch konvertiert)");
|
||||
|
||||
@@ -50,6 +50,7 @@ public class BlightGame extends SimpleApplication {
|
||||
private AudioSettings audioSettings;
|
||||
private ScreenshotAppState screenshotState;
|
||||
private Path screenshotDir;
|
||||
private com.jme3.audio.AudioNode screenshotClickSound;
|
||||
private WorldScene worldScene;
|
||||
private ConfigScreen configScreen;
|
||||
private GraphicsScreen graphicsScreen;
|
||||
@@ -380,8 +381,12 @@ public class BlightGame extends SimpleApplication {
|
||||
try {
|
||||
screenshotDir = BlightHome.resolve("screenshots");
|
||||
Files.createDirectories(screenshotDir);
|
||||
screenshotState = new ScreenshotAppState(screenshotDir + File.separator, "screenshot");
|
||||
screenshotState = new ScreenshotAppState("", "screenshot");
|
||||
stateManager.attach(screenshotState);
|
||||
screenshotClickSound = new com.jme3.audio.AudioNode(assetManager, "audio/static/screenshot_click.ogg", com.jme3.audio.AudioData.DataType.Buffer);
|
||||
screenshotClickSound.setPositional(false);
|
||||
screenshotClickSound.setLooping(false);
|
||||
screenshotClickSound.setVolume(1f);
|
||||
log.info("Screenshots werden gespeichert in: {}", screenshotDir.toAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
log.warn("Screenshot-Verzeichnis konnte nicht angelegt werden", e);
|
||||
@@ -389,8 +394,12 @@ public class BlightGame extends SimpleApplication {
|
||||
inputManager.addMapping("Screenshot", new KeyTrigger(KeyInput.KEY_F12));
|
||||
inputManager.addListener((ActionListener) (name, isPressed, tpf) -> {
|
||||
if (isPressed && screenshotState != null) {
|
||||
log.info("[Screenshot] Speichere in: {}", screenshotDir.toAbsolutePath());
|
||||
String ts = java.time.LocalDateTime.now()
|
||||
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"));
|
||||
screenshotState.setFileName(screenshotDir + java.io.File.separator + "blight_" + ts + "_");
|
||||
log.info("[Screenshot] Speichere: blight_{}.png", ts);
|
||||
screenshotState.takeScreenshot();
|
||||
if (screenshotClickSound != null) screenshotClickSound.playInstance();
|
||||
}
|
||||
}, "Screenshot");
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import com.jme3.texture.image.ColorSpace;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -127,6 +129,8 @@ public class NinePatch {
|
||||
// Guide-Pixel durch Rahmen-Nachbarfarbe ersetzen
|
||||
stripGuides(img, w, h, l - 1, w - r, t - 1, h - b);
|
||||
|
||||
img = gaussianBlur(img, 0.0f);
|
||||
|
||||
Texture2D tex = toJmeTexture(img, w, h);
|
||||
return new NinePatch(assets, tex, w, h, l, r, t, b);
|
||||
}
|
||||
@@ -219,6 +223,26 @@ public class NinePatch {
|
||||
return 0; // Fallback: transparent
|
||||
}
|
||||
|
||||
/**
|
||||
* Weicher Gauss-Blur (3×3-Kernel) auf das Bild – glättet Übergänge an Patch-Grenzen.
|
||||
* sigma=0 deaktiviert den Blur.
|
||||
*/
|
||||
private static BufferedImage gaussianBlur(BufferedImage img, float sigma) {
|
||||
if (sigma <= 0f) { return img; }
|
||||
float s2 = 2f * sigma * sigma;
|
||||
float[] data = {
|
||||
(float) Math.exp(-2 / s2), (float) Math.exp(-1 / s2), (float) Math.exp(-2 / s2),
|
||||
(float) Math.exp(-1 / s2), 1f, (float) Math.exp(-1 / s2),
|
||||
(float) Math.exp(-2 / s2), (float) Math.exp(-1 / s2), (float) Math.exp(-2 / s2)
|
||||
};
|
||||
float sum = 0; for (float v : data) sum += v;
|
||||
for (int i = 0; i < data.length; i++) data[i] /= sum;
|
||||
ConvolveOp op = new ConvolveOp(new Kernel(3, 3, data), ConvolveOp.EDGE_NO_OP, null);
|
||||
BufferedImage out = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
|
||||
op.filter(img, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert ein BufferedImage in eine JME3-Textur.
|
||||
* Zeilen werden von unten nach oben gelesen (entspricht JME3-Y-Flip beim PNG-Laden).
|
||||
@@ -266,7 +290,7 @@ public class NinePatch {
|
||||
*/
|
||||
public Node build(float x, float y, float w, float h, float z) {
|
||||
tex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||
tex.setMagFilter(Texture.MagFilter.Nearest);
|
||||
tex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||
|
||||
Material mat = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setTexture("ColorMap", tex);
|
||||
@@ -282,22 +306,75 @@ public class NinePatch {
|
||||
float sx1 = x + l, sx2 = x + w - r;
|
||||
float sy1 = y + b, sy2 = y + h - t;
|
||||
|
||||
// Kachelgröße des Mittelbereichs in Bildschirmpixeln
|
||||
float cw = (u2 - u1) * texW;
|
||||
float ch = (v2 - v1) * texH;
|
||||
|
||||
Node n = new Node("ninepatch");
|
||||
// Reihe unten
|
||||
n.attachChild(q(mat, x, y, l, b, 0, 0, u1, v1, z));
|
||||
n.attachChild(q(mat, sx1, y, sx2-sx1, b, u1, 0, u2, v1, z));
|
||||
n.attachChild(q(mat, sx2, y, r, b, u2, 0, 1, v1, z));
|
||||
// Reihe mitte
|
||||
n.attachChild(q(mat, x, sy1, l, sy2-sy1, 0, v1, u1, v2, z));
|
||||
n.attachChild(q(mat, sx1, sy1, sx2-sx1, sy2-sy1, u1, v1, u2, v2, z));
|
||||
n.attachChild(q(mat, sx2, sy1, r, sy2-sy1, u2, v1, 1, v2, z));
|
||||
// Reihe oben
|
||||
n.attachChild(q(mat, x, sy2, l, t, 0, v2, u1, 1, z));
|
||||
n.attachChild(q(mat, sx1, sy2, sx2-sx1, t, u1, v2, u2, 1, z));
|
||||
n.attachChild(q(mat, sx2, sy2, r, t, u2, v2, 1, 1, z));
|
||||
// Reihe unten – Ecken fix, untere Kante Mirror-X
|
||||
n.attachChild(q(mat, x, y, l, b, 0, 0, u1, v1, z));
|
||||
tileHEdge(n, mat, sx1, y, sx2, b, u1, u2, 0, v1, cw, texW, z);
|
||||
n.attachChild(q(mat, sx2, y, r, b, u2, 0, 1, v1, z));
|
||||
// Reihe mitte – Seiten Mirror-Y, Mitte Mirror-X+Y
|
||||
tileVEdge(n, mat, x, sy1, l, sy2, 0, u1, v1, v2, ch, texH, z);
|
||||
tileCenterQuads(n, mat, sx1, sy1, sx2, sy2, u1, v1, u2, v2, cw, ch, texW, texH, z);
|
||||
tileVEdge(n, mat, sx2, sy1, r, sy2, u2, 1f, v1, v2, ch, texH, z);
|
||||
// Reihe oben – Ecken fix, obere Kante Mirror-X
|
||||
n.attachChild(q(mat, x, sy2, l, t, 0, v2, u1, 1f, z));
|
||||
tileHEdge(n, mat, sx1, sy2, sx2, t, u1, u2, v2, 1f, cw, texW, z);
|
||||
n.attachChild(q(mat, sx2, sy2, r, t, u2, v2, 1f, 1f, z));
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Mitte: Mirror-Repeat in X und Y. */
|
||||
private static void tileCenterQuads(Node n, Material mat,
|
||||
float sx1, float sy1, float sx2, float sy2,
|
||||
float u1, float v1, float u2, float v2,
|
||||
float cw, float ch,
|
||||
float texW, float texH, float z) {
|
||||
int iz = 0;
|
||||
for (float py = sy1; py < sy2; py += ch, iz++) {
|
||||
float th = Math.min(ch, sy2 - py);
|
||||
float vA = (iz & 1) == 0 ? v1 : v2;
|
||||
float vB = (iz & 1) == 0 ? v1+th/texH : v2-th/texH;
|
||||
int ix = 0;
|
||||
for (float px = sx1; px < sx2; px += cw, ix++) {
|
||||
float tw = Math.min(cw, sx2 - px);
|
||||
float uA = (ix & 1) == 0 ? u1 : u2;
|
||||
float uB = (ix & 1) == 0 ? u1+tw/texW : u2-tw/texW;
|
||||
n.attachChild(q(mat, px, py, tw, th, uA, vA, uB, vB, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Horizontaler Randstreifen (oben/unten): Mirror-Repeat nur in X. */
|
||||
private static void tileHEdge(Node n, Material mat,
|
||||
float sx1, float py, float sx2, float ph,
|
||||
float u1, float u2, float vMin, float vMax,
|
||||
float cw, float texW, float z) {
|
||||
int ix = 0;
|
||||
for (float px = sx1; px < sx2; px += cw, ix++) {
|
||||
float tw = Math.min(cw, sx2 - px);
|
||||
float uA = (ix & 1) == 0 ? u1 : u2;
|
||||
float uB = (ix & 1) == 0 ? u1+tw/texW : u2-tw/texW;
|
||||
n.attachChild(q(mat, px, py, tw, ph, uA, vMin, uB, vMax, z));
|
||||
}
|
||||
}
|
||||
|
||||
/** Vertikaler Randstreifen (links/rechts): Mirror-Repeat nur in Y. */
|
||||
private static void tileVEdge(Node n, Material mat,
|
||||
float px, float sy1, float pw, float sy2,
|
||||
float uMin, float uMax, float v1, float v2,
|
||||
float ch, float texH, float z) {
|
||||
int iz = 0;
|
||||
for (float py = sy1; py < sy2; py += ch, iz++) {
|
||||
float th = Math.min(ch, sy2 - py);
|
||||
float vA = (iz & 1) == 0 ? v1 : v2;
|
||||
float vB = (iz & 1) == 0 ? v1+th/texH : v2-th/texH;
|
||||
n.attachChild(q(mat, px, py, pw, th, uMin, vA, uMax, vB, z));
|
||||
}
|
||||
}
|
||||
|
||||
private static Geometry q(Material mat,
|
||||
float x, float y, float w, float h,
|
||||
float uMin, float vMin, float uMax, float vMax,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user