Weiter an der Karte gearbeitet - hier Höhlen und Tunnel

This commit is contained in:
2026-09-06 11:41:04 +02:00
parent ca4d34b806
commit faa0a8b254
18 changed files with 1192 additions and 63 deletions

View File

@@ -150,6 +150,13 @@ public class MinimapState extends BaseAppState {
private Geometry fogFullGeo;
private Geometry playerDotFull;
// ── Höhlen-Karte ──────────────────────────────────────────────────────────
private Geometry caveGeoMini = null; // Höhlenkarte über Minimap
private Geometry caveGeoFull = null; // Höhlenkarte über Vollkarte
private float caveBlend = 0f; // 0 = Außen, 1 = Höhle
/** upperHole-Maske der geladenen Karte für Laufzeit-Erkennung; null = nicht verfügbar. */
private byte[] upperHoleMask = null;
private float fmMapSize;
private float fmMapOriginX;
private float fmMapOriginY;
@@ -212,8 +219,12 @@ public class MinimapState extends BaseAppState {
}
float px = playerNode.getWorldTranslation().x;
float py = playerNode.getWorldTranslation().y;
float pz = playerNode.getWorldTranslation().z;
// Höhlen-Erkennung: upperHole-Zelle am Spieler-XZ
tickCaveBlend(px, py, pz, tpf);
// Erkundung markieren + Nebel animieren
tickExploration(px, pz, tpf);
fogTime += tpf;
@@ -267,6 +278,22 @@ public class MinimapState extends BaseAppState {
}
}
private void tickCaveBlend(float px, float py, float pz, float tpf) {
if (upperHoleMask == null || caveGeoMini == null) return;
int C = MapData.UPPER_CELLS;
int cx = Math.max(0, Math.min(C-1, (int)((px + WORLD_HALF) / WORLD_SIZE * C)));
int cz = Math.max(0, Math.min(C-1, (int)((pz + WORLD_HALF) / WORLD_SIZE * C)));
boolean inHole = upperHoleMask[cz * C + cx] != 0;
float target = inHole ? 1f : 0f;
if (caveBlend != target) {
float speed = 1.5f; // blend completes in ~0.67s
caveBlend += (target - caveBlend) * Math.min(tpf * speed, 1f);
if (Math.abs(caveBlend - target) < 0.005f) caveBlend = target;
ColorRGBA col = new ColorRGBA(1f, 1f, 1f, caveBlend);
caveGeoMini.getMaterial().setColor("Color", col);
}
}
private static float distSq(float x1, float z1, float x2, float z2) {
float dx = x2 - x1, dz = z2 - z1;
return dx * dx + dz * dz;
@@ -551,10 +578,11 @@ public class MinimapState extends BaseAppState {
List<PlacedWaterfall> waterfalls = WaterfallIO.load();
int[] slotColors = computeSlotColors(mapData, root);
int[][] tilePixels = WorldMapRenderer.loadMapTiles(root);
float[] voxelSurface = WorldMapRenderer.bakeVoxelSurface(mapData);
renderInput = new RenderInput(mapData, areas, zones, locs, waters, models, slotColors);
de.blight.common.map.WorldMapRenderModel renderModel = new de.blight.common.map.WorldMapRenderModel(
mapData, areas, zones, locs, waters, models, waterfalls,
slotColors, null, null, null, null, null, tilePixels, null);
slotColors, null, voxelSurface, null, null, null, tilePixels, null);
log.debug("[Minimap] Render-Modell bereit – {} Areas, {} Orte, {} Wasser ({} ms)",
areas.size(), locs.size(), waters.size(), System.currentTimeMillis() - t0);
@@ -565,6 +593,13 @@ public class MinimapState extends BaseAppState {
log.debug("[Minimap] Weltkarte gerendert und gespeichert ({} ms): {}",
System.currentTimeMillis() - t1, png);
BufferedImage caveBi = WorldMapRenderer.buildCaveMap(mapData, TEXTURE_SIZE);
ImageIO.write(caveBi, "PNG", dir.resolve("minimap_cave.png").toFile());
// Verwende Voxel-basierten Tunnel-Maske für Laufzeit-Erkennung (UPPER_CELLS Auflösung)
boolean[] tunnelMask256 = WorldMapRenderer.buildCaveTunnelMask(mapData, MapData.UPPER_CELLS);
upperHoleMask = new byte[tunnelMask256.length];
for (int i = 0; i < tunnelMask256.length; i++) upperHoleMask[i] = tunnelMask256[i] ? (byte)1 : 0;
app.enqueue(() -> { onTextureReady(); return null; });
} catch (Exception e) {
log.error("[Minimap] Render-Fehler: {}", e.getMessage(), e);
@@ -621,10 +656,16 @@ public class MinimapState extends BaseAppState {
MINIMAP_SIZE / 2f - DOT_SIZE / 2f,
MINIMAP_SIZE / 2f - DOT_SIZE / 2f, 2f);
caveGeoMini = new Geometry("minimap_cave", minimapMesh);
caveGeoMini.setMaterial(buildCaveTexMat());
caveGeoMini.setQueueBucket(RenderQueue.Bucket.Gui);
caveGeoMini.setLocalTranslation(0f, 0f, 0.5f); // between world map and fog
minimapNode = new Node("minimap");
minimapNode.setLocalTranslation(x, y, 48f);
minimapNode.attachChild(border);
minimapNode.attachChild(minimapGeo);
minimapNode.attachChild(caveGeoMini);
minimapNode.attachChild(fogMiniGeo);
minimapNode.attachChild(playerDotMini);
@@ -634,6 +675,21 @@ public class MinimapState extends BaseAppState {
}
}
/** Erstellt das Material für die Höhlenkarte mit Alpha-Blending (initial unsichtbar). */
private Material buildCaveTexMat() {
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
try {
Texture tex = app.getAssetManager().loadTexture("Textures/hud/minimap_cave.png");
tex.setWrap(Texture.WrapMode.EdgeClamp);
mat.setTexture("ColorMap", tex);
} catch (Exception e) {
log.warn("[Minimap] Höhlenkarten-Textur nicht ladbar");
}
mat.setColor("Color", new ColorRGBA(1f, 1f, 1f, 0f)); // zunächst unsichtbar
return mat;
}
// ── Vektor-Layer ──────────────────────────────────────────────────────────
private void scheduleVectorRedraw() {