• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.jme3.terrain.heightmap;
33 
34 import java.util.logging.Logger;
35 
36 /**
37  * <code>CombinerHeightMap</code> generates a new height map based on
38  * two provided height maps. These had maps can either be added together
39  * or substracted from each other. Each heightmap has a weight to
40  * determine how much one will affect the other. By default it is set to
41  * 0.5, 0.5 and meaning the two heightmaps are averaged evenly. This
42  * value can be adjusted at will, as long as the two factors are equal
43  * to 1.0.
44  *
45  * @author Mark Powell
46  * @version $Id$
47  */
48 public class CombinerHeightMap extends AbstractHeightMap {
49 
50     private static final Logger logger = Logger.getLogger(CombinerHeightMap.class.getName());
51     /**
52      * Constant mode to denote adding the two heightmaps.
53      */
54     public static final int ADDITION = 0;
55     /**
56      * Constant mode to denote subtracting the two heightmaps.
57      */
58     public static final int SUBTRACTION = 1;
59     //the two maps.
60     private AbstractHeightMap map1;
61     private AbstractHeightMap map2;
62     //the two factors
63     private float factor1 = 0.5f;
64     private float factor2 = 0.5f;
65     //the combine mode.
66     private int mode;
67 
68     /**
69      * Constructor combines two given heightmaps by the specified mode.
70      * The heightmaps will be evenly distributed. The heightmaps
71      * must be of the same size.
72      *
73      * @param map1 the first heightmap to combine.
74      * @param map2 the second heightmap to combine.
75      * @param mode denotes whether to add or subtract the heightmaps, may
76      *              be either ADDITION or SUBTRACTION.
77      * @throws JmeException if either map is null, their size
78      *              do not match or the mode is invalid.
79      */
CombinerHeightMap( AbstractHeightMap map1, AbstractHeightMap map2, int mode)80     public CombinerHeightMap(
81             AbstractHeightMap map1,
82             AbstractHeightMap map2,
83             int mode) throws Exception {
84 
85 
86         //insure all parameters are valid.
87         if (null == map1 || null == map2) {
88             throw new Exception("Height map may not be null");
89         }
90 
91 
92         if (map1.getSize() != map2.getSize()) {
93             throw new Exception("The two maps must be of the same size");
94         }
95 
96 
97         if ((factor1 + factor2) != 1.0f) {
98             throw new Exception("factor1 and factor2 must add to 1.0");
99         }
100 
101 
102         this.size = map1.getSize();
103         this.map1 = map1;
104         this.map2 = map2;
105 
106 
107         setMode(mode);
108 
109         load();
110     }
111 
112     /**
113      * Constructor combines two given heightmaps by the specified mode.
114      * The heightmaps will be distributed based on the given factors.
115      * For example, if factor1 is 0.6 and factor2 is 0.4, then 60% of
116      * map1 will be used with 40% of map2. The two factors must add up
117      * to 1.0. The heightmaps must also be of the same size.
118      *
119      * @param map1 the first heightmap to combine.
120      * @param factor1 the factor for map1.
121      * @param map2 the second heightmap to combine.
122      * @param factor2 the factor for map2.
123      * @param mode denotes whether to add or subtract the heightmaps, may
124      *              be either ADDITION or SUBTRACTION.
125      * @throws JmeException if either map is null, their size
126      *              do not match, the mode is invalid, or the factors do not add
127      *              to 1.0.
128      */
CombinerHeightMap( AbstractHeightMap map1, float factor1, AbstractHeightMap map2, float factor2, int mode)129     public CombinerHeightMap(
130             AbstractHeightMap map1,
131             float factor1,
132             AbstractHeightMap map2,
133             float factor2,
134             int mode) throws Exception {
135 
136 
137         //insure all parameters are valid.
138         if (null == map1 || null == map2) {
139             throw new Exception("Height map may not be null");
140         }
141 
142 
143         if (map1.getSize() != map2.getSize()) {
144             throw new Exception("The two maps must be of the same size");
145         }
146 
147 
148         if ((factor1 + factor2) != 1.0f) {
149             throw new Exception("factor1 and factor2 must add to 1.0");
150         }
151 
152 
153         setMode(mode);
154 
155 
156         this.size = map1.getSize();
157         this.map1 = map1;
158         this.map2 = map2;
159         this.factor1 = factor1;
160         this.factor2 = factor2;
161 
162 
163         this.mode = mode;
164 
165 
166         load();
167     }
168 
169     /**
170      * <code>setFactors</code> sets the distribution of heightmaps.
171      * For example, if factor1 is 0.6 and factor2 is 0.4, then 60% of
172      * map1 will be used with 40% of map2. The two factors must add up
173      * to 1.0.
174      * @param factor1 the factor for map1.
175      * @param factor2 the factor for map2.
176      * @throws JmeException if the factors do not add to 1.0.
177      */
setFactors(float factor1, float factor2)178     public void setFactors(float factor1, float factor2) throws Exception {
179         if ((factor1 + factor2) != 1.0f) {
180             throw new Exception("factor1 and factor2 must add to 1.0");
181         }
182 
183 
184         this.factor1 = factor1;
185         this.factor2 = factor2;
186     }
187 
188     /**
189      * <code>setHeightMaps</code> sets the height maps to combine.
190      * The size of the height maps must be the same.
191      * @param map1 the first height map.
192      * @param map2 the second height map.
193      * @throws JmeException if the either heightmap is null, or their
194      *              sizes do not match.
195      */
setHeightMaps(AbstractHeightMap map1, AbstractHeightMap map2)196     public void setHeightMaps(AbstractHeightMap map1, AbstractHeightMap map2) throws Exception {
197         if (null == map1 || null == map2) {
198             throw new Exception("Height map may not be null");
199         }
200 
201 
202         if (map1.getSize() != map2.getSize()) {
203             throw new Exception("The two maps must be of the same size");
204         }
205 
206 
207         this.size = map1.getSize();
208         this.map1 = map1;
209         this.map2 = map2;
210     }
211 
212     /**
213      * <code>setMode</code> sets the mode of the combiner. This may either
214      * be ADDITION or SUBTRACTION.
215      * @param mode the mode of the combiner.
216      * @throws JmeException if mode is not ADDITION or SUBTRACTION.
217      */
setMode(int mode)218     public void setMode(int mode) throws Exception {
219         if (mode != ADDITION && mode != SUBTRACTION) {
220             throw new Exception("Invalid mode");
221         }
222         this.mode = mode;
223     }
224 
225     /**
226      * <code>load</code> builds a new heightmap based on the combination of
227      * two other heightmaps. The conditions of the combiner determine the
228      * final outcome of the heightmap.
229      *
230      * @return boolean if the heightmap was successfully created.
231      */
load()232     public boolean load() {
233         if (null != heightData) {
234             unloadHeightMap();
235         }
236 
237 
238         heightData = new float[size * size];
239 
240 
241         float[] temp1 = map1.getHeightMap();
242         float[] temp2 = map2.getHeightMap();
243 
244 
245         if (mode == ADDITION) {
246             for (int i = 0; i < size; i++) {
247                 for (int j = 0; j < size; j++) {
248                     heightData[i + (j * size)] =
249                             (int) (temp1[i + (j * size)] * factor1
250                             + temp2[i + (j * size)] * factor2);
251                 }
252             }
253         } else if (mode == SUBTRACTION) {
254             for (int i = 0; i < size; i++) {
255                 for (int j = 0; j < size; j++) {
256                     heightData[i + (j * size)] =
257                             (int) (temp1[i + (j * size)] * factor1
258                             - temp2[i + (j * size)] * factor2);
259                 }
260             }
261         }
262 
263 
264         logger.info("Created heightmap using Combiner");
265 
266 
267         return true;
268     }
269 }
270