Commit vor dem Einfügen von Gebierge über Voxels

This commit is contained in:
2026-06-10 12:56:29 +02:00
parent edb9cfc946
commit fe5dfc19b1
75 changed files with 80 additions and 16 deletions

View File

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 21:42:20 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=manatrank
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.2
scaleY=0.2
scaleZ=0.2
solid=false
tags=
uniformScale=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@@ -3549,6 +3549,7 @@ public class EditorApp extends Application {
Files.deleteIfExists(p);
item.getParent().getChildren().remove(item);
itemPaths.remove(item);
if (pStr.endsWith(".j3o")) deleteJ3oSideFiles(p);
setStatus("Gelöscht: " + p.getFileName());
} catch (IOException ex) {
setStatus("Fehler: " + ex.getMessage());
@@ -3837,6 +3838,32 @@ public class EditorApp extends Application {
} catch (IOException ignored) {}
}
/**
* Löscht Thumbnail und Impostor-Textur, die zu einer .j3o-Datei gehören.
* Impostor-Dateien werden anhand des Zeitstempel-Suffixes (_YYYYMMDD_HHMMSS) ermittelt.
*/
private void deleteJ3oSideFiles(Path j3oPath) {
// Thumbnail
try {
Files.deleteIfExists(
de.blight.editor.state.ThumbnailRenderer.sidecarPath(j3oPath, ASSET_ROOT));
} catch (IOException ignored) {}
// Impostor: Zeitstempel aus Dateiname extrahieren und passende Datei suchen
String base = j3oPath.getFileName().toString().replace(".j3o", "");
java.util.regex.Matcher m = java.util.regex.Pattern
.compile(".*(\\d{8}_\\d{6})$").matcher(base);
if (!m.matches()) return;
String ts = m.group(1);
Path impostorDir = ASSET_ROOT.resolve(
de.blight.editor.state.ThumbnailRenderer.IMPOSTOR_DIR);
if (!Files.isDirectory(impostorDir)) return;
try (var stream = Files.list(impostorDir)) {
stream.filter(f -> f.getFileName().toString().endsWith("_" + ts + ".png"))
.forEach(f -> { try { Files.deleteIfExists(f); } catch (IOException ignored) {} });
} catch (IOException ignored) {}
}
/**
* Befüllt den Asset-Baum vollständig aus dem Dateisystem.
* Bekannte Verzeichnisse werden den Kategorie-Feldern zugewiesen;
@@ -3851,6 +3878,11 @@ public class EditorApp extends Application {
List<Path> topDirs;
try (var s = Files.list(ASSET_ROOT)) {
topDirs = s.filter(Files::isDirectory)
.filter(p -> {
String n = p.getFileName().toString();
return !n.equals(de.blight.editor.state.ThumbnailRenderer.THUMB_DIR)
&& !n.equals(de.blight.editor.state.ThumbnailRenderer.IMPOSTOR_DIR);
})
.sorted(Comparator.comparing(p -> p.getFileName().toString().toLowerCase()))
.collect(java.util.stream.Collectors.toList());
} catch (IOException e) { topDirs = List.of(); }

View File

@@ -599,12 +599,12 @@ public class EzTreeState extends BaseAppState {
img.setRGB(x, height - 1 - y, (a<<24)|(r<<16)|(g<<8)|b);
}
}
Path texDir = ASSET_ROOT.resolve("Textures").resolve("impostor");
Path texDir = ASSET_ROOT.resolve(ThumbnailRenderer.IMPOSTOR_DIR);
Files.createDirectories(texDir);
File pngFile = texDir.resolve(name + ".png").toFile();
ImageIO.write(img, "PNG", pngFile);
try {
return (Texture2D) assets.loadTexture("Textures/impostor/" + name + ".png");
return (Texture2D) assets.loadTexture(ThumbnailRenderer.IMPOSTOR_DIR + "/" + name + ".png");
} catch (Exception ignored) {
pixels.rewind();
Image jmeImg = new Image(Image.Format.RGBA8, width, height, pixels, null,

View File

@@ -28,6 +28,9 @@ import java.nio.file.Path;
*/
public class ModelEditorState extends BaseAppState {
private static final java.nio.file.Path ASSET_ROOT =
de.blight.editor.ProjectRoot.resolve("blight-assets", "src", "main", "resources");
// ── Orbit-Kamera ─────────────────────────────────────────────────────────
private static final float ORBIT_SENS = 0.4f; // Grad / Pixel Maus-Delta
private static final float ZOOM_FACTOR = 0.1f;
@@ -421,7 +424,7 @@ public class ModelEditorState extends BaseAppState {
Spatial modelClone = modelWrapper.clone();
byte[] thumb = ThumbnailRenderer.render(modelClone, app.getRenderManager(), app.getRenderer());
if (thumb == null) return;
ThumbnailRenderer.saveSidecar(thumb, j3oPath);
ThumbnailRenderer.saveSidecar(thumb, j3oPath, ASSET_ROOT);
embedThumbnail(thumb, j3oPath);
} catch (Exception e) {
System.err.println("[ModelEditor] Thumbnail-Fehler: " + e.getMessage());

View File

@@ -1250,7 +1250,7 @@ public class SceneObjectState extends BaseAppState {
ThumbnailRenderer.embed(model, thumb);
Files.createDirectories(req.destJ3o().getParent());
BinaryExporter.getInstance().save(model, req.destJ3o().toFile());
ThumbnailRenderer.saveSidecar(thumb, req.destJ3o());
ThumbnailRenderer.saveSidecar(thumb, req.destJ3o(), ASSET_ROOT);
} else {
Files.createDirectories(req.destJ3o().getParent());
BinaryExporter.getInstance().save(model, req.destJ3o().toFile());

View File

@@ -128,19 +128,28 @@ public final class ThumbnailRenderer {
public static String getUserDataKey() { return USERDATA_KEY; }
/** Speichert PNG-Bytes als {@code <j3oPath>.thumb.png}. */
public static void saveSidecar(byte[] pngBytes, Path j3oPath) {
/** Name des zentralen Thumbnail-Verzeichnisses (direkt unter assetRoot). */
public static final String THUMB_DIR = ".thumbnails";
/** Name des zentralen Impostor-Textur-Verzeichnisses (direkt unter assetRoot). */
public static final String IMPOSTOR_DIR = ".impostors";
/** Speichert PNG-Bytes ins zentrale Thumbnail-Verzeichnis ({@code assetRoot/.thumbnails/…}). */
public static void saveSidecar(byte[] pngBytes, Path j3oPath, Path assetRoot) {
if (pngBytes == null) return;
try {
Files.write(sidecarPath(j3oPath), pngBytes);
Path dest = sidecarPath(j3oPath, assetRoot);
Files.createDirectories(dest.getParent());
Files.write(dest, pngBytes);
} catch (IOException e) {
System.err.println("[ThumbnailRenderer] Sidecar-Fehler: " + e.getMessage());
}
}
/** Pfad der Sidecar-Datei: {@code <j3oPath>.thumb.png}. */
public static Path sidecarPath(Path j3oPath) {
return j3oPath.resolveSibling(j3oPath.getFileName() + ".thumb.png");
/** Pfad der Thumbnail-Datei: {@code assetRoot/.thumbnails/<relative-j3o-pfad>.thumb.png}. */
public static Path sidecarPath(Path j3oPath, Path assetRoot) {
Path rel = assetRoot.relativize(j3oPath);
return assetRoot.resolve(THUMB_DIR).resolve(rel.toString() + ".thumb.png");
}
// ── Intern ───────────────────────────────────────────────────────────────

View File

@@ -593,14 +593,14 @@ public class TreeGeneratorState extends BaseAppState {
img.setRGB(x, height - 1 - y, (a<<24)|(r<<16)|(g<<8)|b);
}
}
Path texDir = ASSET_ROOT.resolve("Textures").resolve("impostor");
Path texDir = ASSET_ROOT.resolve(ThumbnailRenderer.IMPOSTOR_DIR);
Files.createDirectories(texDir);
File pngFile = texDir.resolve(name + ".png").toFile();
ImageIO.write(img, "PNG", pngFile);
log.info("[Blight-Baum] Impostor: {}", pngFile.getAbsolutePath());
try {
return (Texture2D) assets.loadTexture("Textures/impostor/" + name + ".png");
return (Texture2D) assets.loadTexture(ThumbnailRenderer.IMPOSTOR_DIR + "/" + name + ".png");
} catch (Exception loadEx) {
pixels.rewind();
Image jmeImg = new Image(Image.Format.RGBA8, width, height,

View File

@@ -8,7 +8,7 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Modality;
import java.io.ByteArrayInputStream;
import de.blight.editor.state.ThumbnailRenderer;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
@@ -18,7 +18,7 @@ import java.util.stream.Stream;
/**
* Dialog zum Auswählen eines .j3o-Modells.
* Zeigt Karten mit 128×128-Thumbnail wenn eine {@code .j3o.thumb.png} Sidecar-Datei vorhanden ist.
* Zeigt Karten mit 128×128-Thumbnail wenn eine Thumbnail-Datei im zentralen {@code .thumbnails}-Verzeichnis vorhanden ist.
* Gibt den relativen Asset-Pfad (z.B. "Models/Items/sword.j3o") zurück oder {@code null} bei Abbruch.
*/
public class ModelChooser extends Dialog<String> {
@@ -113,8 +113,7 @@ public class ModelChooser extends Dialog<String> {
}
private Image loadThumb(Path j3o) {
// Sidecar: <model.j3o>.thumb.png
Path sidecar = j3o.resolveSibling(j3o.getFileName() + ".thumb.png");
Path sidecar = ThumbnailRenderer.sidecarPath(j3o, assetRoot);
if (Files.isRegularFile(sidecar)) {
try (InputStream is = Files.newInputStream(sidecar)) {
return new Image(is, THUMB_SIZE, THUMB_SIZE, true, true);

View File

@@ -44,3 +44,4 @@ Models/plants/fern/fern_20260608_165628.j3o 162.27063 0.88450 24.03092 0.00000 1
Models/plants/misc/heliconia+plant+3d+model.j3o 152.94803 0.96488 8.99865 0.00000 1.00000 0.00000 0.00000 false true true 30.00000 80.00000 120.00000
Models/plants/misc/heliconia+plant+3d+model.j3o 155.57500 0.96457 10.04861 0.00000 1.00000 0.00000 0.00000 false true true 30.00000 80.00000 120.00000
Models/plants/misc/kaktusfeige.j3o 153.89978 0.98474 38.66549 0.00000 2.50000 0.00000 0.00000 true true true 30.00000 80.00000 120.00000
@plane 102.97000 1.06000 -172.24001 0.00000 1.00000 0.00000 0.00000 false Textures/ground/Rocks015_1K-JPG_Color.jpg Textures/ground/Rocks015_1K-JPG_NormalDX.jpg Common/MatDefs/Light/Lighting.j3md Models/custom_mesh_7.j3o true true 30.00000 80.00000 120.00000