Home | History | Annotate | Download | only in heightmap
      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 
     33 package com.jme3.terrain.heightmap;
     34 
     35 import java.nio.ByteBuffer;
     36 import com.jme3.math.ColorRGBA;
     37 import com.jme3.texture.Image;
     38 import java.nio.ShortBuffer;
     39 
     40 /**
     41  * <code>ImageBasedHeightMap</code> is a height map created from the grayscale
     42  * conversion of an image. The image used currently must have an equal height
     43  * and width, although future work could scale an incoming image to a specific
     44  * height and width.
     45  *
     46  * @author Mike Kienenberger
     47  * @version $id$
     48  */
     49 public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeightmap {
     50 
     51 
     52     protected Image colorImage;
     53 
     54 
     55     public void setImage(Image image) {
     56         this.colorImage = image;
     57     }
     58 
     59     /**
     60      * Creates a HeightMap from an Image. The image will be converted to
     61      * grayscale, and the grayscale values will be used to generate the height
     62      * map. White is highest point while black is lowest point.
     63      *
     64      * Currently, the Image used must be square (width == height), but future
     65      * work could rescale the image.
     66      *
     67      * @param colorImage
     68      *            Image to map to the height map.
     69      */
     70     public ImageBasedHeightMap(Image colorImage) {
     71         this.colorImage = colorImage;
     72     }
     73 
     74     public ImageBasedHeightMap(Image colorImage, float heightScale) {
     75     	this.colorImage = colorImage;
     76         this.heightScale = heightScale;
     77     }
     78 
     79     /**
     80      * Loads the image data from top left to bottom right
     81      */
     82     public boolean load() {
     83         return load(false, false);
     84     }
     85 
     86     /**
     87      * Get the grayscale value, or override in your own sub-classes
     88      */
     89     protected float calculateHeight(float red, float green, float blue) {
     90         return (float) (0.299 * red + 0.587 * green + 0.114 * blue);
     91     }
     92 
     93     public boolean load(boolean flipX, boolean flipY) {
     94 
     95         int imageWidth = colorImage.getWidth();
     96         int imageHeight = colorImage.getHeight();
     97 
     98         if (imageWidth != imageHeight)
     99                 throw new RuntimeException("imageWidth: " + imageWidth
    100                         + " != imageHeight: " + imageHeight);
    101 
    102         size = imageWidth;
    103 
    104         ByteBuffer buf = colorImage.getData(0);
    105 
    106         heightData = new float[(imageWidth * imageHeight)];
    107 
    108         ColorRGBA colorStore = new ColorRGBA();
    109 
    110         int index = 0;
    111         if (flipY) {
    112             for (int h = 0; h < imageHeight; ++h) {
    113                 if (flipX) {
    114                     for (int w = imageWidth - 1; w >= 0; --w) {
    115                         int baseIndex = (h * imageWidth)+ w;
    116                         heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
    117                     }
    118                 } else {
    119                     for (int w = 0; w < imageWidth; ++w) {
    120                         int baseIndex = (h * imageWidth)+ w;
    121                         heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
    122                     }
    123                 }
    124             }
    125         } else {
    126             for (int h = imageHeight - 1; h >= 0; --h) {
    127                 if (flipX) {
    128                     for (int w = imageWidth - 1; w >= 0; --w) {
    129                         int baseIndex = (h * imageWidth)+ w;
    130                         heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
    131                     }
    132                 } else {
    133                     for (int w = 0; w < imageWidth; ++w) {
    134                         int baseIndex = (h * imageWidth)+ w;
    135                         heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
    136                     }
    137                 }
    138             }
    139         }
    140 
    141         return true;
    142     }
    143 
    144     protected float getHeightAtPostion(ByteBuffer buf, Image image, int position, ColorRGBA store) {
    145         switch (image.getFormat()){
    146             case RGBA8:
    147                 buf.position( position * 4 );
    148                 store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()));
    149                 return calculateHeight(store.r, store.g, store.b);
    150             case ABGR8:
    151                 buf.position( position * 4 );
    152                 float a = byte2float(buf.get());
    153                 float b = byte2float(buf.get());
    154                 float g = byte2float(buf.get());
    155                 float r = byte2float(buf.get());
    156                 store.set(r,g,b,a);
    157                 return calculateHeight(store.r, store.g, store.b);
    158             case RGB8:
    159                 buf.position( position * 3 );
    160                 store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), 1);
    161                 return calculateHeight(store.r, store.g, store.b);
    162             case Luminance8:
    163                 buf.position( position );
    164                 return byte2float(buf.get())*255*heightScale;
    165             case Luminance16:
    166                 ShortBuffer sbuf = buf.asShortBuffer();
    167                 sbuf.position( position );
    168                 return (sbuf.get() & 0xFFFF) / 65535f * 255f * heightScale;
    169             default:
    170                 throw new UnsupportedOperationException("Image format: "+image.getFormat());
    171         }
    172     }
    173 
    174     private float byte2float(byte b){
    175         return ((float)(b & 0xFF)) / 255f;
    176     }
    177 }