159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/*
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Copyright (c) 2009-2010 jMonkeyEngine
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * All rights reserved.
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Redistribution and use in source and binary forms, with or without
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * modification, are permitted provided that the following conditions are
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * met:
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions of source code must retain the above copyright
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   notice, this list of conditions and the following disclaimer.
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions in binary form must reproduce the above copyright
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   notice, this list of conditions and the following disclaimer in the
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   documentation and/or other materials provided with the distribution.
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   may be used to endorse or promote products derived from this software
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   without specific prior written permission.
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.terrain.heightmap;
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.DataOutputStream;
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.FileNotFoundException;
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.FileOutputStream;
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException;
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level;
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger;
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <code>AbstractHeightMap</code> provides a base implementation of height
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * data for terrain rendering. The loading of the data is dependent on the
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * subclass. The abstract implementation provides a means to retrieve the height
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * data and to save it.
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * It is the general contract that any subclass provide a means of editing
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * required attributes and calling <code>load</code> again to recreate a
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * heightfield with these new parameters.
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Mark Powell
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @version $Id: AbstractHeightMap.java 4133 2009-03-19 20:40:11Z blaine.dev $
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic abstract class AbstractHeightMap implements HeightMap {
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private static final Logger logger = Logger.getLogger(AbstractHeightMap.class.getName());
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /** Height data information. */
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float[] heightData = null;
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /** The size of the height map's width. */
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int size = 0;
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /** Allows scaling the Y height of the map. */
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float heightScale = 1.0f;
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /** The filter is used to erode the terrain. */
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float filter = 0.5f;
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /** The range used to normalize terrain */
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public static float NORMALIZE_RANGE = 255f;
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>unloadHeightMap</code> clears the data of the height map. This
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * insures it is ready for reloading.
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void unloadHeightMap() {
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        heightData = null;
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>setHeightScale</code> sets the scale of the height values.
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Typically, the height is a little too extreme and should be scaled to a
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * smaller value (i.e. 0.25), to produce cleaner slopes.
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param scale
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the scale to multiply height values by.
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setHeightScale(float scale) {
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        heightScale = scale;
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>setHeightAtPoint</code> sets the height value for a given
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * coordinate. It is recommended that the height value be within the 0 - 255
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * range.
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param height
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the new height for the coordinate.
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the x (east/west) coordinate.
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the z (north/south) coordinate.
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setHeightAtPoint(float height, int x, int z) {
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        heightData[x + (z * size)] = height;
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>setSize</code> sets the size of the terrain where the area is
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * size x size.
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param size
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the new size of the terrain.
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws Exception
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws JmeException
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *             if the size is less than or equal to zero.
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setSize(int size) throws Exception {
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (size <= 0) {
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new Exception("size must be greater than zero.");
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.size = size;
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>setFilter</code> sets the erosion value for the filter. This
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * value must be between 0 and 1, where 0.2 - 0.4 produces arguably the best
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * results.
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param filter
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the erosion value.
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws Exception
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws JmeException
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *             if filter is less than 0 or greater than 1.
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setMagnificationFilter(float filter) throws Exception {
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (filter < 0 || filter >= 1) {
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new Exception("filter must be between 0 and 1");
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.filter = filter;
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>getTrueHeightAtPoint</code> returns the non-scaled value at the
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * point provided.
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the x (east/west) coordinate.
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the z (north/south) coordinate.
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the value at (x,z).
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getTrueHeightAtPoint(int x, int z) {
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //logger.info( heightData[x + (z*size)]);
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return heightData[x + (z * size)];
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>getScaledHeightAtPoint</code> returns the scaled value at the
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * point provided.
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the x (east/west) coordinate.
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the z (north/south) coordinate.
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the scaled value at (x, z).
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getScaledHeightAtPoint(int x, int z) {
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return ((heightData[x + (z * size)]) * heightScale);
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>getInterpolatedHeight</code> returns the height of a point that
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * does not fall directly on the height posts.
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x
17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the x coordinate of the point.
17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z
17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the y coordinate of the point.
17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the interpolated height at this point.
17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getInterpolatedHeight(float x, float z) {
18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float low, highX, highZ;
18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float intX, intZ;
18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float interpolation;
18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        low = getScaledHeightAtPoint((int) x, (int) z);
18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x + 1 >= size) {
18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return low;
18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        highX = getScaledHeightAtPoint((int) x + 1, (int) z);
19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        interpolation = x - (int) x;
19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        intX = ((highX - low) * interpolation) + low;
19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (z + 1 >= size) {
19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return low;
19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        highZ = getScaledHeightAtPoint((int) x, (int) z + 1);
20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        interpolation = z - (int) z;
20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        intZ = ((highZ - low) * interpolation) + low;
20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return ((intX + intZ) / 2);
20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>getHeightMap</code> returns the entire grid of height data.
21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the grid of height data.
21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] getHeightMap() {
21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return heightData;
21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Build a new array of height data with the scaled values.
21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] getScaledHeightMap() {
22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] hm = new float[heightData.length];
22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i=0; i<heightData.length; i++) {
22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            hm[i] = heightScale * heightData[i];
22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return hm;
22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>getSize</code> returns the size of one side the height map. Where
23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * the area of the height map is size x size.
23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the size of a single side.
23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getSize() {
23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return size;
23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>save</code> will save the heightmap data into a new RAW file
24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * denoted by the supplied filename.
24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param filename
24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the file name to save the current data as.
24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return true if the save was successful, false otherwise.
24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws Exception
24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @throws JmeException
24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *             if filename is null.
25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean save(String filename) throws Exception {
25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (null == filename) {
25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new Exception("Filename must not be null");
25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //open the streams and send the height data to the file.
25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        try {
25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            FileOutputStream fos = new FileOutputStream(filename);
25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            DataOutputStream dos = new DataOutputStream(fos);
25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = 0; i < size; i++) {
26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int j = 0; j < size; j++) {
26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    dos.write((int) heightData[j + (i * size)]);
26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fos.close();
26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            dos.close();
26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (FileNotFoundException e) {
26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            logger.log(Level.WARNING, "Error opening file {0}", filename);
26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return false;
27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (IOException e) {
27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            logger.log(Level.WARNING, "Error writing to file {0}", filename);
27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return false;
27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        logger.log(Level.INFO, "Saved terrain to {0}", filename);
27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return true;
27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>normalizeTerrain</code> takes the current terrain data and
28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * converts it to values between 0 and <code>value</code>.
28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param value
28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the value to normalize to.
28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void normalizeTerrain(float value) {
28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float currentMin, currentMax;
28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float height;
28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        currentMin = heightData[0];
29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        currentMax = heightData[0];
29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //find the min/max values of the height fTemptemptempBuffer
29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < size; i++) {
29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 0; j < size; j++) {
29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (heightData[i + j * size] > currentMax) {
29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    currentMax = heightData[i + j * size];
29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (heightData[i + j * size] < currentMin) {
29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    currentMin = heightData[i + j * size];
30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //find the range of the altitude
30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (currentMax <= currentMin) {
30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        height = currentMax - currentMin;
31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //scale the values to a range of 0-255
31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < size; i++) {
31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 0; j < size; j++) {
31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[i + j * size] = ((heightData[i + j * size] - currentMin) / height) * value;
31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Find the minimum and maximum height values.
32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return a float array with two value: min height, max height
32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] findMinMaxHeights() {
32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] minmax = new float[2];
32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float currentMin, currentMax;
32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        currentMin = heightData[0];
32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        currentMax = heightData[0];
32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < heightData.length; i++) {
33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (heightData[i] > currentMax) {
33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                currentMax = heightData[i];
33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (heightData[i] < currentMin) {
33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                currentMin = heightData[i];
33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        minmax[0] = currentMin;
33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        minmax[1] = currentMax;
33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return minmax;
34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>erodeTerrain</code> is a convenience method that applies the FIR
34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * filter to a given height map. This simulates water errosion.
34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @see setFilter
34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void erodeTerrain() {
34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //erode left to right
35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float v;
35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < size; i++) {
35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            v = heightData[i];
35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 1; j < size; j++) {
35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                v = heightData[i + j * size];
35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //erode right to left
36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = size - 1; i >= 0; i--) {
36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            v = heightData[i];
36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 0; j < size; j++) {
36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                v = heightData[i + j * size];
36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //erodeBand(tempBuffer[size * i + size - 1], -1);
36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //erode top to bottom
37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < size; i++) {
37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            v = heightData[i];
37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 0; j < size; j++) {
37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                v = heightData[i + j * size];
37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //erode from bottom to top
38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = size - 1; i >= 0; i--) {
38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            v = heightData[i];
38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = 0; j < size; j++) {
38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                v = heightData[i + j * size];
38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Flattens out the valleys. The flatten algorithm makes the valleys more
39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * prominent while keeping the hills mostly intact. This effect is based on
39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * what happens when values below one are squared. The terrain will be
39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * normalized between 0 and 1 for this function to work.
39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param flattening
39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *            the power of flattening applied, 1 means none
39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void flatten(byte flattening) {
39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // If flattening is one we can skip the calculations
40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // since it wouldn't change anything. (e.g. 2 power 1 = 2)
40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (flattening <= 1) {
40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] minmax = findMinMaxHeights();
40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normalizeTerrain(1f);
40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int x = 0; x < size; x++) {
41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int y = 0; y < size; y++) {
41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float flat = 1.0f;
41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float original = heightData[x + y * size];
41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // Flatten as many times as desired;
41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int i = 0; i < flattening; i++) {
41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    flat *= original;
41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[x + y * size] = flat;
41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // re-normalize back to its oraginal height range
42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float height = minmax[1] - minmax[0];
42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normalizeTerrain(height);
42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Smooth the terrain. For each node, its 8 neighbors heights
42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * are averaged and will participate in the  node new height
43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * by a factor <code>np</code> between 0 and 1
43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * You must first load() the heightmap data before this will have any effect.
43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param np
43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          The factor to what extend the neighbors average has an influence.
43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          Value of 0 will ignore neighbors (no smoothing)
43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          Value of 1 will ignore the node old height.
43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void smooth(float np) {
44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        smooth(np, 1);
44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Smooth the terrain. For each node, its X(determined by radius) neighbors heights
44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * are averaged and will participate in the  node new height
44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * by a factor <code>np</code> between 0 and 1
44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * You must first load() the heightmap data before this will have any effect.
44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param np
45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          The factor to what extend the neighbors average has an influence.
45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          Value of 0 will ignore neighbors (no smoothing)
45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          Value of 1 will ignore the node old height.
45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void smooth(float np, int radius) {
45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (np < 0 || np > 1) {
45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (radius == 0)
46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            radius = 1;
46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int x = 0; x < size; x++) {
46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int y = 0; y < size; y++) {
46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int neighNumber = 0;
46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float neighAverage = 0;
46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int rx = -radius; rx <= radius; rx++) {
46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    for (int ry = -radius; ry <= radius; ry++) {
46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (x+rx < 0 || x+rx >= size) {
46959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        continue;
47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (y+ry < 0 || y+ry >= size) {
47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            continue;
47359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
47459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        neighNumber++;
47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        neighAverage += heightData[(x+rx) + (y+ry) * size];
47659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
47759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
47859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                neighAverage /= neighNumber;
48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float cp = 1 - np;
48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                heightData[x + y * size] = neighAverage * np + heightData[x + y * size] * cp;
48259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
48559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
486