|
|
|
@ -8,185 +8,32 @@
@@ -8,185 +8,32 @@
|
|
|
|
|
|
|
|
|
|
package engine.InterestManagement; |
|
|
|
|
|
|
|
|
|
import engine.Enum; |
|
|
|
|
import engine.gameManager.ConfigManager; |
|
|
|
|
import engine.gameManager.DbManager; |
|
|
|
|
import engine.gameManager.ZoneManager; |
|
|
|
|
import engine.math.Vector2f; |
|
|
|
|
import engine.math.Vector3fImmutable; |
|
|
|
|
import engine.objects.Zone; |
|
|
|
|
import org.pmw.tinylog.Logger; |
|
|
|
|
|
|
|
|
|
import javax.imageio.ImageIO; |
|
|
|
|
import java.awt.*; |
|
|
|
|
import java.awt.image.BufferedImage; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.nio.file.Files; |
|
|
|
|
import java.nio.file.Path; |
|
|
|
|
import java.nio.file.Paths; |
|
|
|
|
import java.sql.ResultSet; |
|
|
|
|
import java.sql.SQLException; |
|
|
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
public class Terrain { |
|
|
|
|
|
|
|
|
|
// Class variables
|
|
|
|
|
|
|
|
|
|
public static final HashMap<Integer, Terrain> heightmapByLoadNum = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
public static final HashMap<Integer, short[][]> _pixelData = new HashMap<>(); |
|
|
|
|
public static final HashMap<Integer, short[][]> _heightmap_pixel_cache = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
// Bootstrap Tracking
|
|
|
|
|
public static int heightMapsCreated = 0; |
|
|
|
|
public static Terrain playerCityTerrain; |
|
|
|
|
|
|
|
|
|
// Heightmap data for this heightmap
|
|
|
|
|
public final int heightMapID; |
|
|
|
|
public final int maxHeight; |
|
|
|
|
public final int fullExtentsX; |
|
|
|
|
public final int fullExtentsY; |
|
|
|
|
public final int zoneLoadID; |
|
|
|
|
public BufferedImage heightmapImage; |
|
|
|
|
public float cell_size_x; |
|
|
|
|
public float cell_size_y; |
|
|
|
|
public float seaLevel = 0; |
|
|
|
|
public short[][] pixelColorValues; |
|
|
|
|
public int cell_count_x; |
|
|
|
|
public int cell_count_y; |
|
|
|
|
public int template; |
|
|
|
|
public short[][] terrain_pixel_data; |
|
|
|
|
|
|
|
|
|
public Vector2f terrain_size = new Vector2f(); |
|
|
|
|
public Vector2f cell_size = new Vector2f(); |
|
|
|
|
public Vector2f cell_count = new Vector2f(); |
|
|
|
|
public float terrain_scale; |
|
|
|
|
|
|
|
|
|
public Terrain(ResultSet rs) throws SQLException { |
|
|
|
|
|
|
|
|
|
this.heightMapID = rs.getInt("heightMapID"); |
|
|
|
|
this.maxHeight = rs.getInt("maxHeight"); |
|
|
|
|
int halfExtentsX = rs.getInt("xRadius"); |
|
|
|
|
int halfExtentsY = rs.getInt("zRadius"); |
|
|
|
|
this.zoneLoadID = rs.getInt("zoneLoadID"); |
|
|
|
|
this.seaLevel = rs.getFloat("seaLevel"); |
|
|
|
|
|
|
|
|
|
// Cache the full extents to avoid the calculation
|
|
|
|
|
|
|
|
|
|
this.fullExtentsX = halfExtentsX * 2; |
|
|
|
|
this.fullExtentsY = halfExtentsY * 2; |
|
|
|
|
|
|
|
|
|
this.heightmapImage = null; |
|
|
|
|
File imageFile = new File(ConfigManager.DEFAULT_DATA_DIR + "heightmaps/" + this.heightMapID + ".bmp"); |
|
|
|
|
|
|
|
|
|
// early exit if no image file was found. Will log in caller.
|
|
|
|
|
|
|
|
|
|
if (!imageFile.exists()) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// load the heightmap image.
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
this.heightmapImage = ImageIO.read(imageFile); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
Logger.error("***Error loading heightmap data for heightmap " + this.heightMapID + e); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Calculate the data we do not load from table
|
|
|
|
|
|
|
|
|
|
this.cell_count_x = this.heightmapImage.getWidth() - 1; |
|
|
|
|
this.cell_size_x = this.fullExtentsX / (float) cell_count_x; |
|
|
|
|
|
|
|
|
|
this.cell_count_y = this.heightmapImage.getHeight() - 1; |
|
|
|
|
this.cell_size_y = this.fullExtentsY / (float) cell_count_y; |
|
|
|
|
|
|
|
|
|
this.terrain_scale = this.maxHeight / 255f; |
|
|
|
|
|
|
|
|
|
// Generate pixel array from image data
|
|
|
|
|
|
|
|
|
|
generatePixelData(this); |
|
|
|
|
|
|
|
|
|
Terrain.heightmapByLoadNum.put(this.zoneLoadID, this); |
|
|
|
|
|
|
|
|
|
heightMapsCreated++; |
|
|
|
|
} |
|
|
|
|
public static int heightMapsCreated; |
|
|
|
|
|
|
|
|
|
//Created for PlayerCities
|
|
|
|
|
public Terrain() { |
|
|
|
|
|
|
|
|
|
this.heightMapID = 999999; |
|
|
|
|
this.maxHeight = 5; // for real...
|
|
|
|
|
int halfExtentsX = (int) Enum.CityBoundsType.ZONE.halfExtents; |
|
|
|
|
int halfExtentsY = (int) Enum.CityBoundsType.ZONE.halfExtents; |
|
|
|
|
this.zoneLoadID = 0; |
|
|
|
|
this.seaLevel = 0; |
|
|
|
|
|
|
|
|
|
// Cache the full extents to avoid the calculation
|
|
|
|
|
|
|
|
|
|
this.fullExtentsX = halfExtentsX * 2; |
|
|
|
|
this.fullExtentsY = halfExtentsY * 2; |
|
|
|
|
|
|
|
|
|
this.heightmapImage = null; |
|
|
|
|
|
|
|
|
|
// Calculate the data we do not load from table
|
|
|
|
|
|
|
|
|
|
this.cell_size_x = halfExtentsX; |
|
|
|
|
this.cell_size_y = halfExtentsY; |
|
|
|
|
|
|
|
|
|
this.pixelColorValues = new short[this.fullExtentsX][this.fullExtentsY]; |
|
|
|
|
|
|
|
|
|
for (int y = 0; y < this.fullExtentsY; y++) { |
|
|
|
|
for (int x = 0; x < this.fullExtentsX; x++) { |
|
|
|
|
pixelColorValues[x][y] = 255; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cell_count_x = this.pixelColorValues.length - 1; |
|
|
|
|
cell_count_y = this.pixelColorValues[0].length - 1; |
|
|
|
|
this.terrain_scale = this.maxHeight / 255f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Terrain(Zone zone) { |
|
|
|
|
|
|
|
|
|
this.heightMapID = 999999; |
|
|
|
|
this.maxHeight = 0; |
|
|
|
|
int halfExtentsX = (int) zone.bounds.getHalfExtents().x; |
|
|
|
|
int halfExtentsY = (int) zone.bounds.getHalfExtents().y; |
|
|
|
|
this.zoneLoadID = 0; |
|
|
|
|
this.seaLevel = 0; |
|
|
|
|
|
|
|
|
|
// Cache the full extents to avoid the calculation
|
|
|
|
|
|
|
|
|
|
this.fullExtentsX = halfExtentsX * 2; |
|
|
|
|
this.fullExtentsY = halfExtentsY * 2; |
|
|
|
|
|
|
|
|
|
this.heightmapImage = null; |
|
|
|
|
|
|
|
|
|
// Calculate the data we do not load from table
|
|
|
|
|
|
|
|
|
|
this.cell_size_x = halfExtentsX; |
|
|
|
|
this.cell_size_y = halfExtentsY; |
|
|
|
|
|
|
|
|
|
this.pixelColorValues = new short[this.fullExtentsX][this.fullExtentsY]; |
|
|
|
|
|
|
|
|
|
for (int y = 0; y < this.fullExtentsY; y++) { |
|
|
|
|
for (int x = 0; x < this.fullExtentsX; x++) { |
|
|
|
|
pixelColorValues[x][y] = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cell_count_x = this.pixelColorValues.length - 1; |
|
|
|
|
cell_count_y = this.pixelColorValues[0].length - 1; |
|
|
|
|
this.terrain_scale = this.maxHeight / 255f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static void GeneratePlayerCityHeightMap() { |
|
|
|
|
|
|
|
|
|
Terrain.playerCityTerrain = new Terrain(); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static void GenerateCustomHeightMap(Zone zone) { |
|
|
|
|
|
|
|
|
|
Terrain heightMap = new Terrain(zone); |
|
|
|
|
|
|
|
|
|
Terrain.heightmapByLoadNum.put(zone.template, heightMap); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -245,91 +92,19 @@ public class Terrain {
@@ -245,91 +92,19 @@ public class Terrain {
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static void loadAlHeightMaps() { |
|
|
|
|
|
|
|
|
|
// Load the heightmaps into staging hashmap keyed by HashMapID
|
|
|
|
|
|
|
|
|
|
DbManager.HeightMapQueries.LOAD_ALL_HEIGHTMAPS(); |
|
|
|
|
|
|
|
|
|
//generate static player city heightmap.
|
|
|
|
|
|
|
|
|
|
Terrain.GeneratePlayerCityHeightMap(); |
|
|
|
|
|
|
|
|
|
Logger.info(Terrain.heightmapByLoadNum.size() + " Heightmaps cached."); |
|
|
|
|
|
|
|
|
|
// Load pixel data for heightmaps
|
|
|
|
|
|
|
|
|
|
try (Stream<Path> filePathStream = Files.walk(Paths.get(ConfigManager.DEFAULT_DATA_DIR + "heightmaps/TARGA/"))) { |
|
|
|
|
filePathStream.forEach(filePath -> { |
|
|
|
|
|
|
|
|
|
if (Files.isRegularFile(filePath)) { |
|
|
|
|
File imageFile = filePath.toFile(); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
BufferedImage heightmapImage = ImageIO.read(imageFile); |
|
|
|
|
|
|
|
|
|
// Generate pixel data for this heightmap. RPG channels are all the same
|
|
|
|
|
// in this greyscale TGA heightmap. We will choose red.
|
|
|
|
|
|
|
|
|
|
short[][] colorData = new short[heightmapImage.getWidth()][heightmapImage.getHeight()]; |
|
|
|
|
|
|
|
|
|
for (int y = 0; y < heightmapImage.getHeight(); y++) |
|
|
|
|
for (int x = 0; x < heightmapImage.getWidth(); x++) { |
|
|
|
|
|
|
|
|
|
Color color = new Color(heightmapImage.getRGB(x, y)); |
|
|
|
|
colorData[x][y] = (short) color.getRed(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Insert color data into lookup table
|
|
|
|
|
|
|
|
|
|
int heightMapID = Integer.parseInt(imageFile.getName().substring(0, imageFile.getName().lastIndexOf("."))); |
|
|
|
|
_pixelData.put(heightMapID, colorData); |
|
|
|
|
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
Logger.error(e); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
}); // Try with resources block
|
|
|
|
|
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
Logger.error(e); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void generatePixelData(Terrain terrain) { |
|
|
|
|
|
|
|
|
|
Color color; |
|
|
|
|
|
|
|
|
|
// Generate altitude lookup table for this heightmap
|
|
|
|
|
|
|
|
|
|
terrain.pixelColorValues = new short[terrain.heightmapImage.getWidth()][terrain.heightmapImage.getHeight()]; |
|
|
|
|
|
|
|
|
|
for (int y = 0; y < terrain.heightmapImage.getHeight(); y++) { |
|
|
|
|
for (int x = 0; x < terrain.heightmapImage.getWidth(); x++) { |
|
|
|
|
|
|
|
|
|
color = new Color(terrain.heightmapImage.getRGB(x, y)); |
|
|
|
|
terrain.pixelColorValues[x][y] = (short) color.getRed(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Vector2f getTerrainCell(Vector2f terrainLoc) { |
|
|
|
|
|
|
|
|
|
float terrainCell_x = terrainLoc.x / this.cell_size_x; |
|
|
|
|
float terrainCell_y = terrainLoc.y / this.cell_size_y; |
|
|
|
|
Vector2f terrain_cell = new Vector2f(terrainLoc.x / this.cell_size.x, terrainLoc.y / this.cell_size.y); |
|
|
|
|
|
|
|
|
|
// Clamp values when standing directly on max pole
|
|
|
|
|
|
|
|
|
|
if (terrainCell_x >= this.cell_count_x) |
|
|
|
|
terrainCell_x = terrainCell_x - 1; |
|
|
|
|
if (terrain_cell.x >= this.cell_count.x) |
|
|
|
|
terrain_cell.x = terrain_cell.x - 1; |
|
|
|
|
|
|
|
|
|
if (terrainCell_y >= this.cell_count_y) |
|
|
|
|
terrainCell_y = terrainCell_x - 1; |
|
|
|
|
if (terrain_cell.x >= this.cell_count.y) |
|
|
|
|
terrain_cell.y = terrain_cell.y - 1; |
|
|
|
|
|
|
|
|
|
return new Vector2f(terrainCell_x, terrainCell_y); |
|
|
|
|
return terrain_cell; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public float getInterpolatedTerrainHeight(Vector2f terrainLoc) { |
|
|
|
@ -351,10 +126,10 @@ public class Terrain {
@@ -351,10 +126,10 @@ public class Terrain {
|
|
|
|
|
float bottomLeftHeight; |
|
|
|
|
float bottomRightHeight; |
|
|
|
|
|
|
|
|
|
topLeftHeight = pixelColorValues[gridX][gridY]; |
|
|
|
|
topRightHeight = pixelColorValues[gridX + 1][gridY]; |
|
|
|
|
bottomLeftHeight = pixelColorValues[gridX][gridY + 1]; |
|
|
|
|
bottomRightHeight = pixelColorValues[gridX + 1][gridY + 1]; |
|
|
|
|
topLeftHeight = terrain_pixel_data[gridX][gridY]; |
|
|
|
|
topRightHeight = terrain_pixel_data[gridX + 1][gridY]; |
|
|
|
|
bottomLeftHeight = terrain_pixel_data[gridX][gridY + 1]; |
|
|
|
|
bottomRightHeight = terrain_pixel_data[gridX + 1][gridY + 1]; |
|
|
|
|
|
|
|
|
|
// Interpolate between the 4 vertices
|
|
|
|
|
|
|
|
|
@ -367,5 +142,4 @@ public class Terrain {
@@ -367,5 +142,4 @@ public class Terrain {
|
|
|
|
|
|
|
|
|
|
return interpolatedHeight; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|