Anpassung der KArte
This commit is contained in:
@@ -1228,17 +1228,17 @@ public final class WorldMapRenderer {
|
|||||||
int[] tileAvg = computeTileAvgColors(tilePixels);
|
int[] tileAvg = computeTileAvgColors(tilePixels);
|
||||||
int[] slotTypes = computeSlotTileTypes(m, slotColors);
|
int[] slotTypes = computeSlotTileTypes(m, slotColors);
|
||||||
float cellW = WORLD_SIZE / (SIZE - 1);
|
float cellW = WORLD_SIZE / (SIZE - 1);
|
||||||
int[] result = new int[SIZE * SIZE];
|
int[] terrainRGB = new int[SIZE * SIZE];
|
||||||
|
float[] cliffMask = new float[SIZE * SIZE];
|
||||||
for (int py = 0; py < SIZE; py++) {
|
for (int py = 0; py < SIZE; py++) {
|
||||||
float wz = -WORLD_HALF + py * cellW;
|
float wz = -WORLD_HALF + py * cellW;
|
||||||
for (int px = 0; px < SIZE; px++) {
|
for (int px = 0; px < SIZE; px++) {
|
||||||
float wx = -WORLD_HALF + px * cellW;
|
float wx = -WORLD_HALF + px * cellW;
|
||||||
int idx = py * SIZE + px;
|
int idx = py * SIZE + px;
|
||||||
float h = heights[idx];
|
float h = heights[idx];
|
||||||
int rgb;
|
|
||||||
if (h < 0f || (waterH[idx] > 0f && waterH[idx] > h)) {
|
if (h < 0f || (waterH[idx] > 0f && waterH[idx] > h)) {
|
||||||
int[] wc = TILE_FALLBACK_RGB[TILE_WATER];
|
int[] wc = TILE_FALLBACK_RGB[TILE_WATER];
|
||||||
rgb = (wc[0]<<16)|(wc[1]<<8)|wc[2];
|
terrainRGB[idx] = (wc[0]<<16)|(wc[1]<<8)|wc[2];
|
||||||
} else {
|
} else {
|
||||||
int dPx = Math.max(1, (int)(1.5f / cellW));
|
int dPx = Math.max(1, (int)(1.5f / cellW));
|
||||||
float hR = heights[py * SIZE + Math.min(SIZE-1, px+dPx)];
|
float hR = heights[py * SIZE + Math.min(SIZE-1, px+dPx)];
|
||||||
@@ -1248,14 +1248,23 @@ public final class WorldMapRenderer {
|
|||||||
float dist = dPx * cellW;
|
float dist = dPx * cellW;
|
||||||
double slope = Math.toDegrees(Math.atan(Math.sqrt(
|
double slope = Math.toDegrees(Math.atan(Math.sqrt(
|
||||||
Math.pow((hR-hL)/(2*dist),2) + Math.pow((hD-hU)/(2*dist),2))));
|
Math.pow((hR-hL)/(2*dist),2) + Math.pow((hD-hU)/(2*dist),2))));
|
||||||
if (slope > 30.0) {
|
cliffMask[idx] = (slope > 30.0) ? 1f : 0f;
|
||||||
rgb = tileAvg[Math.min(TILE_CLIFF, tileAvg.length-1)];
|
terrainRGB[idx] = blendTileAvgColors(m, SS, wx, wz, tileAvg, slotTypes);
|
||||||
} else {
|
|
||||||
rgb = blendTileAvgColors(m, SS, wx, wz, tileAvg, slotTypes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[idx] = rgb;
|
|
||||||
}
|
}
|
||||||
|
// Cliff-Maske weichzeichnen → weiche Klippen-Kanten ohne Änderung der Schwellwert-Logik
|
||||||
|
float[] softMask = boxBlurFloat(cliffMask, SIZE, 3);
|
||||||
|
int cliffRgb = tileAvg[Math.min(TILE_CLIFF, tileAvg.length-1)];
|
||||||
|
int cr = (cliffRgb>>16)&0xFF, cg = (cliffRgb>>8)&0xFF, cb = cliffRgb&0xFF;
|
||||||
|
int[] result = new int[SIZE * SIZE];
|
||||||
|
for (int i = 0; i < SIZE * SIZE; i++) {
|
||||||
|
float t = softMask[i];
|
||||||
|
int tc = terrainRGB[i];
|
||||||
|
int r = (tc>>16)&0xFF, g = (tc>>8)&0xFF, b = tc&0xFF;
|
||||||
|
result[i] = (clamp((int)(r + (cr-r)*t)) << 16)
|
||||||
|
| (clamp((int)(g + (cg-g)*t)) << 8)
|
||||||
|
| clamp((int)(b + (cb-b)*t));
|
||||||
}
|
}
|
||||||
return boxBlur(result, SIZE, 2);
|
return boxBlur(result, SIZE, 2);
|
||||||
}
|
}
|
||||||
@@ -1295,38 +1304,67 @@ public final class WorldMapRenderer {
|
|||||||
float cellW = WORLD_SIZE / (SIZE - 1);
|
float cellW = WORLD_SIZE / (SIZE - 1);
|
||||||
int written = 0;
|
int written = 0;
|
||||||
|
|
||||||
|
final int CLIFF_BLUR_R = 4; // Blur-Radius für weiche Klippen-Kanten
|
||||||
|
final int EXT = CLIFF_BLUR_R;
|
||||||
|
final int EXTW = CS + 2 * EXT; // erweitertes Fenster inkl. Rand für nahtlosen Blur
|
||||||
|
int dPxHires = Math.max(1, (int)(1.5f / cellW));
|
||||||
|
|
||||||
for (int cy = 0; cy < COLS; cy++) {
|
for (int cy = 0; cy < COLS; cy++) {
|
||||||
for (int cx = 0; cx < COLS; cx++) {
|
for (int cx = 0; cx < COLS; cx++) {
|
||||||
java.nio.file.Path p = chunkPath(cacheDir, cx, cy);
|
java.nio.file.Path p = chunkPath(cacheDir, cx, cy);
|
||||||
if (!forceRegen && Files.exists(p)) continue;
|
if (!forceRegen && Files.exists(p)) continue;
|
||||||
// Chunk-Pixel berechnen
|
|
||||||
|
// Erweiterte Cliff-Maske + Terrain-Farben (mit Rand über Chunk-Grenze)
|
||||||
|
float[] cliffMaskExt = new float[EXTW * EXTW];
|
||||||
|
boolean[] isWaterExt = new boolean[EXTW * EXTW];
|
||||||
|
int[] terrainColExt = new int[EXTW * EXTW];
|
||||||
|
for (int ely = 0; ely < EXTW; ely++) {
|
||||||
|
int gy = cy * CS + ely - EXT;
|
||||||
|
int gyC = Math.max(0, Math.min(SIZE-1, gy));
|
||||||
|
float wz = -WORLD_HALF + gyC * cellW;
|
||||||
|
for (int elx = 0; elx < EXTW; elx++) {
|
||||||
|
int gx = cx * CS + elx - EXT;
|
||||||
|
int gxC = Math.max(0, Math.min(SIZE-1, gx));
|
||||||
|
float wx = -WORLD_HALF + gxC * cellW;
|
||||||
|
int eidx = ely * EXTW + elx;
|
||||||
|
int hidx = gyC * SIZE + gxC;
|
||||||
|
float h = heights[hidx];
|
||||||
|
if (h < 0f || (waterH[hidx] > 0f && waterH[hidx] > h)) {
|
||||||
|
isWaterExt[eidx] = true;
|
||||||
|
} else {
|
||||||
|
float hR = heights[gyC * SIZE + Math.min(SIZE-1, gxC+dPxHires)];
|
||||||
|
float hL = heights[gyC * SIZE + Math.max(0, gxC-dPxHires)];
|
||||||
|
float hD = heights[Math.min(SIZE-1, gyC+dPxHires) * SIZE + gxC];
|
||||||
|
float hU = heights[Math.max(0, gyC-dPxHires) * SIZE + gxC];
|
||||||
|
float dist = dPxHires * cellW;
|
||||||
|
double slope = Math.toDegrees(Math.atan(Math.sqrt(
|
||||||
|
Math.pow((hR-hL)/(2*dist),2) + Math.pow((hD-hU)/(2*dist),2))));
|
||||||
|
cliffMaskExt[eidx] = (slope > 30.0) ? 1f : 0f;
|
||||||
|
terrainColExt[eidx] = blendTileAvgColors(m, SS, wx, wz, tileAvg, slotTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float[] softMaskExt = boxBlurFloat(cliffMaskExt, EXTW, CLIFF_BLUR_R);
|
||||||
|
int cliffRgb = tileAvg[Math.min(TILE_CLIFF, tileAvg.length-1)];
|
||||||
|
int cr = (cliffRgb>>16)&0xFF, cg = (cliffRgb>>8)&0xFF, cb = cliffRgb&0xFF;
|
||||||
|
|
||||||
|
// Chunk-Pixel aus dem inneren Bereich der erweiterten Maske zusammensetzen
|
||||||
int[] chunk = new int[CS * CS];
|
int[] chunk = new int[CS * CS];
|
||||||
for (int ly = 0; ly < CS; ly++) {
|
for (int ly = 0; ly < CS; ly++) {
|
||||||
int gy = cy * CS + ly; // globaler Y-Index in der HIRES-Karte
|
|
||||||
float wz = -WORLD_HALF + gy * cellW;
|
|
||||||
for (int lx = 0; lx < CS; lx++) {
|
for (int lx = 0; lx < CS; lx++) {
|
||||||
int gx = cx * CS + lx;
|
int extIdx = (ly + EXT) * EXTW + (lx + EXT);
|
||||||
float wx = -WORLD_HALF + gx * cellW;
|
|
||||||
int idx = gy * SIZE + gx;
|
|
||||||
float h = heights[idx];
|
|
||||||
int tr, tg, tb;
|
int tr, tg, tb;
|
||||||
if (h < 0f || (waterH[idx] > 0f && waterH[idx] > h)) {
|
if (isWaterExt[extIdx]) {
|
||||||
tr = (WATER_COLOR >> 16) & 0xFF;
|
tr = (WATER_COLOR >> 16) & 0xFF;
|
||||||
tg = (WATER_COLOR >> 8) & 0xFF;
|
tg = (WATER_COLOR >> 8) & 0xFF;
|
||||||
tb = WATER_COLOR & 0xFF;
|
tb = WATER_COLOR & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
int dPx = Math.max(1, (int)(1.5f / cellW));
|
float t = softMaskExt[extIdx];
|
||||||
float hR = heights[gy * SIZE + Math.min(SIZE-1, gx+dPx)];
|
int tc = terrainColExt[extIdx];
|
||||||
float hL = heights[gy * SIZE + Math.max(0, gx-dPx)];
|
int r2 = (tc>>16)&0xFF, g2 = (tc>>8)&0xFF, b2 = tc&0xFF;
|
||||||
float hD = heights[Math.min(SIZE-1, gy+dPx) * SIZE + gx];
|
tr = clamp((int)(r2 + (cr-r2)*t));
|
||||||
float hU = heights[Math.max(0, gy-dPx) * SIZE + gx];
|
tg = clamp((int)(g2 + (cg-g2)*t));
|
||||||
float dist = dPx * cellW;
|
tb = clamp((int)(b2 + (cb-b2)*t));
|
||||||
double slope = Math.toDegrees(Math.atan(Math.sqrt(
|
|
||||||
Math.pow((hR-hL)/(2*dist),2) + Math.pow((hD-hU)/(2*dist),2))));
|
|
||||||
int rgb = (slope > 30.0)
|
|
||||||
? tileAvg[Math.min(TILE_CLIFF, tileAvg.length-1)]
|
|
||||||
: blendTileAvgColors(m, SS, wx, wz, tileAvg, slotTypes);
|
|
||||||
tr = (rgb>>16)&0xFF; tg = (rgb>>8)&0xFF; tb = rgb&0xFF;
|
|
||||||
}
|
}
|
||||||
chunk[ly * CS + lx] = (0xFF << 24) | (tr << 16) | (tg << 8) | tb;
|
chunk[ly * CS + lx] = (0xFF << 24) | (tr << 16) | (tg << 8) | tb;
|
||||||
}
|
}
|
||||||
@@ -1360,6 +1398,32 @@ public final class WorldMapRenderer {
|
|||||||
return dir.resolve(String.format("chunk_%02x_%02x.png", cx, cy));
|
return dir.resolve(String.format("chunk_%02x_%02x.png", cx, cy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Separabler Box-Blur auf float-Array (horizontal + vertikal), Radius in Pixeln. */
|
||||||
|
private static float[] boxBlurFloat(float[] src, int size, int radius) {
|
||||||
|
float[] tmp = new float[size * size];
|
||||||
|
float[] dst = new float[size * size];
|
||||||
|
float inv = 1f / (radius * 2 + 1);
|
||||||
|
for (int py = 0; py < size; py++) {
|
||||||
|
for (int px = 0; px < size; px++) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int dx = -radius; dx <= radius; dx++) {
|
||||||
|
sum += src[py * size + Math.max(0, Math.min(size-1, px+dx))];
|
||||||
|
}
|
||||||
|
tmp[py * size + px] = sum * inv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int py = 0; py < size; py++) {
|
||||||
|
for (int px = 0; px < size; px++) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int dz = -radius; dz <= radius; dz++) {
|
||||||
|
sum += tmp[Math.max(0, Math.min(size-1, py+dz)) * size + px];
|
||||||
|
}
|
||||||
|
dst[py * size + px] = sum * inv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
/** Separabler Box-Blur (horizontal + vertikal), Radius in Pixeln. */
|
/** Separabler Box-Blur (horizontal + vertikal), Radius in Pixeln. */
|
||||||
private static int[] boxBlur(int[] src, int size, int radius) {
|
private static int[] boxBlur(int[] src, int size, int radius) {
|
||||||
int[] tmp = new int[size * size];
|
int[] tmp = new int[size * size];
|
||||||
|
|||||||
Reference in New Issue
Block a user