|
|
|
@ -32,12 +32,10 @@ |
|
|
|
|
|
|
|
|
|
package com.jme3.terrain.heightmap; |
|
|
|
|
|
|
|
|
|
import java.awt.Graphics2D; |
|
|
|
|
import java.awt.Image; |
|
|
|
|
import java.awt.image.BufferedImage; |
|
|
|
|
import java.awt.image.ColorModel; |
|
|
|
|
import java.awt.image.PixelGrabber; |
|
|
|
|
import java.awt.image.WritableRaster; |
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
import com.jme3.math.ColorRGBA; |
|
|
|
|
import com.jme3.texture.Image; |
|
|
|
|
import java.nio.ShortBuffer; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <code>ImageBasedHeightMap</code> is a height map created from the grayscale |
|
|
|
@ -49,102 +47,15 @@ import java.awt.image.WritableRaster; |
|
|
|
|
* @version $id$ |
|
|
|
|
*/ |
|
|
|
|
public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeightmap { |
|
|
|
|
|
|
|
|
|
static protected class ImageConverter { |
|
|
|
|
|
|
|
|
|
// Posted by DrLaszloJamf to Java Technology Forums
|
|
|
|
|
//
|
|
|
|
|
// Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
|
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
|
// modification, are permitted provided that the following conditions
|
|
|
|
|
// are met:
|
|
|
|
|
//
|
|
|
|
|
// Redistribution of source code must retain the above copyright notice,
|
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
|
//
|
|
|
|
|
// Redistribution 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 Sun Microsystems, Inc. or the names of
|
|
|
|
|
// contributors may be used to endorse or promote products derived from
|
|
|
|
|
// this software without specific prior written permission.
|
|
|
|
|
//
|
|
|
|
|
// This software is provided "AS IS," without a warranty of any kind.
|
|
|
|
|
// ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
|
|
|
|
|
// INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
|
|
|
|
|
// PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
|
|
|
|
|
// MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
|
|
|
|
|
// ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
|
|
|
|
|
// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
|
|
|
|
|
// OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
|
|
|
|
|
// FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
|
|
|
|
|
// DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
|
|
|
|
|
// ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
|
|
|
|
|
// SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// You acknowledge that this software is not designed, licensed or
|
|
|
|
|
// intended for use in the design, construction, operation or
|
|
|
|
|
// maintenance of any nuclear facility.
|
|
|
|
|
|
|
|
|
|
// preserves image's colormodel. Assumes image is loaded
|
|
|
|
|
public static BufferedImage toBufferedImage(Image image) { |
|
|
|
|
if (image instanceof BufferedImage) return (BufferedImage) image; |
|
|
|
|
ColorModel cm = getColorModel(image); |
|
|
|
|
int width = image.getWidth(null); |
|
|
|
|
int height = image.getHeight(null); |
|
|
|
|
return copy(createBufferedImage(cm, width, height), image); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static BufferedImage toBufferedImage(Image image, int type) { |
|
|
|
|
if (image instanceof BufferedImage |
|
|
|
|
&& ((BufferedImage) image).getType() == type) |
|
|
|
|
return (BufferedImage) image; |
|
|
|
|
int width = image.getWidth(null); |
|
|
|
|
int height = image.getHeight(null); |
|
|
|
|
return copy(new BufferedImage(width, height, type), image); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns target. Assumes source is loaded
|
|
|
|
|
public static BufferedImage copy(BufferedImage target, Image source) { |
|
|
|
|
Graphics2D g = target.createGraphics(); |
|
|
|
|
g.drawImage(source, 0, 0, null); |
|
|
|
|
g.dispose(); |
|
|
|
|
return target; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static ColorModel getColorModel(Image image) { |
|
|
|
|
try { |
|
|
|
|
PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); |
|
|
|
|
pg.grabPixels(); |
|
|
|
|
return pg.getColorModel(); |
|
|
|
|
} catch (InterruptedException e) { |
|
|
|
|
throw new RuntimeException("Unexpected interruption", e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static BufferedImage createBufferedImage(ColorModel cm, int w, |
|
|
|
|
int h) { |
|
|
|
|
WritableRaster raster = cm.createCompatibleWritableRaster(w, h); |
|
|
|
|
boolean isRasterPremultiplied = cm.isAlphaPremultiplied(); |
|
|
|
|
return new BufferedImage(cm, raster, isRasterPremultiplied, null); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected Image colorImage; |
|
|
|
|
protected float dampen = 1.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setImage(Image image) { |
|
|
|
|
this.colorImage = image; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public int getSupportedImageType() { |
|
|
|
|
return BufferedImage.TYPE_3BYTE_BGR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates a HeightMap from an Image. The image will be converted to |
|
|
|
|
* grayscale, and the grayscale values will be used to generate the height |
|
|
|
@ -157,13 +68,12 @@ public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeigh |
|
|
|
|
* Image to map to the height map. |
|
|
|
|
*/ |
|
|
|
|
public ImageBasedHeightMap(Image colorImage) { |
|
|
|
|
this(colorImage, 1.0f); |
|
|
|
|
this.colorImage = colorImage; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public ImageBasedHeightMap(Image colorImage, float dampen) { |
|
|
|
|
super(); |
|
|
|
|
public ImageBasedHeightMap(Image colorImage, float heightScale) { |
|
|
|
|
this.colorImage = colorImage; |
|
|
|
|
this.dampen = dampen; |
|
|
|
|
this.heightScale = heightScale; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -177,19 +87,13 @@ public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeigh |
|
|
|
|
* Get the grayscale value, or override in your own sub-classes |
|
|
|
|
*/ |
|
|
|
|
protected float calculateHeight(float red, float green, float blue) { |
|
|
|
|
return (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen); |
|
|
|
|
return (float) (0.299 * red + 0.587 * green + 0.114 * blue); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public boolean load(boolean flipX, boolean flipY) { |
|
|
|
|
|
|
|
|
|
// FUTURE: Rescale image if not square?
|
|
|
|
|
BufferedImage colorBufferedImage = ImageConverter.toBufferedImage( |
|
|
|
|
colorImage, BufferedImage.TYPE_3BYTE_BGR); |
|
|
|
|
|
|
|
|
|
boolean hasAlpha = colorBufferedImage.getColorModel().hasAlpha(); |
|
|
|
|
|
|
|
|
|
int imageWidth = colorBufferedImage.getWidth(); |
|
|
|
|
int imageHeight = colorBufferedImage.getHeight(); |
|
|
|
|
int imageWidth = colorImage.getWidth(); |
|
|
|
|
int imageHeight = colorImage.getHeight(); |
|
|
|
|
|
|
|
|
|
if (imageWidth != imageHeight) |
|
|
|
|
throw new RuntimeException("imageWidth: " + imageWidth |
|
|
|
@ -197,58 +101,24 @@ public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeigh |
|
|
|
|
|
|
|
|
|
size = imageWidth; |
|
|
|
|
|
|
|
|
|
byte data[] = (byte[]) colorBufferedImage.getRaster().getDataElements( |
|
|
|
|
0, 0, imageWidth, imageHeight, null); |
|
|
|
|
|
|
|
|
|
int bytesPerPixel = 3; |
|
|
|
|
int blueBase = 0; |
|
|
|
|
if (hasAlpha) { |
|
|
|
|
bytesPerPixel = 4; |
|
|
|
|
blueBase = 1; |
|
|
|
|
} |
|
|
|
|
ByteBuffer buf = colorImage.getData(0); |
|
|
|
|
|
|
|
|
|
heightData = new float[(imageWidth * imageHeight)]; |
|
|
|
|
|
|
|
|
|
int startW = 0; |
|
|
|
|
int endW = imageWidth-1; |
|
|
|
|
if (flipX) { |
|
|
|
|
startW = imageWidth-1; |
|
|
|
|
endW = 0; |
|
|
|
|
} |
|
|
|
|
int startH = imageHeight-1; |
|
|
|
|
int endH = 0; |
|
|
|
|
if (flipY) { |
|
|
|
|
startH = 0; |
|
|
|
|
endH = imageHeight-1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ColorRGBA colorStore = new ColorRGBA(); |
|
|
|
|
|
|
|
|
|
int index = 0; |
|
|
|
|
if (flipY) { |
|
|
|
|
for (int h = 0; h < imageHeight; ++h) { |
|
|
|
|
if (flipX) { |
|
|
|
|
for (int w = imageWidth - 1; w >= 0; --w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
heightData[index++] = calculateHeight(red,green,blue); |
|
|
|
|
int baseIndex = (h * imageWidth)+ w; |
|
|
|
|
heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (int w = 0; w < imageWidth; ++w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
heightData[index++] = calculateHeight(red,green,blue); |
|
|
|
|
|
|
|
|
|
int baseIndex = (h * imageWidth)+ w; |
|
|
|
|
heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -256,103 +126,52 @@ public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeigh |
|
|
|
|
for (int h = imageHeight - 1; h >= 0; --h) { |
|
|
|
|
if (flipX) { |
|
|
|
|
for (int w = imageWidth - 1; w >= 0; --w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
heightData[index++] = calculateHeight(red,green,blue); |
|
|
|
|
int baseIndex = (h * imageWidth)+ w; |
|
|
|
|
heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (int w = 0; w < imageWidth; ++w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
heightData[index++] = calculateHeight(red,green,blue); |
|
|
|
|
int baseIndex = (h * imageWidth)+ w; |
|
|
|
|
heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*int index = 0; |
|
|
|
|
if (flipY) { |
|
|
|
|
for (int h = 0; h < imageHeight; ++h) { |
|
|
|
|
if (flipX) { |
|
|
|
|
for (int w = imageWidth-1; w >= 0; --w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
|
|
|
|
|
float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen); |
|
|
|
|
|
|
|
|
|
heightData[index++] = grayscale; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (int w = 0; w < imageWidth; ++w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
|
|
|
|
|
float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen); |
|
|
|
|
|
|
|
|
|
heightData[index++] = grayscale; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (int h = imageHeight-1; h >= 0; --h) { |
|
|
|
|
if (flipX) { |
|
|
|
|
for (int w = imageWidth-1; w >= 0; --w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
|
|
|
|
|
float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen); |
|
|
|
|
|
|
|
|
|
heightData[index++] = grayscale; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (int w = 0; w < imageWidth; ++w) { |
|
|
|
|
int baseIndex = (h * imageWidth * bytesPerPixel) |
|
|
|
|
+ (w * bytesPerPixel) + blueBase; |
|
|
|
|
float blue = data[baseIndex] >= 0 ? data[baseIndex] |
|
|
|
|
: (256 + (data[baseIndex])); |
|
|
|
|
float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1] |
|
|
|
|
: (256 + (data[baseIndex + 1])); |
|
|
|
|
float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2] |
|
|
|
|
: (256 + (data[baseIndex + 2])); |
|
|
|
|
|
|
|
|
|
float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen); |
|
|
|
|
|
|
|
|
|
heightData[index++] = grayscale; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected float getHeightAtPostion(ByteBuffer buf, Image image, int position, ColorRGBA store) { |
|
|
|
|
switch (image.getFormat()){ |
|
|
|
|
case RGBA8: |
|
|
|
|
buf.position( position * 4 ); |
|
|
|
|
store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get())); |
|
|
|
|
return calculateHeight(store.r, store.g, store.b); |
|
|
|
|
case ABGR8: |
|
|
|
|
buf.position( position * 4 ); |
|
|
|
|
float a = byte2float(buf.get()); |
|
|
|
|
float b = byte2float(buf.get()); |
|
|
|
|
float g = byte2float(buf.get()); |
|
|
|
|
float r = byte2float(buf.get()); |
|
|
|
|
store.set(r,g,b,a); |
|
|
|
|
return calculateHeight(store.r, store.g, store.b); |
|
|
|
|
case RGB8: |
|
|
|
|
buf.position( position * 3 ); |
|
|
|
|
store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), 1); |
|
|
|
|
return calculateHeight(store.r, store.g, store.b); |
|
|
|
|
case Luminance8: |
|
|
|
|
buf.position( position ); |
|
|
|
|
return byte2float(buf.get())*255*heightScale; |
|
|
|
|
case Luminance16: |
|
|
|
|
ShortBuffer sbuf = buf.asShortBuffer(); |
|
|
|
|
sbuf.position( position ); |
|
|
|
|
return (sbuf.get() & 0xFFFF) / 65535f * 255f * heightScale; |
|
|
|
|
default: |
|
|
|
|
throw new UnsupportedOperationException("Image format: "+image.getFormat()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private float byte2float(byte b){ |
|
|
|
|
return ((float)(b & 0xFF)) / 255f; |
|
|
|
|
} |
|
|
|
|
} |