• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2012 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 package com.jme3.terrain.heightmap;
33 
34 import java.util.Random;
35 import java.util.logging.Logger;
36 
37 /**
38  * <code>HillHeightMap</code> generates a height map base on the Hill
39  * Algorithm. Terrain is generatd by growing hills of random size and height at
40  * random points in the heightmap. The terrain is then normalized and valleys
41  * can be flattened.
42  *
43  * @author Frederik Blthoff
44  * @see <a href="http://www.robot-frog.com/3d/hills/hill.html">Hill Algorithm</a>
45  */
46 public class HillHeightMap extends AbstractHeightMap {
47 
48     private static final Logger logger = Logger.getLogger(HillHeightMap.class.getName());
49     private int iterations; // how many hills to generate
50     private float minRadius; // the minimum size of a hill radius
51     private float maxRadius; // the maximum size of a hill radius
52     private long seed; // the seed for the random number generator
53 
54     /**
55      * Constructor sets the attributes of the hill system and generates the
56      * height map.
57      *
58      * @param size
59      *            size the size of the terrain to be generated
60      * @param iterations
61      *            the number of hills to grow
62      * @param minRadius
63      *            the minimum radius of a hill
64      * @param maxRadius
65      *            the maximum radius of a hill
66      * @param seed
67      *            the seed to generate the same heightmap again
68      * @throws Exception
69      * @throws JmeException
70      *             if size of the terrain is not greater that zero, or number of
71      *             iterations is not greater that zero
72      */
HillHeightMap(int size, int iterations, float minRadius, float maxRadius, long seed)73     public HillHeightMap(int size, int iterations, float minRadius,
74             float maxRadius, long seed) throws Exception {
75         if (size <= 0 || iterations <= 0 || minRadius <= 0 || maxRadius <= 0
76                 || minRadius >= maxRadius) {
77             throw new Exception(
78                     "Either size of the terrain is not greater that zero, "
79                     + "or number of iterations is not greater that zero, "
80                     + "or minimum or maximum radius are not greater than zero, "
81                     + "or minimum radius is greater than maximum radius, "
82                     + "or power of flattening is below one");
83         }
84         logger.info("Contructing hill heightmap using seed: " + seed);
85         this.size = size;
86         this.seed = seed;
87         this.iterations = iterations;
88         this.minRadius = minRadius;
89         this.maxRadius = maxRadius;
90 
91         load();
92     }
93 
94     /**
95      * Constructor sets the attributes of the hill system and generates the
96      * height map by using a random seed.
97      *
98      * @param size
99      *            size the size of the terrain to be generated
100      * @param iterations
101      *            the number of hills to grow
102      * @param minRadius
103      *            the minimum radius of a hill
104      * @param maxRadius
105      *            the maximum radius of a hill
106      * @throws Exception
107      * @throws JmeException
108      *             if size of the terrain is not greater that zero, or number of
109      *             iterations is not greater that zero
110      */
HillHeightMap(int size, int iterations, float minRadius, float maxRadius)111     public HillHeightMap(int size, int iterations, float minRadius,
112             float maxRadius) throws Exception {
113         this(size, iterations, minRadius, maxRadius, new Random().nextLong());
114     }
115 
116     /*
117      * Generates a heightmap using the Hill Algorithm and the attributes set by
118      * the constructor or the setters.
119      */
load()120     public boolean load() {
121         // clean up data if needed.
122         if (null != heightData) {
123             unloadHeightMap();
124         }
125         heightData = new float[size * size];
126         float[][] tempBuffer = new float[size][size];
127         Random random = new Random(seed);
128 
129         // Add the hills
130         for (int i = 0; i < iterations; i++) {
131             addHill(tempBuffer, random);
132         }
133 
134         // transfer temporary buffer to final heightmap
135         for (int i = 0; i < size; i++) {
136             for (int j = 0; j < size; j++) {
137                 setHeightAtPoint((float) tempBuffer[i][j], j, i);
138             }
139         }
140 
141         normalizeTerrain(NORMALIZE_RANGE);
142 
143         logger.info("Created Heightmap using the Hill Algorithm");
144 
145         return true;
146     }
147 
148     /**
149      * Generates a new hill of random size and height at a random position in
150      * the heightmap. This is the actual Hill algorithm. The <code>Random</code>
151      * object is used to guarantee the same heightmap for the same seed and
152      * attributes.
153      *
154      * @param tempBuffer
155      *            the temporary height map buffer
156      * @param random
157      *            the random number generator
158      */
addHill(float[][] tempBuffer, Random random)159     protected void addHill(float[][] tempBuffer, Random random) {
160         // Pick the radius for the hill
161         float radius = randomRange(random, minRadius, maxRadius);
162 
163         // Pick a centerpoint for the hill
164         float x = randomRange(random, -radius, size + radius);
165         float y = randomRange(random, -radius, size + radius);
166 
167         float radiusSq = radius * radius;
168         float distSq;
169         float height;
170 
171         // Find the range of hills affected by this hill
172         int xMin = Math.round(x - radius - 1);
173         int xMax = Math.round(x + radius + 1);
174 
175         int yMin = Math.round(y - radius - 1);
176         int yMax = Math.round(y + radius + 1);
177 
178         // Don't try to affect points outside the heightmap
179         if (xMin < 0) {
180             xMin = 0;
181         }
182         if (xMax > size) {
183             xMax = size - 1;
184         }
185 
186         if (yMin < 0) {
187             yMin = 0;
188         }
189         if (yMax > size) {
190             yMax = size - 1;
191         }
192 
193         for (int i = xMin; i <= xMax; i++) {
194             for (int j = yMin; j <= yMax; j++) {
195                 distSq = (x - i) * (x - i) + (y - j) * (y - j);
196                 height = radiusSq - distSq;
197 
198                 if (height > 0) {
199                     tempBuffer[i][j] += height;
200                 }
201             }
202         }
203     }
204 
randomRange(Random random, float min, float max)205     private float randomRange(Random random, float min, float max) {
206         return (random.nextInt() * (max - min) / Integer.MAX_VALUE) + min;
207     }
208 
209     /**
210      * Sets the number of hills to grow. More hills usually mean a nicer
211      * heightmap.
212      *
213      * @param iterations
214      *            the number of hills to grow
215      * @throws Exception
216      * @throws JmeException
217      *             if iterations if not greater than zero
218      */
setIterations(int iterations)219     public void setIterations(int iterations) throws Exception {
220         if (iterations <= 0) {
221             throw new Exception(
222                     "Number of iterations is not greater than zero");
223         }
224         this.iterations = iterations;
225     }
226 
227     /**
228      * Sets the minimum radius of a hill.
229      *
230      * @param maxRadius
231      *            the maximum radius of a hill
232      * @throws Exception
233      * @throws JmeException
234      *             if the maximum radius if not greater than zero or not greater
235      *             than the minimum radius
236      */
setMaxRadius(float maxRadius)237     public void setMaxRadius(float maxRadius) throws Exception {
238         if (maxRadius <= 0 || maxRadius <= minRadius) {
239             throw new Exception("The maximum radius is not greater than 0, "
240                     + "or not greater than the minimum radius");
241         }
242         this.maxRadius = maxRadius;
243     }
244 
245     /**
246      * Sets the maximum radius of a hill.
247      *
248      * @param minRadius
249      *            the minimum radius of a hill
250      * @throws Exception
251      * @throws JmeException if the minimum radius is not greater than zero or not
252      *        lower than the maximum radius
253      */
setMinRadius(float minRadius)254     public void setMinRadius(float minRadius) throws Exception {
255         if (minRadius <= 0 || minRadius >= maxRadius) {
256             throw new Exception("The minimum radius is not greater than 0, "
257                     + "or not lower than the maximum radius");
258         }
259         this.minRadius = minRadius;
260     }
261 }
262