Skip to content

Instantly share code, notes, and snippets.

@BurntPizza
Forked from Warlander/FastRandom.java
Last active August 29, 2015 14:10
Show Gist options
  • Save BurntPizza/c6327045f6ad3d7c56c3 to your computer and use it in GitHub Desktop.
Save BurntPizza/c6327045f6ad3d7c56c3 to your computer and use it in GitHub Desktop.
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;
}
}
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;
}
}
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;
}
}
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