-
-
Save BurntPizza/c6327045f6ad3d7c56c3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pl.warlander.terraingen.core; | |
import java.util.Random; | |
public class FractalLayer { | |
private final float value; | |
private final int size; | |
private final int width; | |
private final int height; | |
private float[][] noise; | |
FractalLayer(float value, int size, int width, int height, long seed) { | |
this.value = value; | |
this.size = size; | |
this.width = width; | |
this.height = height; | |
this.noise = new float[height+1][width+1]; | |
Random rand = new Random(seed); | |
for (int y = 0; y < noise.length - 1; y++) { | |
final float[] n = noise[y]; | |
for (int x = 0; x < n.length - 1; x++) { | |
n[x] = (rand.nextFloat()*2-1) * value; | |
} | |
} | |
} | |
public void smooth(double power) { | |
float[][] newNoise = new float[width+1][height+1]; | |
double centerValue = 1-power; | |
double nearValue = power/9; | |
for (int i = 0; i <= width; i++) { | |
for (int i2 = 0; i2 <= height; i2++) { | |
newNoise[i][i2] = smoothValue(newNoise, i, i2, centerValue, nearValue); | |
} | |
} | |
noise = newNoise; | |
} | |
private float smoothValue(float[][] newNoise, int x, int y, double centerValue, double nearValue) { | |
float val = 0; | |
for (int i = -1; i<=1; i++) { | |
for (int i2 = -1; i2<=1; i2++) { | |
if (i==0 && i2==0) { | |
val += noise[x][y] * centerValue; | |
} | |
else { | |
val += getValueOrDefault(newNoise, x+i, y+i2, noise[x][y]) * nearValue; | |
} | |
} | |
} | |
return val; | |
} | |
private double getValueOrDefault(float[][] array, int x, int y, double def) { | |
try { | |
return array[x][y]; | |
} catch (IndexOutOfBoundsException ex) { | |
return def; | |
} | |
} | |
public double get(int y, int x) { | |
if (y<0 || y>=width*size || x<0 || x>=height*size) { | |
throw new IllegalArgumentException("Width or height is too small or too high"); | |
} | |
final int startY = y/size; | |
final int startX = x/size; | |
final double h00 = noise[startY][startX]; | |
final double h10 = noise[startY+1][startX]; | |
final double h01 = noise[startY][startX+1]; | |
final double h11 = noise[startY+1][startX+1]; | |
final double ratioY = (double)y/size-startY; | |
final double ratioX = (double)x/size-startX; | |
final double revRatioY = 1 - ratioY; | |
final double revRatioX = 1 - ratioX; | |
final double y0 = (h00*revRatioY + h10*ratioY); | |
final double y1 = (h01*revRatioY + h11*ratioY); | |
return (y0*revRatioX + y1*ratioX); | |
} | |
public double getValue() { | |
return value; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pl.warlander.terraingen.core; | |
import java.util.ArrayList; | |
import java.util.Random; | |
public class HeightGenerator { | |
private final long seed; | |
private final int width; | |
private final int height; | |
private final Random rand; | |
private final ArrayList<FractalLayer> genLayers; | |
public HeightGenerator(final long seed, final int width, final int height) { | |
this.seed = seed; | |
this.width = width; | |
this.height = height; | |
this.rand = new Random(seed); | |
genLayers = new ArrayList<>(); | |
} | |
public FractalLayer addFractalLayer(final double value, final int size) { | |
if (width % size != 0 || height % size != 0) { | |
throw new IllegalArgumentException("Size must be a divisor of width and height"); | |
} | |
FractalLayer layer = new FractalLayer((float) value, size, width / size, height / size, rand.nextLong()); | |
genLayers.add(layer); | |
return layer; | |
} | |
public float[][] genArray() { | |
double vs = 0;//genLayers.stream().mapToDouble(layer -> layer.getValue()).sum(); | |
// expensive stream creation dominates this otherwise super-cheap operation | |
for (FractalLayer l : genLayers) { | |
vs += l.getValue(); | |
} | |
final float valuesSum = (float) vs; | |
// switched to explict [y][x] indexing | |
final float[][] result = new float[height][width]; | |
for (FractalLayer layer : genLayers) { | |
for (int y = 0; y < result.length; y++) { | |
final float[] r = result[y]; | |
for (int x = 0; x < r.length; x++) { | |
// note that get(y, x) is considerably faster than get(x, y) | |
r[x] += layer.get(y, x); | |
} | |
} | |
} | |
for (int y = 0; y < result.length; y++) { | |
final float[] r = result[y]; | |
for (int x = 0; x < r.length; x++) { | |
r[x] /= valuesSum; | |
} | |
} | |
return result; | |
} | |
public long getSeed() { | |
return seed; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pl.warlander.terraingen.util; | |
import java.awt.Color; | |
import java.awt.image.BufferedImage; | |
import java.awt.image.DataBufferInt; | |
public class ImageExporter { | |
public static BufferedImage exportToImage(float[][] terrain) { | |
int height = terrain.length; | |
int width = terrain[0].length; | |
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); | |
final int[] pix = ((DataBufferInt)img.getRaster().getDataBuffer()).getData(); | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
float value = terrain[y][x]; | |
boolean water = value < 0; | |
value = Math.abs(value); | |
value = Math.min(value, 1); | |
int c = (int) (255 - value * 255); | |
if (water) { | |
//img.setRGB(x, y, new Color(0, 0, 1-value).getRGB()); | |
pix[x + y * width] = 0xFF000000 | c; | |
} | |
else { | |
//img.setRGB(x, y, new Color(0, 1-value, 0).getRGB()); | |
pix[x + y * width] = 0xFF000000 | (c << 8); | |
} | |
} | |
} | |
return img; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pl.warlander.terraingen; | |
import java.io.BufferedOutputStream; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import javax.imageio.ImageIO; | |
import pl.warlander.terraingen.core.HeightGenerator; | |
import pl.warlander.terraingen.util.ImageExporter; | |
public class Launch { | |
public static void main(String[] args) { | |
int blackbox = 0; | |
int num = 500; | |
long t = System.currentTimeMillis(); | |
for (int i = 0; i < num; i++) { | |
HeightGenerator gen = new HeightGenerator(10002, 2048, 2048); | |
gen.addFractalLayer(1d, 512); | |
//generateAndSaveImage(gen, "1.png"); | |
gen.addFractalLayer(1d / 2, 128); | |
//generateAndSaveImage(gen, "2.png"); | |
gen.addFractalLayer(1d / 4, 64); | |
//generateAndSaveImage(gen, "3.png"); | |
gen.addFractalLayer(1d / 8, 32); | |
//generateAndSaveImage(gen, "4.png"); | |
gen.addFractalLayer(1d / 16, 16); | |
//generateAndSaveImage(gen, "5.png"); | |
gen.addFractalLayer(1d / 32, 8); | |
//generateAndSaveImage(gen, "6.png"); | |
gen.addFractalLayer(1d / 64, 4); | |
//generateAndSaveImage(gen, "7.png"); | |
gen.addFractalLayer(1d / 128, 2); | |
//generateAndSaveImage(gen, "8.png"); | |
gen.addFractalLayer(1d / 256, 1); | |
blackbox += gen.genArray().length; | |
} | |
System.out.println(blackbox); | |
// IO, as expected, takes most of the exec time (~2 sec on HDD for 2048x2048, not including genArray) | |
//generateAndSaveImage(gen, "9.png"); | |
System.out.println((System.currentTimeMillis() - t) / num); | |
} | |
private static void generateAndSaveImage(HeightGenerator gen, String file) { | |
try { | |
ImageIO.write(ImageExporter.exportToImage(gen.genArray()), "png", new File(file)); | |
} catch (IOException ex) { | |
Logger.getLogger(Launch.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment