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 */ 32package com.jme3.terrain.heightmap; 33 34import 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 */ 48public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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