/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.terrain.heightmap; import com.jme3.math.FastMath; import com.jme3.util.LittleEndien; import java.io.*; import java.net.URL; import java.util.logging.Logger; /** * <code>RawHeightMap</code> creates a height map from a RAW image file. The * greyscale image denotes height based on the value of the pixel for each * point. Where pure black the lowest point and pure white denotes the highest. * * @author Mark Powell * @version $Id$ */ public class RawHeightMap extends AbstractHeightMap { private static final Logger logger = Logger.getLogger(RawHeightMap.class.getName()); /** * Format specification for 8 bit precision heightmaps */ public static final int FORMAT_8BIT = 0; /** * Format specification for 16 bit little endian heightmaps */ public static final int FORMAT_16BITLE = 1; /** * Format specification for 16 bit big endian heightmaps */ public static final int FORMAT_16BITBE = 2; private int format; private boolean swapxy; private InputStream stream; /** * Constructor creates a new <code>RawHeightMap</code> object and loads a * RAW image file to use as a height field. The greyscale image denotes the * height of the terrain, where dark is low point and bright is high point. * The values of the RAW correspond directly with the RAW values or 0 - 255. * * @param filename * the RAW file to use as the heightmap. * @param size * the size of the RAW (must be square). * @throws JmeException * if the filename is null or not RAW, and if the size is 0 or * less. */ public RawHeightMap(String filename, int size) throws Exception { this(filename, size, FORMAT_8BIT, false); } public RawHeightMap(float heightData[]) { this.heightData = heightData; this.size = (int) FastMath.sqrt(heightData.length); this.format = FORMAT_8BIT; } public RawHeightMap(String filename, int size, int format, boolean swapxy) throws Exception { // varify that filename and size are valid. if (null == filename || size <= 0) { throw new Exception("Must supply valid filename and " + "size (> 0)"); } try { setup(new FileInputStream(filename), size, format, swapxy); } catch (FileNotFoundException e) { throw new Exception("height file not found: " + filename); } } public RawHeightMap(InputStream stream, int size, int format, boolean swapxy) throws Exception { setup(stream, size, format, swapxy); } public RawHeightMap(URL resource, int size, int format, boolean swapxy) throws Exception { // varify that resource and size are valid. if (null == resource || size <= 0) { throw new Exception("Must supply valid resource and " + "size (> 0)"); } try { setup(resource.openStream(), size, format, swapxy); } catch (IOException e) { throw new Exception("Unable to open height url: " + resource); } } private void setup(InputStream stream, int size, int format, boolean swapxy) throws Exception { // varify that filename and size are valid. if (null == stream || size <= 0) { throw new Exception("Must supply valid stream and " + "size (> 0)"); } this.stream = stream; this.size = size; this.format = format; this.swapxy = swapxy; load(); } /** * <code>load</code> fills the height data array with the appropriate data * from the set RAW image. If the RAW image has not been set a JmeException * will be thrown. * * @return true if the load is successfull, false otherwise. */ @Override public boolean load() { // confirm data has been set. Redundant check... if (null == stream || size <= 0) { throw new RuntimeException("Must supply valid stream and " + "size (> 0)"); } // clean up if (null != heightData) { unloadHeightMap(); } // initialize the height data attributes heightData = new float[size * size]; // attempt to connect to the supplied file. BufferedInputStream bis = null; try { bis = new BufferedInputStream(stream); if (format == RawHeightMap.FORMAT_16BITLE) { LittleEndien dis = new LittleEndien(bis); int index; // read the raw file for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { if (swapxy) { index = i + j * size; } else { index = (i * size) + j; } heightData[index] = dis.readUnsignedShort(); } } dis.close(); } else { DataInputStream dis = new DataInputStream(bis); // read the raw file for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { int index; if (swapxy) { index = i + j * size; } else { index = (i * size) + j; } if (format == RawHeightMap.FORMAT_16BITBE) { heightData[index] = dis.readUnsignedShort(); } else { heightData[index] = dis.readUnsignedByte(); } } } dis.close(); } bis.close(); } catch (IOException e1) { logger.warning("Error reading height data from stream."); return false; } return true; } /** * <code>setFilename</code> sets the file to use for the RAW data. A call * to <code>load</code> is required to put the changes into effect. * * @param filename * the new file to use for the height data. * @throws JmeException * if the file is null or not RAW. */ public void setFilename(String filename) throws Exception { if (null == filename) { throw new Exception("Must supply valid filename."); } try { this.stream = new FileInputStream(filename); } catch (FileNotFoundException e) { throw new Exception("height file not found: " + filename); } } /** * <code>setHeightStream</code> sets the stream to use for the RAW data. A call * to <code>load</code> is required to put the changes into effect. * * @param stream * the new stream to use for the height data. * @throws JmeException * if the stream is null or not RAW. */ public void setHeightStream(InputStream stream) throws Exception { if (null == stream) { throw new Exception("Must supply valid stream."); } this.stream = stream; } }