1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.terrain.heightmap; 33 34 import com.jme3.math.FastMath; 35 import com.jme3.util.LittleEndien; 36 import java.io.*; 37 import java.net.URL; 38 import java.util.logging.Logger; 39 40 /** 41 * <code>RawHeightMap</code> creates a height map from a RAW image file. The 42 * greyscale image denotes height based on the value of the pixel for each 43 * point. Where pure black the lowest point and pure white denotes the highest. 44 * 45 * @author Mark Powell 46 * @version $Id$ 47 */ 48 public class RawHeightMap extends AbstractHeightMap { 49 50 private static final Logger logger = Logger.getLogger(RawHeightMap.class.getName()); 51 /** 52 * Format specification for 8 bit precision heightmaps 53 */ 54 public static final int FORMAT_8BIT = 0; 55 /** 56 * Format specification for 16 bit little endian heightmaps 57 */ 58 public static final int FORMAT_16BITLE = 1; 59 /** 60 * Format specification for 16 bit big endian heightmaps 61 */ 62 public static final int FORMAT_16BITBE = 2; 63 private int format; 64 private boolean swapxy; 65 private InputStream stream; 66 67 /** 68 * Constructor creates a new <code>RawHeightMap</code> object and loads a 69 * RAW image file to use as a height field. The greyscale image denotes the 70 * height of the terrain, where dark is low point and bright is high point. 71 * The values of the RAW correspond directly with the RAW values or 0 - 255. 72 * 73 * @param filename 74 * the RAW file to use as the heightmap. 75 * @param size 76 * the size of the RAW (must be square). 77 * @throws JmeException 78 * if the filename is null or not RAW, and if the size is 0 or 79 * less. 80 */ RawHeightMap(String filename, int size)81 public RawHeightMap(String filename, int size) throws Exception { 82 this(filename, size, FORMAT_8BIT, false); 83 } 84 RawHeightMap(float heightData[])85 public RawHeightMap(float heightData[]) { 86 this.heightData = heightData; 87 this.size = (int) FastMath.sqrt(heightData.length); 88 this.format = FORMAT_8BIT; 89 } 90 RawHeightMap(String filename, int size, int format, boolean swapxy)91 public RawHeightMap(String filename, int size, int format, boolean swapxy) throws Exception { 92 // varify that filename and size are valid. 93 if (null == filename || size <= 0) { 94 throw new Exception("Must supply valid filename and " 95 + "size (> 0)"); 96 } 97 try { 98 setup(new FileInputStream(filename), size, format, swapxy); 99 } catch (FileNotFoundException e) { 100 throw new Exception("height file not found: " + filename); 101 } 102 } 103 RawHeightMap(InputStream stream, int size, int format, boolean swapxy)104 public RawHeightMap(InputStream stream, int size, int format, boolean swapxy) throws Exception { 105 setup(stream, size, format, swapxy); 106 } 107 RawHeightMap(URL resource, int size, int format, boolean swapxy)108 public RawHeightMap(URL resource, int size, int format, boolean swapxy) throws Exception { 109 // varify that resource and size are valid. 110 if (null == resource || size <= 0) { 111 throw new Exception("Must supply valid resource and " 112 + "size (> 0)"); 113 } 114 115 116 try { 117 setup(resource.openStream(), size, format, swapxy); 118 } catch (IOException e) { 119 throw new Exception("Unable to open height url: " + resource); 120 } 121 } 122 setup(InputStream stream, int size, int format, boolean swapxy)123 private void setup(InputStream stream, int size, int format, boolean swapxy) throws Exception { 124 // varify that filename and size are valid. 125 if (null == stream || size <= 0) { 126 throw new Exception("Must supply valid stream and " 127 + "size (> 0)"); 128 } 129 130 131 this.stream = stream; 132 this.size = size; 133 this.format = format; 134 this.swapxy = swapxy; 135 load(); 136 } 137 138 /** 139 * <code>load</code> fills the height data array with the appropriate data 140 * from the set RAW image. If the RAW image has not been set a JmeException 141 * will be thrown. 142 * 143 * @return true if the load is successfull, false otherwise. 144 */ 145 @Override load()146 public boolean load() { 147 // confirm data has been set. Redundant check... 148 if (null == stream || size <= 0) { 149 throw new RuntimeException("Must supply valid stream and " 150 + "size (> 0)"); 151 } 152 153 154 // clean up 155 if (null != heightData) { 156 unloadHeightMap(); 157 } 158 159 160 // initialize the height data attributes 161 heightData = new float[size * size]; 162 163 164 // attempt to connect to the supplied file. 165 BufferedInputStream bis = null; 166 167 168 try { 169 bis = new BufferedInputStream(stream); 170 if (format == RawHeightMap.FORMAT_16BITLE) { 171 LittleEndien dis = new LittleEndien(bis); 172 int index; 173 // read the raw file 174 for (int i = 0; i < size; i++) { 175 for (int j = 0; j < size; j++) { 176 if (swapxy) { 177 index = i + j * size; 178 } else { 179 index = (i * size) + j; 180 } 181 heightData[index] = dis.readUnsignedShort(); 182 } 183 } 184 dis.close(); 185 } else { 186 DataInputStream dis = new DataInputStream(bis); 187 // read the raw file 188 for (int i = 0; i < size; i++) { 189 for (int j = 0; j < size; j++) { 190 int index; 191 if (swapxy) { 192 index = i + j * size; 193 } else { 194 index = (i * size) + j; 195 } 196 if (format == RawHeightMap.FORMAT_16BITBE) { 197 heightData[index] = dis.readUnsignedShort(); 198 } else { 199 heightData[index] = dis.readUnsignedByte(); 200 } 201 } 202 } 203 dis.close(); 204 } 205 bis.close(); 206 } catch (IOException e1) { 207 logger.warning("Error reading height data from stream."); 208 return false; 209 } 210 return true; 211 } 212 213 /** 214 * <code>setFilename</code> sets the file to use for the RAW data. A call 215 * to <code>load</code> is required to put the changes into effect. 216 * 217 * @param filename 218 * the new file to use for the height data. 219 * @throws JmeException 220 * if the file is null or not RAW. 221 */ setFilename(String filename)222 public void setFilename(String filename) throws Exception { 223 if (null == filename) { 224 throw new Exception("Must supply valid filename."); 225 } 226 try { 227 this.stream = new FileInputStream(filename); 228 } catch (FileNotFoundException e) { 229 throw new Exception("height file not found: " + filename); 230 } 231 } 232 233 /** 234 * <code>setHeightStream</code> sets the stream to use for the RAW data. A call 235 * to <code>load</code> is required to put the changes into effect. 236 * 237 * @param stream 238 * the new stream to use for the height data. 239 * @throws JmeException 240 * if the stream is null or not RAW. 241 */ setHeightStream(InputStream stream)242 public void setHeightStream(InputStream stream) throws Exception { 243 if (null == stream) { 244 throw new Exception("Must supply valid stream."); 245 } 246 this.stream = stream; 247 } 248 } 249