• 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 com.jme3.math.FastMath;
35 import java.util.Random;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38 
39 /**
40  * Creates an heightmap based on the fault algorithm. Each iteration, a random line
41  * crossing the map is generated. On one side height values are raised, on the other side
42  * lowered.
43  * @author cghislai
44  */
45 public class FaultHeightMap extends AbstractHeightMap {
46 
47     private static final Logger logger = Logger.getLogger(FaultHeightMap.class.getName());
48     /**
49      * Values on one side are lowered, on the other side increased,
50      * creating a step at the fault line
51      */
52     public static final int FAULTTYPE_STEP = 0;
53     /**
54      * Values on one side are lowered, then increase lineary while crossing
55      * the fault line to the other side. The fault line will be a inclined
56      * plane
57      */
58     public static final int FAULTTYPE_LINEAR = 1;
59     /**
60      * Values are lowered on one side, increased on the other, creating a
61      * cosine curve on the fault line
62      */
63     public static final int FAULTTYPE_COSINE = 2;
64     /**
65      * Value are lowered on both side, but increased on the fault line
66      * creating a smooth ridge on the fault line.
67      */
68     public static final int FAULTTYPE_SINE = 3;
69     /**
70      * A linear fault is created
71      */
72     public static final int FAULTSHAPE_LINE = 10;
73     /**
74      * A circular fault is created.
75      */
76     public static final int FAULTSHAPE_CIRCLE = 11;
77     private long seed; // A seed to feed the random
78     private int iterations; // iterations to perform
79     private float minFaultHeight; // the height modification applied
80     private float maxFaultHeight; // the height modification applied
81     private float minRange; // The range for linear and trigo faults
82     private float maxRange; // The range for linear and trigo faults
83     private float minRadius; // radii for circular fault
84     private float maxRadius;
85     private int faultType; // The type of fault
86     private int faultShape; // The type of fault
87 
88     /**
89      * Constructor creates the fault. For faulttype other than STEP, a range can
90      * be provided. For faultshape circle, min and max radii can be provided.
91      * Don't forget to reload the map if you have set parameters after the constructor
92      * call.
93      * @param size The size of the heightmap
94      * @param iterations Iterations to perform
95      * @param faultType Type of fault
96      * @param faultShape Shape of the fault -line or circle
97      * @param minFaultHeight Height modified on each side
98      * @param maxFaultHeight Height modified on each side
99      * @param seed A seed to feed the Random generator
100      * @see setFaultRange, setMinRadius, setMaxRadius
101      */
FaultHeightMap(int size, int iterations, int faultType, int faultShape, float minFaultHeight, float maxFaultHeight, long seed)102     public FaultHeightMap(int size, int iterations, int faultType, int faultShape, float minFaultHeight, float maxFaultHeight, long seed) throws Exception {
103         if (size < 0 || iterations < 0) {
104             throw new Exception("Size and iterations must be greater than 0!");
105         }
106         this.size = size;
107         this.iterations = iterations;
108         this.faultType = faultType;
109         this.faultShape = faultShape;
110         this.minFaultHeight = minFaultHeight;
111         this.maxFaultHeight = maxFaultHeight;
112         this.seed = seed;
113         this.minRange = minFaultHeight;
114         this.maxRange = maxFaultHeight;
115         this.minRadius = size / 10;
116         this.maxRadius = size / 4;
117         load();
118     }
119 
120     /**
121      * Create an heightmap with linear step faults.
122      * @param size size of heightmap
123      * @param iterations number of iterations
124      * @param minFaultHeight Height modified on each side
125      * @param maxFaultHeight Height modified on each side
126      */
FaultHeightMap(int size, int iterations, float minFaultHeight, float maxFaultHeight)127     public FaultHeightMap(int size, int iterations, float minFaultHeight, float maxFaultHeight) throws Exception {
128         this(size, iterations, FAULTTYPE_STEP, FAULTSHAPE_LINE, minFaultHeight, maxFaultHeight, new Random().nextLong());
129     }
130 
131     @Override
load()132     public boolean load() {
133         // clean up data if needed.
134         if (null != heightData) {
135             unloadHeightMap();
136         }
137         heightData = new float[size * size];
138         float[][] tempBuffer = new float[size][size];
139         Random random = new Random(seed);
140 
141         for (int i = 0; i < iterations; i++) {
142             addFault(tempBuffer, random);
143         }
144 
145         for (int i = 0; i < size; i++) {
146             for (int j = 0; j < size; j++) {
147                 setHeightAtPoint(tempBuffer[i][j], i, j);
148             }
149         }
150 
151         normalizeTerrain(NORMALIZE_RANGE);
152 
153         logger.log(Level.INFO, "Fault heightmap generated");
154         return true;
155     }
156 
addFault(float[][] tempBuffer, Random random)157     protected void addFault(float[][] tempBuffer, Random random) {
158         float faultHeight = minFaultHeight + random.nextFloat() * (maxFaultHeight - minFaultHeight);
159         float range = minRange + random.nextFloat() * (maxRange - minRange);
160         switch (faultShape) {
161             case FAULTSHAPE_LINE:
162                 addLineFault(tempBuffer, random, faultHeight, range);
163                 break;
164             case FAULTSHAPE_CIRCLE:
165                 addCircleFault(tempBuffer, random, faultHeight, range);
166                 break;
167         }
168     }
169 
addLineFault(float[][] tempBuffer, Random random, float faultHeight, float range)170     protected void addLineFault(float[][] tempBuffer, Random random, float faultHeight, float range) {
171         int x1 = random.nextInt(size);
172         int x2 = random.nextInt(size);
173         int y1 = random.nextInt(size);
174         int y2 = random.nextInt(size);
175 
176 
177         for (int i = 0; i < size; i++) {
178             for (int j = 0; j < size; j++) {
179                 float dist = ((x2 - x1) * (j - y1) - (y2 - y1) * (i - x1))
180                         / (FastMath.sqrt(FastMath.sqr(x2 - x1) + FastMath.sqr(y2 - y1)));
181                 tempBuffer[i][j] += calcHeight(dist, random, faultHeight, range);
182             }
183         }
184     }
185 
addCircleFault(float[][] tempBuffer, Random random, float faultHeight, float range)186     protected void addCircleFault(float[][] tempBuffer, Random random, float faultHeight, float range) {
187         float radius = random.nextFloat() * (maxRadius - minRadius) + minRadius;
188         int intRadius = (int) FastMath.floor(radius);
189         // Allox circle center to be out of map if not by more than radius.
190         // Unlucky cases will put them in the far corner, with the circle
191         // entirely outside heightmap
192         int x = random.nextInt(size + 2 * intRadius) - intRadius;
193         int y = random.nextInt(size + 2 * intRadius) - intRadius;
194 
195         for (int i = 0; i < size; i++) {
196             for (int j = 0; j < size; j++) {
197                 float dist;
198                 if (i != x || j != y) {
199                     int dx = i - x;
200                     int dy = j - y;
201                     float dmag = FastMath.sqrt(FastMath.sqr(dx) + FastMath.sqr(dy));
202                     float rx = x + dx / dmag * radius;
203                     float ry = y + dy / dmag * radius;
204                     dist = FastMath.sign(dmag - radius)
205                         * FastMath.sqrt(FastMath.sqr(i - rx) + FastMath.sqr(j - ry));
206                 } else {
207                     dist = 0;
208                 }
209                 tempBuffer[i][j] += calcHeight(dist, random, faultHeight, range);
210             }
211         }
212     }
213 
calcHeight(float dist, Random random, float faultHeight, float range)214     protected float calcHeight(float dist, Random random, float faultHeight, float range) {
215         switch (faultType) {
216             case FAULTTYPE_STEP: {
217                 return FastMath.sign(dist) * faultHeight;
218             }
219             case FAULTTYPE_LINEAR: {
220                 if (FastMath.abs(dist) > range) {
221                     return FastMath.sign(dist) * faultHeight;
222                 }
223                 float f = FastMath.abs(dist) / range;
224                 return FastMath.sign(dist) * faultHeight * f;
225             }
226             case FAULTTYPE_SINE: {
227                 if (FastMath.abs(dist) > range) {
228                     return -faultHeight;
229                 }
230                 float f = dist / range;
231                 // We want -1 at f=-1 and f=1; 1 at f=0
232                 return FastMath.sin((1 + 2 * f) * FastMath.PI / 2) * faultHeight;
233             }
234             case FAULTTYPE_COSINE: {
235                 if (FastMath.abs(dist) > range) {
236                     return -FastMath.sign(dist) * faultHeight;
237                 }
238                 float f = dist / range;
239                 float val =  FastMath.cos((1 + f) * FastMath.PI / 2) * faultHeight;
240                 return val;
241             }
242         }
243         //shoudn't go here
244         throw new RuntimeException("Code needs update to switch allcases");
245     }
246 
getFaultShape()247     public int getFaultShape() {
248         return faultShape;
249     }
250 
setFaultShape(int faultShape)251     public void setFaultShape(int faultShape) {
252         this.faultShape = faultShape;
253     }
254 
getFaultType()255     public int getFaultType() {
256         return faultType;
257     }
258 
setFaultType(int faultType)259     public void setFaultType(int faultType) {
260         this.faultType = faultType;
261     }
262 
getIterations()263     public int getIterations() {
264         return iterations;
265     }
266 
setIterations(int iterations)267     public void setIterations(int iterations) {
268         this.iterations = iterations;
269     }
270 
getMaxFaultHeight()271     public float getMaxFaultHeight() {
272         return maxFaultHeight;
273     }
274 
setMaxFaultHeight(float maxFaultHeight)275     public void setMaxFaultHeight(float maxFaultHeight) {
276         this.maxFaultHeight = maxFaultHeight;
277     }
278 
getMaxRadius()279     public float getMaxRadius() {
280         return maxRadius;
281     }
282 
setMaxRadius(float maxRadius)283     public void setMaxRadius(float maxRadius) {
284         this.maxRadius = maxRadius;
285     }
286 
getMaxRange()287     public float getMaxRange() {
288         return maxRange;
289     }
290 
setMaxRange(float maxRange)291     public void setMaxRange(float maxRange) {
292         this.maxRange = maxRange;
293     }
294 
getMinFaultHeight()295     public float getMinFaultHeight() {
296         return minFaultHeight;
297     }
298 
setMinFaultHeight(float minFaultHeight)299     public void setMinFaultHeight(float minFaultHeight) {
300         this.minFaultHeight = minFaultHeight;
301     }
302 
getMinRadius()303     public float getMinRadius() {
304         return minRadius;
305     }
306 
setMinRadius(float minRadius)307     public void setMinRadius(float minRadius) {
308         this.minRadius = minRadius;
309     }
310 
getMinRange()311     public float getMinRange() {
312         return minRange;
313     }
314 
setMinRange(float minRange)315     public void setMinRange(float minRange) {
316         this.minRange = minRange;
317     }
318 
getSeed()319     public long getSeed() {
320         return seed;
321     }
322 
setSeed(long seed)323     public void setSeed(long seed) {
324         this.seed = seed;
325     }
326 }
327