Compare commits
3 Commits
c9ab15bda5
...
dd32ec65ca
| Author | SHA1 | Date | |
|---|---|---|---|
| dd32ec65ca | |||
| c09502ad3a | |||
| ee8c235655 |
|
Before Width: | Height: | Size: 156 B After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
blight-assets/src/main/resources/Textures/menu/backup/button.png
Normal file
|
After Width: | Height: | Size: 333 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 6.8 KiB |
@@ -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)");
|
||||
|
||||
@@ -994,9 +994,27 @@ public class VoxelEditorState extends BaseAppState {
|
||||
if (cy == aCy) {
|
||||
currentTop = Math.max(0, Math.min(VoxelChunk.SIZE - 1,
|
||||
(int)(anchor - cy * (float) VoxelChunk.CELLS)));
|
||||
} else {
|
||||
// Chunks außerhalb des Anker-Chunks ohne bestehende Voxel: überspringen.
|
||||
} else if (cy < aCy) {
|
||||
// Chunk unterhalb der Terrain-Oberfläche: sofort komplett auffüllen.
|
||||
// Verhindert flache Schnitt-Kante bei Y=0 / Wasserrand.
|
||||
float chunkBaseY = cy * (float) VoxelChunk.CELLS;
|
||||
if (chunkBaseY + VoxelChunk.SIZE >= -10f) {
|
||||
int yFloor = Math.max(0, (int) Math.ceil(-10f - chunkBaseY));
|
||||
for (int lly = yFloor; lly < VoxelChunk.SIZE; lly++) {
|
||||
chunk.setDensity(lx, lly, lz, (byte) 127);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
// Chunk oberhalb des Ankers: nur weiter bauen wenn der Chunk darunter
|
||||
// an seiner Oberkante (ly=SIZE-1) bereits solid ist.
|
||||
// Das erlaubt Voxeln aus dem Unterwasserbereich (aCy=-1) in cy=0
|
||||
// hineinzuwachsen, sobald cy=-1 bis world Y=0 aufgefüllt ist.
|
||||
VoxelChunk below = chunks.get(chunkKey(cx, cy - 1, cz));
|
||||
if (below == null || below.getDensity(lx, VoxelChunk.SIZE - 1, lz) <= 0) {
|
||||
continue;
|
||||
}
|
||||
currentTop = 0;
|
||||
}
|
||||
}
|
||||
int newTop = Math.min(VoxelChunk.SIZE - 1, currentTop + colStep);
|
||||
@@ -1010,8 +1028,10 @@ public class VoxelEditorState extends BaseAppState {
|
||||
if (chunk.getDensity(lx, ly, lz) > 0) { currentTop = ly; break; }
|
||||
}
|
||||
if (currentTop < 0) continue; // Nichts zu entfernen
|
||||
int newTop = Math.max(0, currentTop - colStep);
|
||||
for (int ly = newTop + 1; ly <= currentTop; ly++) {
|
||||
// newTop darf negativ werden: dann werden ALLE Voxel inkl. ly=0 entfernt.
|
||||
// Math.max(0,...) würde den untersten Voxel bei Y=0 permanent einfrieren.
|
||||
int newTop = currentTop - colStep;
|
||||
for (int ly = Math.max(0, newTop + 1); ly <= currentTop; ly++) {
|
||||
chunk.setDensity(lx, ly, lz, Byte.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
@@ -1474,14 +1494,29 @@ public class VoxelEditorState extends BaseAppState {
|
||||
if (currentTopLY >= 0) {
|
||||
startLY = currentTopLY;
|
||||
} else {
|
||||
// Terrain-Oberfläche als Ankerpunkt
|
||||
int tCy = VoxelChunk.worldYToCy(th);
|
||||
if (cy == tCy) {
|
||||
// Terrain-Chunk: schrittweise von der Oberfläche nach oben wachsen lassen.
|
||||
startLY = Math.max(0, Math.min(VoxelChunk.SIZE - 1,
|
||||
(int)(th - cy * (float) VoxelChunk.CELLS)));
|
||||
} else {
|
||||
// Chunk ohne bestehende Voxel und nicht der Terrain-Chunk: überspringen.
|
||||
} else if (cy < tCy) {
|
||||
// Chunk unterhalb der Terrain-Oberfläche: sofort komplett auffüllen
|
||||
// (verhindert flache Schnitt-Kante bei Y=0 / Wasserrand).
|
||||
float chunkBaseY = cy * (float) VoxelChunk.CELLS;
|
||||
if (chunkBaseY + VoxelChunk.SIZE < -10f) continue; // unter Fundament-Limit
|
||||
int yFloor = Math.max(0, (int) Math.ceil(-10f - chunkBaseY));
|
||||
for (int ly = yFloor; ly < VoxelChunk.SIZE; ly++) {
|
||||
chunk.setDensity(lx, ly, lz, (byte) 127);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
// Chunk über der Terrain-Oberfläche (cy > tCy): weiter bauen
|
||||
// wenn der Chunk darunter an seiner Oberkante solid ist.
|
||||
VoxelChunk below = chunks.get(chunkKey(cx, cy - 1, cz));
|
||||
if (below == null || below.getDensity(lx, VoxelChunk.SIZE - 1, lz) <= 0) {
|
||||
continue;
|
||||
}
|
||||
startLY = 0;
|
||||
}
|
||||
}
|
||||
int newTop = Math.min(startLY + step, targetLY); // nie über Ziel-Zelle
|
||||
@@ -1549,10 +1584,21 @@ public class VoxelEditorState extends BaseAppState {
|
||||
float th = terrainH(wx, wz);
|
||||
if (!Float.isFinite(th)) continue;
|
||||
int thCy = VoxelChunk.worldYToCy(th);
|
||||
if (cy != thCy) continue; // Terrain-Oberfläche in anderem Chunk
|
||||
if (cy == thCy) {
|
||||
currentTopWY = th;
|
||||
startLY = Math.max(0, Math.min(VoxelChunk.SIZE - 1,
|
||||
(int)(th - cy * (float) VoxelChunk.CELLS)));
|
||||
} else if (cy > thCy) {
|
||||
// Chunk über dem Terrain: weiter bauen wenn Chunk darunter an Oberkante solid.
|
||||
VoxelChunk below = chunks.get(chunkKey(cx, cy - 1, cz));
|
||||
if (below == null || below.getDensity(lx, VoxelChunk.SIZE - 1, lz) <= 0) continue;
|
||||
// ly=0 immer solid setzen – step könnte 0 sein und die Lücke bei Y=0 offen lassen.
|
||||
chunk.setDensity(lx, 0, lz, (byte) 127);
|
||||
startLY = 0;
|
||||
currentTopWY = VoxelChunk.toWorldY(cy, 0);
|
||||
} else {
|
||||
continue; // cy < thCy: underground, kein Bedarf für Smooth
|
||||
}
|
||||
}
|
||||
|
||||
int step = (int) Math.round((avgH - currentTopWY) * blend);
|
||||
@@ -1662,10 +1708,21 @@ public class VoxelEditorState extends BaseAppState {
|
||||
float th = terrainH(wx, wz);
|
||||
if (!Float.isFinite(th)) continue;
|
||||
int thCy = VoxelChunk.worldYToCy(th);
|
||||
if (cy != thCy) continue;
|
||||
if (cy == thCy) {
|
||||
currentTopWY = th;
|
||||
startLY = Math.max(0, Math.min(VoxelChunk.SIZE - 1,
|
||||
(int)(th - cy * (float) VoxelChunk.CELLS)));
|
||||
} else if (cy > thCy) {
|
||||
// Chunk über dem Terrain: weiter bauen wenn Chunk darunter an Oberkante solid.
|
||||
VoxelChunk below = chunks.get(chunkKey(cx, cy - 1, cz));
|
||||
if (below == null || below.getDensity(lx, VoxelChunk.SIZE - 1, lz) <= 0) continue;
|
||||
// ly=0 immer solid setzen – step könnte 0 sein und die Lücke bei Y=0 offen lassen.
|
||||
chunk.setDensity(lx, 0, lz, (byte) 127);
|
||||
startLY = 0;
|
||||
currentTopWY = VoxelChunk.toWorldY(cy, 0);
|
||||
} else {
|
||||
continue; // cy < thCy: underground, kein Bedarf für Slope
|
||||
}
|
||||
}
|
||||
|
||||
int step = (int) Math.round((targetH - currentTopWY) * blend);
|
||||
|
||||
46
blight-fbx-test/build.gradle
Normal file
@@ -0,0 +1,46 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
ext {
|
||||
jmeVersion = '3.9.0-stable'
|
||||
mainClassName = 'de.blight.fbxtest.FbxTestApp'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jmonkeyengine:jme3-core:${jmeVersion}"
|
||||
implementation "org.jmonkeyengine:jme3-desktop:${jmeVersion}"
|
||||
implementation "org.jmonkeyengine:jme3-lwjgl3:${jmeVersion}"
|
||||
implementation "org.jmonkeyengine:jme3-plugins:${jmeVersion}"
|
||||
implementation 'com.github.stephengold:MonkeyWrench:1.0.0'
|
||||
runtimeOnly 'ch.qos.logback:logback-classic:1.5.18'
|
||||
implementation 'org.slf4j:slf4j-api:2.0.17'
|
||||
}
|
||||
|
||||
tasks.register('extractNatives', Copy) {
|
||||
def nativeConf = configurations.runtimeClasspath.resolvedConfiguration
|
||||
.resolvedArtifacts
|
||||
.findAll { it.name.contains('natives') }
|
||||
.collect { zipTree(it.file) }
|
||||
from nativeConf
|
||||
into "${buildDir}/natives"
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
tasks.register('run', JavaExec) {
|
||||
group = 'application'
|
||||
description = 'Startet den FBX-Tester'
|
||||
mainClass = mainClassName
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(26)
|
||||
}
|
||||
jvmArgs = [
|
||||
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
|
||||
'--add-opens', 'java.desktop/sun.awt=ALL-UNNAMED',
|
||||
'--enable-native-access=ALL-UNNAMED',
|
||||
"-Djava.library.path=${buildDir}/natives",
|
||||
]
|
||||
dependsOn extractNatives
|
||||
workingDir = rootDir
|
||||
}
|
||||
1152
blight-fbx-test/src/main/java/de/blight/fbxtest/FbxTestApp.java
Normal file
12
blight-fbx-test/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<configuration>
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss} %-5level [%logger{20}] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
<logger name="com.jme3" level="WARN"/>
|
||||
<logger name="com.github.stephengold" level="INFO"/>
|
||||
</configuration>
|
||||
@@ -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
|
||||
// Reihe unten – Ecken fix, untere Kante Mirror-X
|
||||
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));
|
||||
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
|
||||
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 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,
|
||||
|
||||
BIN
blight-map/src/main/map/chunks/voxel_17_0_08.blvc
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_0_09.blvc
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_m1_09.blvc
Normal file
@@ -7,3 +7,4 @@ include 'blight-lang'
|
||||
include 'blight-editor'
|
||||
include 'blight-game'
|
||||
include 'blight-vegetation-generator'
|
||||
include 'blight-fbx-test'
|
||||
|
||||