TerraForged/TerraForgedCore/src/main/java/com/terraforged/core/world/biome/BiomeTypeLoader.java

205 lines
7.1 KiB
Java

/*
*
* MIT License
*
* Copyright (c) 2020 TerraForged
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.terraforged.core.world.biome;
import me.dags.noise.util.NoiseUtil;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class BiomeTypeLoader {
private static BiomeTypeLoader instance;
private final float[][] edges = new float[BiomeType.RESOLUTION][BiomeType.RESOLUTION];
private final BiomeType[][] map = new BiomeType[BiomeType.RESOLUTION][BiomeType.RESOLUTION];
public BiomeTypeLoader() {
generateTypeMap();
generateEdgeMap();
}
public BiomeType[][] getTypeMap() {
return map;
}
public float[][] getEdgeMap() {
return edges;
}
private BiomeType getType(int x, int y) {
return map[y][x];
}
private void generateTypeMap() {
try {
BufferedImage image = ImageIO.read(BiomeType.class.getResourceAsStream("/biomes.png"));
float xf = image.getWidth() / (float) BiomeType.RESOLUTION;
float yf = image.getHeight() / (float) BiomeType.RESOLUTION;
for (int y = 0; y < BiomeType.RESOLUTION; y++) {
for (int x = 0; x < BiomeType.RESOLUTION; x++) {
if (BiomeType.MAX - y > x) {
map[BiomeType.MAX - y][x] = BiomeType.ALPINE;
continue;
}
int ix = NoiseUtil.round(x * xf);
int iy = NoiseUtil.round(y * yf);
int argb = image.getRGB(ix, iy);
Color color = fromARGB(argb);
map[BiomeType.MAX - y][x] = forColor(color);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void generateEdgeMap() {
int[] distances = new int[BiomeType.values().length];
for (int y = 0; y < BiomeType.RESOLUTION; y++) {
for (int x = 0; x < BiomeType.RESOLUTION; x++) {
if (y > x) continue;
BiomeType type = getType(x, y);
if (type == BiomeType.ALPINE) {
continue;
}
int distance2 = getEdge(x, y, type);
edges[y][x] = distance2;
distances[type.ordinal()] = Math.max(distances[type.ordinal()], distance2);
}
}
for (int y = 0; y < BiomeType.RESOLUTION; y++) {
for (int x = 0; x < BiomeType.RESOLUTION; x++) {
BiomeType type = getType(x, y);
int max = distances[type.ordinal()];
float distance = edges[y][x];
float value = NoiseUtil.pow(distance / max, 0.33F);
edges[y][x] = NoiseUtil.clamp(value, 0, 1);
}
}
}
private int getEdge(int cx, int cy, BiomeType type) {
int radius = BiomeType.RESOLUTION / 4;
int distance2 = Integer.MAX_VALUE;
int x0 = Math.max(0, cx - radius);
int x1 = Math.min(BiomeType.MAX, cx + radius);
int y0 = Math.max(0, cy - radius);
int y1 = Math.min(BiomeType.MAX, cy + radius);
for (int y = y0; y <= y1; y++) {
for (int x = x0; x <= x1; x++) {
BiomeType neighbour = getType(x, y);
if (neighbour == BiomeType.ALPINE) {
continue;
}
if (neighbour != type) {
int dist2 = dist2(cx, cy, x, y);
if (dist2 < distance2) {
distance2 = dist2;
}
}
}
}
return distance2;
}
private static BiomeType forColor(Color color) {
BiomeType type = null;
int closest = Integer.MAX_VALUE;
for (BiomeType t : BiomeType.values()) {
int distance2 = getDistance2(color, t.getLookup());
if (distance2 < closest) {
closest = distance2;
type = t;
}
}
if (type == null) {
return BiomeType.GRASSLAND;
}
return type;
}
private static int getDistance2(Color a, Color b) {
int dr = a.getRed() - b.getRed();
int dg = a.getGreen() - b.getGreen();
int db = a.getBlue() - b.getBlue();
return dr * dr + dg * dg + db * db;
}
private static Color fromARGB(int argb) {
int b = (argb) & 0xFF;
int g = (argb >> 8) & 0xFF;
int r = (argb >> 16) & 0xFF;
return new Color(r, g, b);
}
private static int dist2(int x1, int y1, int x2, int y2) {
int dx = x1 - x2;
int dy = y1 - y2;
return dx * dx + dy * dy;
}
private static BufferedImage generateEdgeMapImage() {
BufferedImage image = new BufferedImage(BiomeType.RESOLUTION, BiomeType.RESOLUTION, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < BiomeType.RESOLUTION; y++) {
for (int x = 0; x < BiomeType.RESOLUTION; x++) {
float temperature = x / (float) BiomeType.RESOLUTION;
float moisture = y / (float) BiomeType.RESOLUTION;
float value = BiomeType.getEdge(temperature, moisture);
int color = Color.HSBtoRGB(0, 0, value);
image.setRGB(x, image.getHeight() - 1 - y, color);
}
}
return image;
}
public static BiomeTypeLoader getInstance() {
if (instance == null) {
instance = new BiomeTypeLoader();
}
return instance;
}
public static void main(String[] args) throws Throwable {
BufferedImage img = generateEdgeMapImage();
ImageIO.write(img, "png", new File("biomes_dist.png"));
JLabel label = new JLabel(new ImageIcon(img));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}