]> icculus.org git repositories - divverent/nexuiz.git/blob - tools/ImgToMap/src/imgtomap/MapWriter.java
only generate sky-visblockers at the edge of "actual" geometry. If large parts of...
[divverent/nexuiz.git] / tools / ImgToMap / src / imgtomap / MapWriter.java
1 /*
2  * To change this template, choose Tools | Templates
3  * and open the template in the editor.
4  */
5 package imgtomap;
6
7 import java.awt.image.BufferedImage;
8 import java.awt.image.Raster;
9 import java.io.File;
10 import java.io.FileNotFoundException;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
15 import javax.imageio.ImageIO;
16
17 /**
18  *
19  * @author maik
20  */
21 public class MapWriter {
22
23     public int writeMap(Parameters p) {
24         if (!(new File(p.infile).exists())) {
25             return 1;
26         }
27
28         FileOutputStream fos;
29         try {
30             fos = new FileOutputStream(new File(p.outfile));
31         } catch (FileNotFoundException ex) {
32             Logger.getLogger(MapWriter.class.getName()).log(Level.SEVERE, null, ex);
33             return 1;
34         }
35
36         double[][] height = getHeightmap(p.infile);
37         double units = 1d * p.pixelsize;
38         double max = p.height;
39
40         StringBuffer buf = new StringBuffer();
41
42         // worldspawn start
43         buf.append("{\n\"classname\" \"worldspawn\"\n");
44
45         // wander through grid
46         for (int x = 0; x < height.length - 1; ++x) {
47             for (int y = 0; y < height[0].length - 1; ++y) {
48
49                 boolean skip = height[x][y] < 0 || height[x][y + 1] < 0 || height[x + 1][y] < 0 || height[x + 1][y + 1] < 0;
50
51                 if (!skip) {
52
53                     /*
54                      * 
55                      *      a +-------+ b
56                      *       /       /|
57                      *      /       / |
58                      *     /       /  |
59                      *  c +-------+ d + f   (e occluded, unused)
60                      *    |       |  /
61                      *    |       | /
62                      *    |       |/
63                      *  g +-------+ h
64                      * 
65                      */
66
67                     Vector3D a = new Vector3D(x * units, -y * units, height[x][y] * max);
68                     Vector3D b = new Vector3D((x + 1) * units, -y * units, height[x + 1][y] * max);
69                     Vector3D c = new Vector3D(x * units, -(y + 1) * units, height[x][y + 1] * max);
70                     Vector3D d = new Vector3D((x + 1) * units, -(y + 1) * units, height[x + 1][y + 1] * max);
71                     //Vector3D e = new Vector3D(x * units, -y * units, -16.0);
72                     Vector3D f = new Vector3D((x + 1) * units, -y * units, -16.0);
73                     Vector3D g = new Vector3D(x * units, -(y + 1) * units, -16.0);
74                     Vector3D h = new Vector3D((x + 1) * units, -(y + 1) * units, -16.0);
75
76                     buf.append("{\n");
77                     buf.append(getMapPlaneString(a, b, d, p.detail, p.texture, p.texturescale));
78                     buf.append(getMapPlaneString(d, b, f, p.detail, "common/caulk", p.texturescale));
79                     buf.append(getMapPlaneString(f, b, a, p.detail, "common/caulk", p.texturescale));
80                     buf.append(getMapPlaneString(a, d, h, p.detail, "common/caulk", p.texturescale));
81                     buf.append(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
82                     buf.append("}\n");
83
84
85                     buf.append("{\n");
86                     buf.append(getMapPlaneString(d, c, a, p.detail, p.texture, p.texturescale));
87                     buf.append(getMapPlaneString(g, c, d, p.detail, "common/caulk", p.texturescale));
88                     buf.append(getMapPlaneString(c, g, a, p.detail, "common/caulk", p.texturescale));
89                     buf.append(getMapPlaneString(h, d, a, p.detail, "common/caulk", p.texturescale));
90                     buf.append(getMapPlaneString(g, h, f, p.detail, "common/caulk", p.texturescale));
91                     buf.append("}\n");
92                 } else if (p.skyfill) {
93
94                     boolean totalskip = height[x][y] < -5 || height[x][y + 1] < -5 || height[x + 1][y] < -5 || height[x + 1][y + 1] < -5;
95
96                     if (!totalskip) {
97                         // fill skipped blocks with sky
98                         Vector3D p1 = new Vector3D(x * units, -(y + 1) * units, -32.0);
99                         Vector3D p2 = new Vector3D((x + 1) * units, -y * units, p.skyheight);
100
101                         writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
102                     }
103                 }
104             }
105         }
106
107         if (p.sky) {
108             // generate skybox
109             int x = height.length - 1;
110             int y = height[0].length - 1;
111
112             // top
113             Vector3D p1 = new Vector3D(0, -y * units, p.skyheight);
114             Vector3D p2 = new Vector3D(x * units, 0, p.skyheight + 32.0);
115             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
116
117             // bottom
118             p1 = new Vector3D(0, -y * units, -64.0);
119             p2 = new Vector3D(x * units, 0, -32.0);
120             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
121
122             // north
123             p1 = new Vector3D(0, 0, -32.0);
124             p2 = new Vector3D(x * units, 32, p.skyheight);
125             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
126
127             // east
128             p1 = new Vector3D(x * units, -y * units, -32.0);
129             p2 = new Vector3D(x * units + 32.0, 0, p.skyheight);
130             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
131
132             // south
133             p1 = new Vector3D(0, -y * units - 32, -32.0);
134             p2 = new Vector3D(x * units, -y * units, p.skyheight);
135             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
136
137
138             // west
139             p1 = new Vector3D(0 - 32.0, -y * units, -32.0);
140             p2 = new Vector3D(0, 0, p.skyheight);
141             writeBoxBrush(buf, p1, p2, false, p.skytexture, 1.0);
142
143         }
144
145         // worldspawn end
146         buf.append("}\n");
147         try {
148             fos.write(buf.toString().getBytes());
149         } catch (IOException ex) {
150             Logger.getLogger(MapWriter.class.getName()).log(Level.SEVERE, null, ex);
151             return 1;
152         }
153         return 0;
154     }
155
156     private void writeBoxBrush(StringBuffer buf, Vector3D p1, Vector3D p2, boolean detail, String texture, double scale) {
157         Vector3D a = new Vector3D(p1.x, p2.y, p2.z);
158         Vector3D b = p2;
159         Vector3D c = new Vector3D(p1.x, p1.y, p2.z);
160         Vector3D d = new Vector3D(p2.x, p1.y, p2.z);
161         //Vector3D e unused
162         Vector3D f = new Vector3D(p2.x, p2.y, p1.z);
163         Vector3D g = p1;
164         Vector3D h = new Vector3D(p2.x, p1.y, p1.z);
165
166         buf.append("{\n");
167         buf.append(getMapPlaneString(a, b, d, detail, texture, scale));
168         buf.append(getMapPlaneString(d, b, f, detail, texture, scale));
169         buf.append(getMapPlaneString(c, d, h, detail, texture, scale));
170         buf.append(getMapPlaneString(a, c, g, detail, texture, scale));
171         buf.append(getMapPlaneString(f, b, a, detail, texture, scale));
172         buf.append(getMapPlaneString(g, h, f, detail, texture, scale));
173         buf.append("}\n");
174
175     }
176
177     private String getMapPlaneString(Vector3D p1, Vector3D p2, Vector3D p3, boolean detail, String material, double scale) {
178         int flag;
179         if (detail) {
180             flag = 134217728;
181         } else {
182             flag = 0;
183         }
184         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";
185     }
186
187     private class Vector3D {
188
189         public double x,  y,  z;
190
191         public Vector3D() {
192             this(0.0, 0.0, 0.0);
193         }
194
195         public Vector3D(double x, double y, double z) {
196             this.x = x;
197             this.y = y;
198             this.z = z;
199         }
200
201         public Vector3D crossproduct(Vector3D p1) {
202             Vector3D result = new Vector3D();
203
204             result.x = this.y * p1.z - this.z * p1.y;
205             result.y = this.z * p1.x - this.x * p1.z;
206             result.z = this.x * p1.y - this.y * p1.x;
207
208             return result;
209         }
210
211         public double dotproduct(Vector3D p1) {
212             return this.x * p1.x + this.y * p1.y + this.z * p1.z;
213         }
214
215         public Vector3D substract(Vector3D p1) {
216             Vector3D result = new Vector3D();
217
218             result.x = this.x - p1.x;
219             result.y = this.y - p1.y;
220             result.z = this.z - p1.z;
221
222             return result;
223         }
224
225         public void scale(double factor) {
226             x *= factor;
227             y *= factor;
228             z *= factor;
229         }
230
231         public double length() {
232             return Math.sqrt((x * x) + (y * y) + (z * z));
233         }
234
235         public void normalize() {
236             double l = length();
237
238             x /= l;
239             y /= l;
240             z /= l;
241         }
242     }
243
244     private double[][] markTotalSkip(double[][] input) {
245         double[][] result = new double[input.length][input[0].length];
246
247         int xmax = input.length - 1;
248         int ymax = input[0].length - 1;
249
250         for (int x = 0; x <= xmax; ++x) {
251             for (int y = 0; y <= ymax; ++y) {
252                 double val;
253                 double max;
254
255                 val = input[x][y];
256                 max = val;
257
258                 if (x - 1 >= 0 && y - 1 >= 0) {
259                     val = input[x - 1][y - 1];
260                     max = val > max ? val : max;
261                 }
262
263                 if (y - 1 >= 0) {
264                     val = input[x][y - 1];
265                     max = val > max ? val : max;
266                 }
267
268                 if (x + 1 <= xmax && y - 1 >= 0) {
269                     val = input[x + 1][y - 1];
270                     max = val > max ? val : max;
271                 }
272
273                 if (x - 1 >= 0) {
274                     val = input[x - 1][y];
275                     max = val > max ? val : max;
276                 }
277
278                 if (x + 1 <= xmax) {
279                     val = input[x + 1][y];
280                     max = val > max ? val : max;
281                 }
282
283                 if (x - 1 >= 0 && y + 1 <= ymax) {
284                     val = input[x - 1][y + 1];
285                     max = val > max ? val : max;
286                 }
287
288                 if (y + 1 <= ymax) {
289                     val = input[x][y + 1];
290                     max = val > max ? val : max;
291                 }
292
293                 if (x + 1 <= xmax && y + 1 <= ymax) {
294                     val = input[x + 1][y + 1];
295                     max = val > max ? val : max;
296                 }
297
298                 if (max < 0) {
299                     result[x][y] = -10.0;
300                 } else {
301                     result[x][y] = input[x][y];
302                 }
303
304             }
305         }
306
307
308         return result;
309     }
310
311     private double[][] getHeightmap(String file) {
312         try {
313             BufferedImage bimg = ImageIO.read(new File(file));
314             Raster raster = bimg.getRaster();
315             int x = raster.getWidth();
316             int y = raster.getHeight();
317
318             double[][] result = new double[x][y];
319
320             for (int xi = 0; xi < x; ++xi) {
321                 for (int yi = 0; yi < y; ++yi) {
322                     float[] pixel = raster.getPixel(xi, yi, (float[]) null);
323
324                     int channels;
325                     boolean alpha;
326                     if (pixel.length == 3) {
327                         // RGB
328                         channels = 3;
329                         alpha = false;
330                     } else if (pixel.length == 4) {
331                         // RGBA
332                         channels = 3;
333                         alpha = true;
334                     } else if (pixel.length == 1) {
335                         // grayscale
336                         channels = 1;
337                         alpha = false;
338                     } else {
339                         // grayscale with alpha
340                         channels = 1;
341                         alpha = true;
342                     }
343
344                     float tmp = 0f;
345                     for (int i = 0; i < channels; ++i) {
346                         tmp += pixel[i];
347                     }
348                     result[xi][yi] = tmp / (channels * 255f);
349
350                     if (alpha) {
351                         // mark this pixel to be skipped
352                         if (pixel[pixel.length - 1] < 64.0) {
353                             result[xi][yi] = -1.0;
354                         }
355                     }
356                 }
357             }
358
359
360             return markTotalSkip(result);
361         } catch (IOException ex) {
362             Logger.getLogger(MapWriter.class.getName()).log(Level.SEVERE, null, ex);
363         }
364
365         return null;
366     }
367 }