📄 terrainpage.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;
import java.io.IOException;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingVolume;
import com.jme.math.FastMath;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
/**
* <code>TerrainPage</code> is used to build a quad tree of terrain blocks. The
* <code>TerrainPage</code> will have four children, either four pages or four
* blocks. The size of the page must be (2^N + 1), to allow for even splitting
* of the blocks. Organization of the page into a quad tree allows for very fast
* culling of the terrain. The total size of the heightmap is provided, as well
* as the desired end size for a block. Appropriate values for the end block
* size is completely dependent on the application. In some cases, a large size
* will give performance gains, in others, a small size is the best option. It
* is recommended that different combinations are tried.
*
* @author Mark Powell
* @author Joshua Slack
*/
public class TerrainPage extends Node {
private static final long serialVersionUID = 1L;
private Vector2f offset;
private int totalSize;
private int size;
private Vector3f stepScale;
private float offsetAmount;
private short quadrant = 1;
private static Vector3f calcVec1 = new Vector3f();
/**
* Empty Constructor to be used internally only.
*/
public TerrainPage() {
}
/**
* Creates a TerrainPage to be filled later. Usually, users don't want to
* call this function unless they have a terrain page already built.
*
* @param name
* The name of the page node.
*/
public TerrainPage(String name) {
super(name);
}
/**
* Constructor instantiates a new <code>TerrainPage</code> object. The
* data is then split into either 4 new <code>TerrainPages</code> or 4 new
* <code>TerrainBlock</code>.
*
* @param name
* the name of the page.
* @param blockSize
* the size of the leaf nodes. This is used to determine if four
* new <code>TerrainPage</code> objects should be the child or
* four new <code>TerrainBlock</code> objects.
* @param size
* the size of the heightmap for this page.
* @param stepScale
* the scale of the axes.
* @param heightMap
* the height data.
*/
public TerrainPage(String name, int blockSize, int size,
Vector3f stepScale, float[] heightMap) {
this(name, blockSize, size, stepScale, heightMap, size,
new Vector2f(), 0);
fixNormals();
}
/**
* Constructor instantiates a new <code>TerrainPage</code> object. The
* data is then split into either 4 new <code>TerrainPages</code> or 4 new
* <code>TerrainBlock</code>.
*
* @param name
* the name of the page.
* @param blockSize
* the size of the leaf nodes. This is used to determine if four
* new <code>TerrainPage</code> objects should be the child or
* four new <code>TerrainBlock</code> objects.
* @param size
* the size of the heightmap for this page.
* @param stepScale
* the scale of the axes.
* @param heightMap
* the height data.
* @param totalSize
* the total terrain size, used if the page is an internal node
* of a terrain system.
* @param offset
* the texture offset for the page.
* @param offsetAmount
* the amount of the offset.
*/
protected TerrainPage(String name, int blockSize, int size,
Vector3f stepScale, float[] heightMap, int totalSize,
Vector2f offset, float offsetAmount) {
super(name);
if (!FastMath.isPowerOfTwo(size - 1)) {
throw new JmeException("size given: " + size
+ " Terrain page sizes may only be (2^N + 1)");
}
this.offset = offset;
this.offsetAmount = offsetAmount;
this.totalSize = totalSize;
this.size = size;
this.stepScale = stepScale;
split(blockSize, heightMap);
}
/**
* <code>setDetailTexture</code> sets the detail texture coordinates to be
* applied on top of the normal terrain texture.
*
* @param unit
* the texture unit to set the coordinates.
* @param repeat
* the number of tiling for the texture.
*/
public void setDetailTexture(int unit, int repeat) {
for (int i = 0; i < this.getQuantity(); i++) {
if (this.getChild(i) instanceof TerrainPage) {
((TerrainPage) getChild(i)).setDetailTexture(unit, repeat);
} else if (this.getChild(i) instanceof TerrainBlock) {
((TerrainBlock) getChild(i)).setDetailTexture(unit, repeat);
}
}
}
/**
* <code>setModelBound</code> sets the model bounds for the terrain
* blocks.
*
* @param v
* the bounding volume to set for the terrain blocks.
*/
public void setModelBound(BoundingVolume v) {
for (int i = 0; i < this.getQuantity(); i++) {
if (this.getChild(i) instanceof TerrainPage) {
((TerrainPage) getChild(i)).setModelBound(v.clone(null));
} else if (this.getChild(i) instanceof TerrainBlock) {
((TerrainBlock) getChild(i)).setModelBound(v.clone(null));
}
}
}
/**
* <code>updateModelBound</code> updates the model bounds (generates the
* bounds from the current vertices).
*/
public void updateModelBound() {
for (int i = 0; i < this.getQuantity(); i++) {
if (this.getChild(i) instanceof TerrainPage) {
((TerrainPage) getChild(i)).updateModelBound();
} else if (this.getChild(i) instanceof TerrainBlock) {
((TerrainBlock) getChild(i)).updateModelBound();
}
}
}
/**
* <code>updateFromHeightMap</code> updates the verts of all sub blocks
* from the contents of their heightmaps.
*/
public void updateFromHeightMap() {
for (int i = 0; i < this.getQuantity(); i++) {
if (this.getChild(i) instanceof TerrainPage) {
((TerrainPage) getChild(i)).updateFromHeightMap();
} else if (this.getChild(i) instanceof TerrainBlock) {
((TerrainBlock) getChild(i)).updateFromHeightMap();
}
}
}
/**
* <code>getHeight</code> returns the height of an arbitrary point on the
* terrain. If the point is between height point values, the height is
* linearly interpolated. This provides smooth height calculations. If the
* point provided is not within the bounds of the height map, the NaN float
* value is returned (Float.NaN).
*
* @param position
* the vector representing the height location to check.
* @return the height at the provided location.
*/
public float getHeight(Vector2f position) {
return getHeight(position.x, position.y);
}
/**
* <code>getHeight</code> returns the height of an arbitrary point on the
* terrain. If the point is between height point values, the height is
* linearly interpolated. This provides smooth height calculations. If the
* point provided is not within the bounds of the height map, the NaN float
* value is returned (Float.NaN).
*
* @param position
* the vector representing the height location to check. Only the
* x and z values are used.
* @return the height at the provided location.
*/
public float getHeight(Vector3f position) {
return getHeight(position.x, position.z);
}
/**
* <code>getHeight</code> returns the height of an arbitrary point on the
* terrain. If the point is between height point values, the height is
* linearly interpolated. This provides smooth height calculations. If the
* point provided is not within the bounds of the height map, the NaN float
* value is returned (Float.NaN).
*
* @param x
* the x coordinate to check.
* @param z
* the z coordinate to check.
* @return the height at the provided location.
*/
public float getHeight(float x, float z) {
// determine which quadrant this is in.
Spatial child = null;
int split = (size - 1) >> 1;
float halfmapx = split * stepScale.x, halfmapz = split * stepScale.z;
float newX = 0, newZ = 0;
if (x == 0)
x += .001f;
if (z == 0)
z += .001f;
if (x > 0) {
if (z > 0) {
// upper right
child = getChild(3);
newX = x;
newZ = z;
} else {
// lower right
child = getChild(2);
newX = x;
newZ = z + halfmapz;
}
} else {
if (z > 0) {
// upper left
child = getChild(1);
newX = x + halfmapx;
newZ = z;
} else {
// lower left...
child = getChild(0);
if (x == 0)
x -= .1f;
if (z == 0)
z -= .1f;
newX = x + halfmapx;
newZ = z + halfmapz;
}
}
if (child instanceof TerrainBlock)
return ((TerrainBlock) child).getHeight(newX, newZ);
else if (child instanceof TerrainPage)
return ((TerrainPage) child).getHeight(x
- ((TerrainPage) child).getLocalTranslation().x, z
- ((TerrainPage) child).getLocalTranslation().z);
return Float.NaN;
}
/**
* <code>getHeightFromWorld</code> returns the height of an arbitrary
* point on the terrain when given world coordinates. If the point is
* between height point values, the height is linearly interpolated. This
* provides smooth height calculations. If the point provided is not within
* the bounds of the height map, the NaN float value is returned
* (Float.NaN).
*
* @param position
* the vector representing the height location to check.
* @return the height at the provided location.
*/
public float getHeightFromWorld(Vector3f position) {
Vector3f locationPos = calcVec1.set(position).subtractLocal(
localTranslation).divideLocal(stepScale);
locationPos.multLocal(getStepScale());
return getHeight(locationPos.x, locationPos.z);
}
/**
* <code>getSurfaceNormal</code> returns the normal of an arbitrary point
* on the terrain. The normal is linearly interpreted from the normals of
* the 4 nearest defined points. If the point provided is not within the
* bounds of the height map, null is returned.
*
* @param position
* the vector representing the location to find a normal at.
* @param store
* the Vector3f object to store the result in. If null, a new one
* is created.
* @return the normal vector at the provided location.
*/
public Vector3f getSurfaceNormal(Vector2f position, Vector3f store) {
return getSurfaceNormal(position.x, position.y, store);
}
/**
* <code>getSurfaceNormal</code> returns the normal of an arbitrary point
* on the terrain. The normal is linearly interpreted from the normals of
* the 4 nearest defined points. If the point provided is not within the
* bounds of the height map, null is returned.
*
* @param position
* the vector representing the location to find a normal at. Only
* the x and z values are used.
* @param store
* the Vector3f object to store the result in. If null, a new one
* is created.
* @return the normal vector at the provided location.
*/
public Vector3f getSurfaceNormal(Vector3f position, Vector3f store) {
return getSurfaceNormal(position.x, position.z, store);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -