import java.util.Arrays;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** Visual test for Perlin Noise */
class TerrainTest extends JPanel implements KeyListener {
final int W;
final int H;
private static void usage() {
System.err.print(
"Usage: java TerrainTest [OPTS] ALGORITHM\n" +
"OPTS:\n" +
" -g Grayscale (redscale below water)\n" +
" -s Don't render shadows\n" +
" -r Don't render rivers (when simulating rain)\n" +
" -c GRAIN Render as contour plot on a GRAINxGRAIN grid\n" +
" -k Wait for keypress each frame\n" +
Algorithm.usage());
System.exit(1);
}
public static void main(String[] args) {
if (args.length < 1) usage();
JFrame frame = new JFrame("Terrain Test");
TerrainTest panel = new TerrainTest(args);
frame.add(panel);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(panel);
}
public void keyTyped(KeyEvent e) { if (wait) repaint(); }
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
private Algorithm algorithm;
private BufferedImage canvas;
boolean grayscale = false; // Grayscale (over redscale)?
boolean shadows = true; // Render shadows?
boolean rivers = true; // Render rivers?
int contour = 0; // Render as contour plot with given granularity
boolean wait = false; // Wait for keypress between iterations?
public TerrainTest(String[] alg) {
int i = 0;
while (i < alg.length && alg[i].startsWith("-")) {
if (alg[i].equals("-s")) shadows = !shadows;
else if (alg[i].equals("-k")) wait = !wait;
else if (alg[i].equals("-g")) grayscale = !grayscale;
else if (alg[i].equals("-r")) rivers = !rivers;
else if (alg[i].equals("-c")) contour = Integer.parseInt(alg[++i]);
else usage();
++i;
}
algorithm = new Algorithm(Arrays.copyOfRange(alg, i, alg.length));
W = algorithm.W;
H = algorithm.H;
canvas = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
}
int frames = 0;
long lastreport = 0;
private void renderContour(Graphics2D g, long now, int grid,
double threshold) {
// Convert threshold (contour altitude) to color
int R = 0, G = 0, B = 0;
if (threshold < -0.5) {
B = (int)((threshold + 1.0) * 2.0 * 256.0);
} else if (threshold < 0) {
G = (int)((threshold + 0.5) * 2.0 * 256.0);
B = 255;
} else {
R = (int)(threshold * 255);
G = 128 + (int)(threshold * 127);
}
g.setColor(new Color(R,G,B));
Terrain noise = algorithm.terrain();
double z = now / 1000.0;
for (int x = 0; x < W; x += grid) {
for (int y = 0; y < H; y += grid) {
double h00 = noise.noise(x, y);
double h01 = noise.noise(x, y+grid);
double h10 = noise.noise(x+grid, y);
double h11 = noise.noise(x+grid, y+grid);
//
boolean b00 = h00 < threshold;
boolean b01 = h01 < threshold;
boolean b10 = h10 < threshold;
boolean b11 = h11 < threshold;
int mask = (b00?8:0) | (b01?4:0) | (b10?2:0) | (b11?1:0);
if (mask == 0 || mask == 0xF) continue;
int dN=0, dS=0, dE=0, dW=0;
if (b01 != b11) dS = (int)(grid * (h01 - threshold) / (h01 - h11));
if (b00 != b10) dN = (int)(grid * (h00 - threshold) / (h00 - h10));
if (b00 != b01) dW = (int)(grid * (h00 - threshold) / (h00 - h01));
if (b10 != b11) dE = (int)(grid * (h10 - threshold) / (h10 - h11));
switch (mask) {
case 0x1:
case 0xE:
g.drawLine(x+dS, y+grid, x+grid, y+dE); break;
case 0x2:
case 0xD:
g.drawLine(x+dN, y, x+grid, y+dE); break;
case 0x4:
case 0xB:
g.drawLine(x, y+dW, x+dS, y+grid); break;
case 0x8:
case 0x7:
g.drawLine(x, y+dW, x+dN, y); break;
case 0x3:
case 0xC:
g.drawLine(x+dN, y, x+dS, y+grid); break;
case 0x5:
case 0xA:
g.drawLine(x, y+dW, x+grid, y+dE); break;
case 0x6:
case 0x9:
double hmid = noise.noise(x+grid/2, y+grid/2);
boolean bmid = hmid < threshold;
if (bmid == b00) {
g.drawLine(x+dN, y, x+grid, y+dE);
g.drawLine(x, y+dW, x+dS, y+grid);
} else {
g.drawLine(x+dN, y, x, y+dW);
g.drawLine(x+grid, y+dE, x+dS, y+grid);
}
break;
}
}
}
}
private int RGB(int r, int g, int b) {
if (r < 0) r = 0; else if (r > 255) r = 255;
if (g < 0) g = 0; else if (g > 255) g = 255;
if (b < 0) b = 0; else if (b > 255) b = 255;
return 0xFF000000 | r << 16 | g << 8 | b;
}
public void paintComponent(Graphics g) {
algorithm.run();
long now = System.currentTimeMillis();
if (contour > 0) {
Graphics2D gc = canvas.createGraphics();
gc.setBackground(Color.BLACK);
gc.setColor(Color.BLACK);
gc.fillRect(0, 0, W, H);
gc.setColor(Color.WHITE);
for (double tour = -1.0; tour <= 1.0; tour += 0.2)
renderContour(gc, now, contour, tour);
} else {
Render.render(canvas, algorithm, now, shadows, rivers, grayscale);
}
// Report statistics
++frames;
if (now - lastreport > 1000) {
System.out.printf("%3.0f FPS\n", frames * 1000.0 / (now-lastreport));
lastreport = now;
frames = 0;
}
if (!wait)
repaint();
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(canvas, 0, 0, getWidth(), getHeight(), 0, 0, W, H, null);
}
public Dimension getPreferredSize() {
return new Dimension(W,H);
}
}