⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fluidsimheightmap.java

📁 java 3d game jme 工程开发源代码
💻 JAVA
字号:
/*
* Copyright (c) 2003-2009 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
*   notice, this list of conditions and the following disclaimer in the
*   documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors 
*   may be used to endorse or promote products derived from this software 
*   without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.jmex.terrain.util;

import java.util.Random;
import java.util.logging.Logger;

import com.jme.system.JmeException;

/**
* <code>FluidSimHeightMap</code> generates a height map based using some
* sort of fluid simulation. The heightmap is treated as a highly viscous and 
* rubbery fluid enabling to fine tune the generated heightmap using a number
* of parameters.
* 
* @author Frederik B黮thoff
* @see <a href="http://www.gamedev.net/reference/articles/article2001.asp">Terrain Generation Using Fluid Simulation</a>
* @version $Id: FluidSimHeightMap.java,v 1.4 2007/08/02 23:16:21 nca Exp $
* 
*/
public class FluidSimHeightMap extends AbstractHeightMap {
    private static final Logger logger = Logger
            .getLogger(FluidSimHeightMap.class.getName());

	private float waveSpeed = 100.0f;  // speed at which the waves travel
	
	private float timeStep = 0.033f;  // constant time-step between each iteration
	
	private float nodeDistance = 10.0f;   // distance between each node of the surface
	
	private float viscosity = 100.0f; // viscosity of the fluid
	
	private int iterations;    // number of iterations
	
	private float minInitialHeight = -500; // min initial height
	
	private float maxInitialHeight = 500; // max initial height

	private long seed; // the seed for the random number generator
	
	float coefA, coefB, coefC; // pre-computed coefficients in the fluid equation

	/**
	 * Constructor sets the attributes of the hill system and generates the
	 * height map. It gets passed a number of tweakable parameters which 
	 * fine-tune the outcome. 
	 * 
	 * @param size
	 *            size the size of the terrain to be generated
	 * @param iterations
	 *            the number of iterations to do
	 * @param minInitialHeight
	 * 			  the minimum initial height of a terrain value
	 * @param maxInitialHeight
	 * 			  the maximum initial height of a terrain value
	 * @param viscosity
	 * 			  the viscosity of the fluid
	 * @param waveSpeed
	 * 			  the speed at which the waves travel
	 * @param timestep
	 * 			  the constant time-step between each iteration
	 * @param nodeDistance
	 * 			  the distance between each node of the heightmap
	 * @param seed
	 *            the seed to generate the same heightmap again
	 * @throws JmeException
	 *             if size of the terrain is not greater that zero, or number of
	 *             iterations is not greater that zero, or the minimum initial height
	 *             is greater than the maximum (or the other way around)
	 */
	public FluidSimHeightMap(int size, int iterations, float minInitialHeight, float maxInitialHeight, float viscosity, float waveSpeed, float timestep, float nodeDistance, long seed) {
		if (size <= 0 || iterations <= 0 || minInitialHeight >= maxInitialHeight) {
			throw new JmeException(
					"Either size of the terrain is not greater that zero, "
							+ "or number of iterations is not greater that zero, "
							+ "or minimum height greater or equal as the maximum, "
							+ "or maximum height smaller or equal as the minimum.");
		}
		
		this.size = size;
		this.seed = seed;
		this.iterations = iterations;
		this.minInitialHeight = minInitialHeight;
		this.maxInitialHeight = maxInitialHeight;
		this.viscosity = viscosity;
		this.waveSpeed = waveSpeed;
		this.timeStep = timestep;
		this.nodeDistance = nodeDistance;

		load();
	}

	/**
	 * Constructor sets the attributes of the hill system and generates the
	 * height map.
	 * 
	 * @param size
	 *            size the size of the terrain to be generated
	 * @param iterations
	 *            the number of iterations to do
	 * @throws JmeException
	 *             if size of the terrain is not greater that zero, or number of
	 *             iterations is not greater that zero
	 */
	public FluidSimHeightMap(int size, int iterations) {
		if (size <= 0 || iterations <= 0) {
			throw new JmeException(
					"Either size of the terrain is not greater that zero, "
							+ "or number of iterations is not greater that zero");
		}
		
		this.size = size;
		this.iterations = iterations;

		load();
	}

	/*
	 * Generates a heightmap using fluid simulation and the attributes set by
	 * the constructor or the setters.
	 */
	public boolean load() {
		// Clean up data if needed.
		if (null != heightData) {
			unloadHeightMap();
		}
		
		heightData = new float[size * size];
		float[][] tempBuffer = new float[2][size*size];
		Random random = new Random(seed);

		// pre-compute the coefficients in the fluid equation
		coefA = (4 - (8*waveSpeed*waveSpeed*timeStep*timeStep) / (nodeDistance*nodeDistance)) / (viscosity*timeStep + 2);
		coefB = (viscosity*timeStep - 2) / (viscosity*timeStep + 2);
		coefC = ((2*waveSpeed*waveSpeed*timeStep*timeStep) / (nodeDistance*nodeDistance)) / (viscosity*timeStep + 2);

		// initialize the heightmaps to random values except for the edges
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (i == 0 | j == 0 || i == (size - 1) || j == (size - 1)) {
					tempBuffer[0][j+i*size] = tempBuffer[1][j+i*size] = 0;
				} else {
					tempBuffer[0][j+i*size] = tempBuffer[1][j+i*size] = randomRange(random, minInitialHeight, maxInitialHeight);
				}
			}
		}
		
		int curBuf = 0;
		int ind;
		
		float[] oldBuffer;
		float[] newBuffer;
		
		// Iterate over the heightmap, applying the fluid simulation equation.
		// Although it requires knowledge of the two previous timesteps, it only
		// accesses one pixel of the k-1 timestep, so using a simple trick we only
		// need to store the heightmap twice, not three times, and we can avoid
		// copying data every iteration.
		for (int i = 0; i < iterations; i++) {
			oldBuffer = tempBuffer[1 - curBuf];
			newBuffer = tempBuffer[curBuf];

			for (int y = 1; y < size - 1; y++) {
				for (int x = 1; x < size - 1; x++) {
					ind = x + y * size;
					oldBuffer[ind] = coefA * newBuffer[ind] + coefB
							* oldBuffer[ind] + coefC * (newBuffer[ind+1]
							+ newBuffer[ind - 1] + newBuffer[ind + size]
							+ newBuffer[ind - size]);
				}
			}

			curBuf = 1 - curBuf;
		}
		
		normalize(tempBuffer[curBuf]);
		
		// put the normalized heightmap into the range [0...255] and into the heightmap
		for (int y = 0; y < size; y++) {
			for (int x = 0; x < size; x++) {
				heightData[x + y*size] = (float)(tempBuffer[curBuf][x + y*size] * 255);
			}
		}

		logger.info("Created Heightmap using fluid simulation");

		return true;
	}

	/**
	 * Normalizes the heightmap values between 0.0 and 1.0.
	 * 
	 * @param buffer
	 *            the buffer holding the heightmap data
	 */
	protected void normalize(float[] buffer) {
		// find the minimum and maximum heights
		float minHeight = buffer[0];
		float maxHeight = buffer[0];
		
		for (int y = 0; y < size; y++) {
			for (int x = 0; x < size; x++) {
				if (buffer[x + y * size] > maxHeight) {
					maxHeight = buffer[x + y * size];
				} else if (buffer[x + y * size] < minHeight) {
					minHeight = buffer[x + y * size];
				}
			}
		}
		
		// normalize the heightmap
		for (int y = 0; y < size; y++) {
			for (int x = 0; x < size; x++) {
				//if (buffer[x + y*size] == 0.0f) continue;
				//logger.info((buffer[x + y*size] - minHeight) / (maxHeight - minHeight));
				buffer[x + y*size] = (buffer[x + y*size] - minHeight) / (maxHeight - minHeight);
			}
		}
	}

	private float randomRange(Random random, float min, float max) {
		return (random.nextInt() * (max - min) / Integer.MAX_VALUE) + min;
	}

	/**
	 * Sets the number of times the fluid simulation should be iterated over
	 * the heightmap. The more often this is, the less features (hills, etc)
	 * the terrain will have, and the smoother it will be.
	 * 
	 * @param iterations
	 *            the number of iterations to do
	 * @throws JmeException
	 *             if iterations if not greater than zero
	 */
	public void setIterations(int iterations) {
		if (iterations <= 0) {
			throw new JmeException(
					"Number of iterations is not greater than zero");
		}
		this.iterations = iterations;
	}

	
	/**
	 * Sets the maximum initial height of the terrain.
	 * 
	 * @param maxInitialHeight
	 * 			  the maximum initial height
	 * @see #setMinInitialHeight(int)
	 */
	public void setMaxInitialHeight(float maxInitialHeight) {
		this.maxInitialHeight = maxInitialHeight;
	}
	
	/**
	 * Sets the minimum initial height of the terrain.
	 * 
	 * @param minInitialHeight
	 * 			  the minimum initial height
	 * @see #setMaxInitialHeight(int)
	 */

	public void setMinInitialHeight(float minInitialHeight) {
		this.minInitialHeight = minInitialHeight;
	}
	
	/**
	 * Sets the distance between each node of the heightmap.
	 * 
	 * @param nodeDistance
	 * 			 the distance between each node
	 */

	public void setNodeDistance(float nodeDistance) {
		this.nodeDistance = nodeDistance;
	}
	
	/**
	 * Sets the time-speed between each iteration of the fluid
	 * simulation algortithm.
	 * 
	 * @param timeStep
	 * 			 the time-step between each iteration
	 */

	public void setTimeStep(float timeStep) {
		this.timeStep = timeStep;
	}
	
	/**
	 * Sets the viscosity of the simulated fuid.
	 * 
	 * @param viscosity
	 * 			the viscosity of the fluid
	 */

	public void setViscosity(float viscosity) {
		this.viscosity = viscosity;
	}
	
	/**
	 * Sets the speed at which the waves trave.
	 * 
	 * @param waveSpeed
	 * 			the speed at which the waves travel
	 */

	public void setWaveSpeed(float waveSpeed) {
		this.waveSpeed = waveSpeed;
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -