2 * To change this template, choose Tools | Templates
3 * and open the template in the editor.
7 import java.awt.image.BufferedImage;
8 import java.awt.image.Raster;
10 import java.io.FileNotFoundException;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.io.PrintWriter;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.logging.Level;
17 import java.util.logging.Logger;
18 import javax.imageio.ImageIO;
24 public class MapWriter {
26 public int writeMap(Parameters p) {
27 if (!(new File(p.infile).exists())) {
31 double[][] height = getHeightmap(p.infile);
32 double[][] columns = getColumns(height);
33 double units = 1d * p.pixelsize;
34 double max = p.height;
36 PrintWriter pw = null;
38 pw = new PrintWriter(new FileOutputStream(new File(p.outfile)));
39 } catch (FileNotFoundException ex) {
40 Logger.getLogger(MapWriter.class.getName()).log(Level.SEVERE, null, ex);
45 pw.print("{\n\"classname\" \"worldspawn\"\n");
46 pw.print("\n\"gridsize\" \"128 128 256\"\n");
47 pw.print("\n\"blocksize\" \"2048 2048 2048\"\n");
49 double xmax = (columns.length - 1) * units;
50 double ymax = (columns[0].length - 1) * units;
53 List<Block> fillers = genSkyFillers(columns);
54 for (Block b : fillers) {
55 double x = b.x * units;
56 double y = (b.y + b.ydim) * units;
57 x = x > xmax ? xmax : x;
58 y = y > ymax ? ymax : y;
59 Vector3D p1 = new Vector3D(x, -y, -32.0);
61 x = (b.x + b.xdim) * units;
63 x = x > xmax ? xmax : x;
64 y = y > ymax ? ymax : y;
65 Vector3D p2 = new Vector3D(x, -y, p.skyheight);
67 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
73 int x = height.length - 1;
74 int y = height[0].length - 1;
77 Vector3D p1 = new Vector3D(0, -y * units, p.skyheight);
78 Vector3D p2 = new Vector3D(x * units, 0, p.skyheight + 32.0);
79 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
82 p1 = new Vector3D(0, -y * units, -64.0);
83 p2 = new Vector3D(x * units, 0, -32.0);
84 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
87 p1 = new Vector3D(0, 0, -32.0);
88 p2 = new Vector3D(x * units, 32, p.skyheight);
89 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
92 p1 = new Vector3D(x * units, -y * units, -32.0);
93 p2 = new Vector3D(x * units + 32.0, 0, p.skyheight);
94 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
97 p1 = new Vector3D(0, -y * units - 32, -32.0);
98 p2 = new Vector3D(x * units, -y * units, p.skyheight);
99 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
103 p1 = new Vector3D(0 - 32.0, -y * units, -32.0);
104 p2 = new Vector3D(0, 0, p.skyheight);
105 writeBoxBrush(pw, p1, p2, false, p.skytexture, 1.0);
109 // genBlockers screws the columns array!
110 // this should be the last step!
112 List<Block> blockers = genBlockers(columns, 0.15);
113 for (Block b : blockers) {
114 double z = b.minheight * p.height - 1;
115 z = Math.floor(z / 16);
119 double x = b.x * units;
120 double y = (b.y + b.ydim) * units;
121 x = x > xmax ? xmax : x;
122 y = y > ymax ? ymax : y;
123 Vector3D p1 = new Vector3D(x, -y, -32.0);
125 x = (b.x + b.xdim) * units;
127 x = x > xmax ? xmax : x;
128 y = y > ymax ? ymax : y;
129 Vector3D p2 = new Vector3D(x, -y, z);
131 writeBoxBrush(pw, p1, p2, false, "common/caulk", 1.0);
141 pw.print("{\n\"classname\" \"func_group\"\n");
142 pw.print("\n\"terrain\" \"1\"\n");
143 // wander through grid
144 for (int x = 0; x < height.length - 1; ++x) {
145 for (int y = 0; y < height[0].length - 1; ++y) {
147 boolean skip = getMinMaxForRegion(height, x, y, 2)[0] < 0;
157 * c +-------+ d + f (e occluded, unused)
167 double grad1 = Math.abs(height[x][y] - height[x + 1][y + 1]);
170 double grad2 = Math.abs(height[x + 1][y] - height[x][y + 1]);
172 Vector3D a = new Vector3D(x * units, -y * units, Math.floor(height[x][y] * max));
173 Vector3D b = new Vector3D((x + 1) * units, -y * units, Math.floor(height[x + 1][y] * max));
174 Vector3D c = new Vector3D(x * units, -(y + 1) * units, Math.floor(height[x][y + 1] * max));
175 Vector3D d = new Vector3D((x + 1) * units, -(y + 1) * units, Math.floor(height[x + 1][y + 1] * max));
176 //Vector3D e = new Vector3D(x * units, -y * units, -16.0);
177 Vector3D f = new Vector3D((x + 1) * units, -y * units, -16.0);
178 Vector3D g = new Vector3D(x * units, -(y + 1) * units, -16.0);
179 Vector3D h = new Vector3D((x + 1) * units, -(y + 1) * units, -16.0);
183 pw.print(getMapPlaneString(a, b, c, p.detail, p.texture, p.texturescale));
184 pw.print(getMapPlaneString(f, b, a, p.detail, "common/caulk", p.texturescale));
185 pw.print(getMapPlaneString(a, c, g, p.detail, "common/caulk", p.texturescale));
186 pw.print(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
187 pw.print(getMapPlaneString(g, c, b, p.detail, "common/caulk", p.texturescale));
192 pw.print(getMapPlaneString(b, d, c, p.detail, p.texture, p.texturescale));
193 pw.print(getMapPlaneString(d, h, g, p.detail, "common/caulk", p.texturescale));
194 pw.print(getMapPlaneString(d, b, f, p.detail, "common/caulk", p.texturescale));
195 pw.print(getMapPlaneString(f, b, c, p.detail, "common/caulk", p.texturescale));
196 pw.print(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
202 pw.print(getMapPlaneString(a, b, d, p.detail, p.texture, p.texturescale));
203 pw.print(getMapPlaneString(d, b, f, p.detail, "common/caulk", p.texturescale));
204 pw.print(getMapPlaneString(f, b, a, p.detail, "common/caulk", p.texturescale));
205 pw.print(getMapPlaneString(a, d, h, p.detail, "common/caulk", p.texturescale));
206 pw.print(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
211 pw.print(getMapPlaneString(d, c, a, p.detail, p.texture, p.texturescale));
212 pw.print(getMapPlaneString(g, c, d, p.detail, "common/caulk", p.texturescale));
213 pw.print(getMapPlaneString(c, g, a, p.detail, "common/caulk", p.texturescale));
214 pw.print(getMapPlaneString(h, d, a, p.detail, "common/caulk", p.texturescale));
215 pw.print(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
228 private void writeBoxBrush(PrintWriter pw, Vector3D p1, Vector3D p2, boolean detail, String texture, double scale) {
229 Vector3D a = new Vector3D(p1.x, p2.y, p2.z);
231 Vector3D c = new Vector3D(p1.x, p1.y, p2.z);
232 Vector3D d = new Vector3D(p2.x, p1.y, p2.z);
234 Vector3D f = new Vector3D(p2.x, p2.y, p1.z);
236 Vector3D h = new Vector3D(p2.x, p1.y, p1.z);
239 pw.print(getMapPlaneString(a, b, d, detail, texture, scale));
240 pw.print(getMapPlaneString(d, b, f, detail, texture, scale));
241 pw.print(getMapPlaneString(c, d, h, detail, texture, scale));
242 pw.print(getMapPlaneString(a, c, g, detail, texture, scale));
243 pw.print(getMapPlaneString(f, b, a, detail, texture, scale));
244 pw.print(getMapPlaneString(g, h, f, detail, texture, scale));
249 private String getMapPlaneString(Vector3D p1, Vector3D p2, Vector3D p3, boolean detail, String material, double scale) {
256 return "( " + p1.x + " " + p1.y + " " + p1.z + " ) ( " + p2.x + " " + p2.y + " " + p2.z + " ) ( " + p3.x + " " + p3.y + " " + p3.z + " ) " + material + " 0 0 0 " + scale + " " + scale + " " + flag + " 0 0\n";
259 private double[][] getHeightmap(String file) {
261 BufferedImage bimg = ImageIO.read(new File(file));
262 Raster raster = bimg.getRaster();
263 int x = raster.getWidth();
264 int y = raster.getHeight();
266 double[][] result = new double[x][y];
268 for (int xi = 0; xi < x; ++xi) {
269 for (int yi = 0; yi < y; ++yi) {
270 float[] pixel = raster.getPixel(xi, yi, (float[]) null);
274 if (pixel.length == 3) {
278 } else if (pixel.length == 4) {
282 } else if (pixel.length == 1) {
287 // grayscale with alpha
293 for (int i = 0; i < channels; ++i) {
296 result[xi][yi] = tmp / (channels * 255f);
299 // mark this pixel to be skipped
300 if (pixel[pixel.length - 1] < 64.0) {
301 result[xi][yi] = -1.0;
309 } catch (IOException ex) {
310 Logger.getLogger(MapWriter.class.getName()).log(Level.SEVERE, null, ex);
316 private double[][] getColumns(double[][] heights) {
317 double[][] result = new double[heights.length][heights[0].length];
319 for (int x = 0; x < heights.length; ++x) {
320 for (int y = 0; y < heights[0].length; ++y) {
321 result[x][y] = getMinMaxForRegion(heights, x, y, 2)[0];
328 private double[] getMinMaxForRegion(double[][] field, int x, int y, int dim) {
329 return getMinMaxForRegion(field, x, y, dim, dim);
332 private double[] getMinMaxForRegion(double[][] field, int x, int y, int xdim, int ydim) {
336 for (int i = x; i < x + xdim; ++i) {
337 for (int j = y; j < y + ydim; ++j) {
338 if (i >= 0 && j >= 0 && i < field.length && j < field[0].length) {
339 min = field[i][j] < min ? field[i][j] : min;
340 max = field[i][j] > max ? field[i][j] : max;
345 double[] result = {min, max};
349 private List<Block> genBlockers(double[][] columns, double delta) {
351 Block[][] blockers = new Block[columns.length][columns[0].length];
352 LinkedList<Block> result = new LinkedList<Block>();
354 for (int x = 0; x < columns.length; ++x) {
355 for (int y = 0; y < columns[0].length; ++y) {
356 if (blockers[x][y] == null && columns[x][y] >= 0) {
357 // this pixel isn't covered by a blocker yet... so let's create one!
358 Block b = new Block();
362 b.minheight = b.origheight = columns[x][y];
364 // grow till the delta hits
367 boolean xgrow = true;
368 boolean ygrow = true;
369 double min = b.minheight;
370 for (; xdim < columns.length && ydim < columns[0].length;) {
371 double[] minmax = getMinMaxForRegion(columns, x, y, xdim + 1, ydim);
372 if (Math.abs(b.origheight - minmax[0]) > delta || Math.abs(b.origheight - minmax[1]) > delta) {
376 minmax = getMinMaxForRegion(columns, x, y, xdim, ydim + 1);
377 if (Math.abs(b.origheight - minmax[0]) > delta || Math.abs(b.origheight - minmax[1]) > delta) {
390 minmax = getMinMaxForRegion(columns, x, y, xdim, ydim);
393 if (!(xgrow || ygrow)) {
402 for (int i = x; i < x + b.xdim; ++i) {
403 for (int j = y; j < y + b.ydim; ++j) {
404 if (i >= 0 && j >= 0 && i < blockers.length && j < blockers[0].length) {
406 columns[i][j] = -1337.0;
417 private List<Block> genSkyFillers(double[][] columns) {
421 for (int x = 0; x < columns.length; ++x) {
422 for (int y = 0; y < columns[0].length; ++y) {
423 if (columns[x][y] < 0) {
424 // this is a skipped block, see if it neighbours a
426 if (getMinMaxForRegion(columns, x - 1, y - 1, 3)[1] >= 0) {
427 columns[x][y] = -100d;
434 Block[][] fillers = new Block[columns.length][columns[0].length];
435 LinkedList<Block> result = new LinkedList<Block>();
437 for (int x = 0; x < columns.length; ++x) {
438 for (int y = 0; y < columns[0].length; ++y) {
439 if (fillers[x][y] == null && columns[x][y] == -100d) {
440 // this pixel is marked to be skyfill
441 Block b = new Block();
445 b.minheight = b.origheight = columns[x][y];
447 // grow till the delta hits
450 boolean xgrow = true;
451 boolean ygrow = true;
452 double min = b.minheight;
453 for (; xdim < columns.length && ydim < columns[0].length;) {
454 double[] minmax = getMinMaxForRegion(columns, x, y, xdim + 1, ydim);
455 if (Math.abs(b.origheight - minmax[0]) > delta || Math.abs(b.origheight - minmax[1]) > delta) {
459 minmax = getMinMaxForRegion(columns, x, y, xdim, ydim + 1);
460 if (Math.abs(b.origheight - minmax[0]) > delta || Math.abs(b.origheight - minmax[1]) > delta) {
473 minmax = getMinMaxForRegion(columns, x, y, xdim, ydim);
476 if (!(xgrow || ygrow)) {
485 for (int i = x; i < x + b.xdim; ++i) {
486 for (int j = y; j < y + b.ydim; ++j) {
487 if (i >= 0 && j >= 0 && i < fillers.length && j < fillers[0].length) {
489 columns[i][j] = -1337.0;
500 private class Vector3D {
502 public double x, y, z;
508 public Vector3D(double x, double y, double z) {
514 public Vector3D crossproduct(Vector3D p1) {
515 Vector3D result = new Vector3D();
517 result.x = this.y * p1.z - this.z * p1.y;
518 result.y = this.z * p1.x - this.x * p1.z;
519 result.z = this.x * p1.y - this.y * p1.x;
524 public double dotproduct(Vector3D p1) {
525 return this.x * p1.x + this.y * p1.y + this.z * p1.z;
528 public Vector3D substract(Vector3D p1) {
529 Vector3D result = new Vector3D();
531 result.x = this.x - p1.x;
532 result.y = this.y - p1.y;
533 result.z = this.z - p1.z;
538 public void scale(double factor) {
544 public double length() {
545 return Math.sqrt((x * x) + (y * y) + (z * z));
548 public void normalize() {
557 private class Block {
559 public int x, y, xdim, ydim;
560 public double origheight, minheight;