/**
* 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 javax.imageio.ImageIO;
import java.net.URL;
import java.io.IOException;
import java.io.InputStream;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import org.openmali.vecmath2.Vector3f;
/**
* @author Mathias 'cylab' Henze
*/
public class HeightMapSampler implements GridSampler
{
public enum Type
{
RAW_8, RAW_16
}
private float[][] map;
private int sDim;
private int tDim;
private float offset = 0;
private float scale = 1;
private float s1 = 0.0f;
private float t1 = 0.0f;
private float s2 = 1.0f;
private float t2 = 1.0f;
public HeightMapSampler( URL resource ) throws IOException
{
String loc = resource.toString();
if( loc.endsWith( ".hfz" ) || loc.endsWith( ".gz" ) )
{
HF2Map hf = new HF2Map( resource );
sDim = (int) hf.getWidth();
tDim = (int) hf.getHeight();
map = hf.getData();
offset = hf.getMin();
scale = 1f / (hf.getMax() - hf.getMin());
}
else
{
BufferedImage image = ImageIO.read( resource );
sDim = image.getWidth( null );
tDim = image.getHeight( null );
PixelGrabber grabber = new PixelGrabber( image, 0, 0, sDim, tDim, true );
try
{
grabber.grabPixels( 0 );
}
catch( InterruptedException e )
{
e.printStackTrace();
}
int[] source = (int[]) grabber.getPixels();
float[][] result = new float[sDim][tDim];
int i = 0;
for( int t = tDim - 1; t >= 0; t-- )
{
for( int s = 0; s < sDim; s++ )
{
int data = source[i++];
result[s][t] = (float) (((data & 0x00ff0000) >> 16) + ((data & 0xff00) >> 8) + (data & 0xff)) / 3 / 255f;
}
}
this.map = result;
}
}
public HeightMapSampler( URL resource, int sDim, int tDim ) throws IOException
{
this( resource, sDim, tDim, Type.RAW_8 );
}
public HeightMapSampler( URL resource, int sDim, int tDim, Type type ) throws IOException
{
this.sDim = sDim;
this.tDim = tDim;
InputStream in = null;
float[][] result = new float[sDim][tDim];
try
{
in = resource.openStream();
for( int t = tDim - 1; t >= 0; t-- )
{
for( int s = 0; s < sDim; s++ )
{
int highbyte = 0;
int lowbyte = 0;
if( type == Type.RAW_16 )
{
lowbyte = in.read();
if( lowbyte == -1 )
{
throw (new IOException( "EOF" ));
}
}
highbyte = in.read();
if( highbyte == -1 )
{
throw (new IOException( "EOF" ));
}
result[s][t] = ((float) (((highbyte & 0xff) << 8) + (lowbyte & 0xff))) / 65535f;
}
}
}
finally
{
try
{
if( in != null )
{
in.close();
}
}
catch( Exception ignore )
{
}
}
this.map = result;
}
public HeightMapSampler( float[][] map)
{
this.sDim = map.length;
this.tDim = map[0].length;
this.map = map;
}
public HeightMapSampler(int sDim, int tDim)
{
this.sDim = sDim;
this.tDim = tDim;
this.map = new float[sDim][tDim];
}
public HeightMapSampler( GridResourceSpec<GridSampler> spec ) throws IOException
{
this( spec.getLocations()[0] );
s1 = spec.getS1();
s2 = spec.getS2();
t1 = spec.getT1();
t2 = spec.getT2();
float min = spec.getMin();
float max = spec.getMax();
if( min < max )
{
offset = min;
scale = 1f / (max - min);
}
}
public void release()
{
}
public float sampleHeight( float s, float t )
{
s = (1f / (s2 - s1)) * (s - s1);
t = (1f / (t2 - t1)) * (t - t1);
int sIndex = Math.max( 0, Math.min( (int) (s * sDim), sDim - 1 ) );
int tIndex = Math.max( 0, Math.min( (int) (t * tDim), tDim - 1 ) );
float sr = s * sDim - sIndex;
float tr = t * tDim - tIndex;
float y = 0;
// if we are not at the edges of the map, interpolate the height values
if( sIndex < sDim - 1 && tIndex < tDim - 1 )
{
y = map[sIndex][tIndex] * (1 - sr) * (1 - tr);
y += map[sIndex + 1][tIndex] * sr * (1 - tr);
y += map[sIndex][tIndex + 1] * (1 - sr) * tr;
y += map[sIndex + 1][tIndex + 1] * sr * tr;
}
// at the edges of the map, just take the absolute value.
else
{
y = map[sIndex][tIndex];
}
return (Math.min( (y - offset) * scale, 1 ));
}
/**
* Returns the backing two dimensional float array for direct manipulation.
* @return The backing two dimensional float array
*/
public float[][] getMap()
{
return map;
}
public int getSDim()
{
return sDim;
}
public int getTDim()
{
return tDim;
}
public float getOffset()
{
return offset;
}
public void setOffset(float offset)
{
this.offset = offset;
}
public float getScale()
{
return scale;
}
public void setScale(float scale)
{
this.scale = scale;
}
public float getS1()
{
return s1;
}
public void setS1(float s1)
{
this.s1 = s1;
}
public float getS2()
{
return s2;
}
public void setS2(float s2)
{
this.s2 = s2;
}
public float getT1()
{
return t1;
}
public void setT1(float t1)
{
this.t1 = t1;
}
public float getT2()
{
return t2;
}
public void setT2(float t2)
{
this.t2 = t2;
}
public Vector3f sampleBinormal( float s, float t )
{
return new Vector3f(1f,0,0);
}
public Vector3f sampleNormal( float s, float t )
{
return new Vector3f(0,1f,0);
}
public Vector3f sampleTangent( float s, float t )
{
return new Vector3f(0,0,1f);
}
}