/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * 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 the 'Xith3D Project Group' 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) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.terrain; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.ByteOrder; import java.util.zip.GZIPInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; /** * @author Mathias 'cylab' Henze */ public class HF2Map { public static enum Compression{ NONE, GZIP }; private String fileId; private int version; private long width; private long height; private int tileSize; private float precision; private float horizontalScale; private float min; private float max; private float[][] data; public float[][] getData() { return data; } public String getFileId() { return fileId; } public long getHeight() { return height; } public float getHorizontalScale() { return horizontalScale; } public float getPrecision() { return precision; } public int getTileSize() { return tileSize; } public int getVersion() { return version; } public long getWidth() { return width; } public float getMax() { return max; } public float getMin() { return min; } private void load(URL location) throws IOException { InputStream in = null; try { in = location.openStream(); final String loc = location.toString().toLowerCase(); load(in,(loc.endsWith(".hfz")||loc.endsWith(".gz"))?Compression.GZIP:Compression.NONE); } finally { if( in != null ) try { in.close(); } catch( Exception ignore ){} } } private void load(InputStream in, Compression compression) throws IOException { MemoryCacheImageInputStream stream= null; try { stream = new MemoryCacheImageInputStream(compression==Compression.NONE?in:new GZIPInputStream(in)); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // File ID pos:0 size:4 type:string // Should be �HF2� (null terminated) byte[] id= new byte[] { stream.readByte(), stream.readByte(), stream.readByte(), }; if(stream.readByte()!=0 || !"HF2".equals(fileId= new String(id,"ISO-8859-15"))) { throw new IOException("Unsupported format '"+fileId+"'!"); } // Version no. pos:4 size:2 type:unsigned short // Should be 0. version = stream.readUnsignedShort(); if(version!=0) { throw new IOException("Unsupported version '"+version+"'!"); } // Width pos:6 size:4 type:unsigned int (32bit) // Width of map in pixels. width= stream.readUnsignedInt(); // Height pos:10 size:4 type:unsigned int (32bit) // Height of map in pixels. height= stream.readUnsignedInt(); // Tile size pos:14 size:2 type:unsigned short // Size of internal map tiles (8?65535). Default is 256. tileSize= stream.readUnsignedShort(); // Vert. precis. pos:16 size:4 type:float // Precision of vertical scale, in metres. Must be greater than zero. Default is 0.01. precision= stream.readFloat(); // Horiz. scale pos:20 size:4 type:float // Horizontal pixel spacing, in metres. Must be greater than zero. Default is 1. horizontalScale= stream.readFloat(); // Ext. header length pos:24 size:4 type:unsigned int(32bit) // Length of extended header, in bytes. Zero is default. long extHeaderLength= stream.readUnsignedInt(); if((width%tileSize)>0 || (height%tileSize)>0) { throw new IOException("Currently only files are supported, which tile size is a whole number divisor of the map size."); } stream.skipBytes(extHeaderLength); stream.flush(); int htiles= (int)(width/tileSize)+((width%tileSize)>0?1:0); int vtiles= (int)(height/tileSize)+((height%tileSize)>0?1:0); data= new float[(int)width][(int)height]; min= Float.MAX_VALUE; max= Float.MIN_VALUE; for( int v = 0; v < vtiles; v++ ) { for( int h = 0; h < htiles; h++ ) { // Vert. scale pos:0 size:4 type:float // The vertical scaling of the data in the tile. float vScale = stream.readFloat(); // Vert. offset pos:4 size:4 type:float // The vertical offset of the data in the tile. float vOffset = stream.readFloat(); for( int z = 0; z < tileSize; z++ ) { // Byte depth pos:0 size:1 type:byte // The byte-depth used for difference encoding in the line. May be 1, 2 or 4. byte bDepth= stream.readByte(); // Start value pos:1 size:4 type:long // The starting value of the line long currentValue= stream.readUnsignedInt(); float value= (float)currentValue * vScale + vOffset; if(value<min) min=value; if(value>max) max=value; data[h*tileSize][v*tileSize+z] = value; for( int x = 1; x < tileSize; x++ ) { currentValue+= bDepth==4?stream.readInt():(bDepth==2?stream.readShort():stream.readByte()); value= (float)currentValue * vScale + vOffset; if(value<min) min=value; if(value>max) max=value; data[h*tileSize+x][v*tileSize+z] = value; } stream.flush(); } } } } finally { if( stream != null ) try { stream.close(); } catch( Exception ignore ){} } } public HF2Map( InputStream in, Compression compression ) throws IOException { load(in,compression); } public HF2Map( URL location ) throws IOException { load(location); } }