2011-03-14 12:55:32 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2009-2010 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.jme3.terrain.geomipmap;
|
|
|
|
|
|
|
|
import com.jme3.bounding.BoundingBox;
|
|
|
|
import com.jme3.bounding.BoundingSphere;
|
|
|
|
import com.jme3.bounding.BoundingVolume;
|
|
|
|
import com.jme3.collision.Collidable;
|
|
|
|
import com.jme3.collision.CollisionResults;
|
|
|
|
import com.jme3.collision.UnsupportedCollisionException;
|
|
|
|
import com.jme3.export.InputCapsule;
|
|
|
|
import com.jme3.export.JmeExporter;
|
|
|
|
import com.jme3.export.JmeImporter;
|
|
|
|
import com.jme3.export.OutputCapsule;
|
|
|
|
import java.nio.FloatBuffer;
|
|
|
|
import java.nio.IntBuffer;
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
import com.jme3.math.FastMath;
|
|
|
|
import com.jme3.math.Ray;
|
|
|
|
import com.jme3.math.Triangle;
|
|
|
|
import com.jme3.math.Vector2f;
|
|
|
|
import com.jme3.math.Vector3f;
|
|
|
|
import com.jme3.scene.Geometry;
|
|
|
|
import com.jme3.scene.Mesh;
|
|
|
|
import com.jme3.scene.VertexBuffer;
|
|
|
|
import com.jme3.scene.VertexBuffer.Type;
|
2011-06-16 23:57:44 +00:00
|
|
|
import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
|
2011-03-14 12:55:32 +00:00
|
|
|
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
|
|
|
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
|
|
|
|
import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;
|
|
|
|
import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;
|
|
|
|
import com.jme3.util.BufferUtils;
|
|
|
|
import com.jme3.util.TangentBinormalGenerator;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A terrain patch is a leaf in the terrain quad tree. It has a mesh that can change levels of detail (LOD)
|
|
|
|
* whenever the view point, or camera, changes. The actual terrain mesh is created by the LODGeomap class.
|
2011-07-05 19:42:37 +00:00
|
|
|
* That uses a geo-mipmapping algorithm to change the index buffer of the mesh.
|
2011-03-14 12:55:32 +00:00
|
|
|
* The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate
|
|
|
|
* triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost.
|
|
|
|
*
|
|
|
|
* Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different
|
|
|
|
* LOD. If this doesn't happen, you will see gaps.
|
|
|
|
*
|
|
|
|
* The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which
|
|
|
|
* is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that
|
|
|
|
* for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far.
|
|
|
|
*
|
|
|
|
* You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
|
|
|
|
* in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
|
|
|
|
* then the LOD changes every 130 units away.
|
|
|
|
*
|
|
|
|
* @author Brent Owens
|
|
|
|
*/
|
|
|
|
public class TerrainPatch extends Geometry {
|
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
protected LODGeomap geomap;
|
|
|
|
protected int lod = -1; // this terrain patch's LOD
|
|
|
|
private int maxLod = -1;
|
|
|
|
protected int previousLod = -1;
|
|
|
|
protected int lodLeft, lodTop, lodRight, lodBottom; // it's neighbour's LODs
|
|
|
|
|
|
|
|
protected int size;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
protected int totalSize;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
protected short quadrant = 1;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
// x/z step
|
|
|
|
protected Vector3f stepScale;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-07-05 19:42:37 +00:00
|
|
|
// center of the patch in relation to (0,0,0)
|
2011-06-15 15:14:45 +00:00
|
|
|
protected Vector2f offset;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-07-05 19:42:37 +00:00
|
|
|
// amount the patch has been shifted.
|
2011-06-15 15:14:45 +00:00
|
|
|
protected float offsetAmount;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-09-09 23:34:15 +00:00
|
|
|
//protected LodCalculator lodCalculator;
|
|
|
|
//protected LodCalculatorFactory lodCalculatorFactory;
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
|
|
|
|
protected boolean searchedForNeighboursAlready = false;
|
|
|
|
|
|
|
|
|
|
|
|
protected float[] lodEntropy;
|
|
|
|
|
|
|
|
public TerrainPatch() {
|
|
|
|
super("TerrainPatch");
|
|
|
|
}
|
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
public TerrainPatch(String name) {
|
|
|
|
super(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public TerrainPatch(String name, int size) {
|
|
|
|
this(name, size, new Vector3f(1,1,1), null, new Vector3f(0,0,0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor instantiates a new <code>TerrainPatch</code> object. The
|
|
|
|
* parameters and heightmap data are then processed to generate a
|
|
|
|
* <code>TriMesh</code> object for rendering.
|
|
|
|
*
|
|
|
|
* @param name
|
2011-07-05 19:42:37 +00:00
|
|
|
* the name of the terrain patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
* @param size
|
|
|
|
* the size of the heightmap.
|
|
|
|
* @param stepScale
|
|
|
|
* the scale for the axes.
|
|
|
|
* @param heightMap
|
|
|
|
* the height data.
|
|
|
|
* @param origin
|
2011-07-05 19:42:37 +00:00
|
|
|
* the origin offset of the patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
*/
|
|
|
|
public TerrainPatch(String name, int size, Vector3f stepScale,
|
|
|
|
float[] heightMap, Vector3f origin) {
|
|
|
|
this(name, size, stepScale, heightMap, origin, size, new Vector2f(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor instantiates a new <code>TerrainPatch</code> object. The
|
|
|
|
* parameters and heightmap data are then processed to generate a
|
|
|
|
* <code>TriMesh</code> object for renderering.
|
|
|
|
*
|
|
|
|
* @param name
|
2011-07-05 19:42:37 +00:00
|
|
|
* the name of the terrain patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
* @param size
|
2011-07-05 19:42:37 +00:00
|
|
|
* the size of the patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
* @param stepScale
|
|
|
|
* the scale for the axes.
|
|
|
|
* @param heightMap
|
|
|
|
* the height data.
|
|
|
|
* @param origin
|
2011-07-05 19:42:37 +00:00
|
|
|
* the origin offset of the patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
* @param totalSize
|
2011-07-05 19:42:37 +00:00
|
|
|
* the total size of the terrain. (Higher if the patch is part of
|
|
|
|
* a <code>TerrainQuad</code> tree.
|
2011-06-15 15:14:45 +00:00
|
|
|
* @param offset
|
|
|
|
* the offset for texture coordinates.
|
|
|
|
* @param offsetAmount
|
|
|
|
* the total offset amount. Used for texture coordinates.
|
|
|
|
*/
|
|
|
|
public TerrainPatch(String name, int size, Vector3f stepScale,
|
|
|
|
float[] heightMap, Vector3f origin, int totalSize,
|
|
|
|
Vector2f offset, float offsetAmount) {
|
|
|
|
super(name);
|
|
|
|
this.size = size;
|
|
|
|
this.stepScale = stepScale;
|
|
|
|
this.totalSize = totalSize;
|
|
|
|
this.offsetAmount = offsetAmount;
|
|
|
|
this.offset = offset;
|
|
|
|
|
|
|
|
setLocalTranslation(origin);
|
|
|
|
|
2011-11-20 00:24:13 +00:00
|
|
|
geomap = new LODGeomap(size, heightMap);
|
2011-06-15 15:14:45 +00:00
|
|
|
Mesh m = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
|
|
|
|
setMesh(m);
|
|
|
|
|
|
|
|
}
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This calculation is slow, so don't use it often.
|
|
|
|
*/
|
|
|
|
public void generateLodEntropies() {
|
|
|
|
float[] entropies = new float[getMaxLod()+1];
|
|
|
|
for (int i = 0; i <= getMaxLod(); i++){
|
|
|
|
int curLod = (int) Math.pow(2, i);
|
|
|
|
IntBuffer buf = geomap.writeIndexArrayLodDiff(null, curLod, false, false, false, false);
|
|
|
|
entropies[i] = EntropyComputeUtil.computeLodEntropy(mesh, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
lodEntropy = entropies;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float[] getLodEntropies(){
|
|
|
|
if (lodEntropy == null){
|
|
|
|
generateLodEntropies();
|
|
|
|
}
|
|
|
|
return lodEntropy;
|
|
|
|
}
|
|
|
|
|
2011-11-20 00:24:13 +00:00
|
|
|
public float[] getHeightmap() {
|
2011-06-15 15:14:45 +00:00
|
|
|
return geomap.getHeightData();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum lod supported by this terrain patch.
|
|
|
|
* If the patch size is 32 then the returned value would be log2(32)-2 = 3
|
|
|
|
* You can then use that value, 3, to see how many times you can divide 32 by 2
|
|
|
|
* before the terrain gets too un-detailed (can't stitch it any further).
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public int getMaxLod() {
|
|
|
|
if (maxLod < 0)
|
|
|
|
maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
|
|
|
|
|
|
|
|
return maxLod;
|
|
|
|
}
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-09-09 23:34:15 +00:00
|
|
|
protected void reIndexGeometry(HashMap<String,UpdatedTerrainPatch> updated, boolean useVariableLod) {
|
2011-06-15 15:14:45 +00:00
|
|
|
|
|
|
|
UpdatedTerrainPatch utp = updated.get(getName());
|
|
|
|
|
|
|
|
if (utp != null && (utp.isReIndexNeeded() || utp.isFixEdges()) ) {
|
|
|
|
int pow = (int) Math.pow(2, utp.getNewLod());
|
|
|
|
boolean left = utp.getLeftLod() > utp.getNewLod();
|
|
|
|
boolean top = utp.getTopLod() > utp.getNewLod();
|
|
|
|
boolean right = utp.getRightLod() > utp.getNewLod();
|
|
|
|
boolean bottom = utp.getBottomLod() > utp.getNewLod();
|
|
|
|
|
2011-03-14 12:55:32 +00:00
|
|
|
IntBuffer ib = null;
|
2011-09-09 23:34:15 +00:00
|
|
|
if (useVariableLod)
|
2011-03-14 12:55:32 +00:00
|
|
|
ib = geomap.writeIndexArrayLodVariable(null, pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()));
|
|
|
|
else
|
|
|
|
ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom);
|
2011-06-15 15:14:45 +00:00
|
|
|
utp.setNewIndexBuffer(ib);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
public Vector2f getTex(float x, float z, Vector2f store) {
|
|
|
|
if (x < 0 || z < 0 || x >= size || z >= size) {
|
|
|
|
store.set(Vector2f.ZERO);
|
|
|
|
return store;
|
|
|
|
}
|
|
|
|
int idx = (int) (z * size + x);
|
|
|
|
return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2),
|
|
|
|
getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) );
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getHeightmapHeight(float x, float z) {
|
|
|
|
if (x < 0 || z < 0 || x >= size || z >= size)
|
|
|
|
return 0;
|
|
|
|
int idx = (int) (z * size + x);
|
|
|
|
return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the triangle of this geometry at the specified local coordinate.
|
|
|
|
* @param gridX local to the terrain patch
|
|
|
|
* @param gridY local to the terrain patch
|
|
|
|
* @return the triangle in world coordinates, or null if the point does intersect this patch on the XZ axis
|
|
|
|
*/
|
|
|
|
public Triangle getTriangle(float x, float z) {
|
|
|
|
return geomap.getTriangleAtPoint(x, z, getWorldScale() , getWorldTranslation());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the triangles at the specified grid point. Probably only 2 triangles
|
|
|
|
* @param gridX local to the terrain patch
|
|
|
|
* @param gridY local to the terrain patch
|
|
|
|
* @return the triangles in world coordinates, or null if the point does intersect this patch on the XZ axis
|
|
|
|
*/
|
|
|
|
public Triangle[] getGridTriangles(float x, float z) {
|
|
|
|
return geomap.getGridTrianglesAtPoint(x, z, getWorldScale() , getWorldTranslation());
|
|
|
|
}
|
|
|
|
|
2011-06-16 23:57:44 +00:00
|
|
|
protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) {
|
2011-03-14 12:55:32 +00:00
|
|
|
|
2011-06-16 23:57:44 +00:00
|
|
|
for (LocationHeight lh : locationHeights) {
|
|
|
|
if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
|
|
|
|
continue;
|
|
|
|
int idx = lh.z * size + lh.x;
|
|
|
|
if (overrideHeight) {
|
2011-11-20 00:24:13 +00:00
|
|
|
geomap.getHeightData()[idx] = lh.h;
|
2011-06-16 23:57:44 +00:00
|
|
|
} else {
|
|
|
|
float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
|
2011-11-20 00:24:13 +00:00
|
|
|
geomap.getHeightData()[idx] = h+lh.h;
|
2011-06-16 23:57:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-03-14 12:55:32 +00:00
|
|
|
FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
|
|
|
|
getMesh().clearBuffer(Type.Position);
|
2011-06-16 23:57:44 +00:00
|
|
|
getMesh().setBuffer(Type.Position, 3, newVertexBuffer);
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* recalculate all of this normal vectors in this terrain patch
|
|
|
|
*/
|
|
|
|
protected void updateNormals() {
|
2011-08-22 23:11:20 +00:00
|
|
|
FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale());
|
2011-03-14 12:55:32 +00:00
|
|
|
getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);
|
2011-08-24 16:12:38 +00:00
|
|
|
FloatBuffer newTangentBuffer = null;
|
|
|
|
FloatBuffer newBinormalBuffer = null;
|
|
|
|
FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());
|
|
|
|
newTangentBuffer = tb[0];
|
|
|
|
newBinormalBuffer = tb[1];
|
|
|
|
getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);
|
|
|
|
getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Matches the normals along the edge of the patch with the neighbours.
|
|
|
|
* Computes the normals for the right, bottom, left, and top edges of the
|
|
|
|
* patch, and saves those normals in the neighbour's edges too.
|
|
|
|
*
|
|
|
|
* Takes 4 points (if has neighbour on that side) for each
|
|
|
|
* point on the edge of the patch:
|
|
|
|
* *
|
|
|
|
* |
|
|
|
|
* *---x---*
|
|
|
|
* |
|
|
|
|
* *
|
|
|
|
*/
|
|
|
|
protected void fixNormalEdges(TerrainPatch right,
|
|
|
|
TerrainPatch bottom,
|
|
|
|
TerrainPatch top,
|
|
|
|
TerrainPatch left,
|
|
|
|
TerrainPatch bottomRight,
|
|
|
|
TerrainPatch bottomLeft,
|
|
|
|
TerrainPatch topRight,
|
|
|
|
TerrainPatch topLeft)
|
|
|
|
{
|
|
|
|
Vector3f rootPoint = new Vector3f();
|
|
|
|
Vector3f rightPoint = new Vector3f();
|
|
|
|
Vector3f leftPoint = new Vector3f();
|
|
|
|
Vector3f topPoint = new Vector3f();
|
|
|
|
Vector3f bottomPoint = new Vector3f();
|
|
|
|
Vector2f rootTex = new Vector2f();
|
|
|
|
Vector2f rightTex = new Vector2f();
|
|
|
|
Vector2f leftTex = new Vector2f();
|
|
|
|
Vector2f topTex = new Vector2f();
|
|
|
|
Vector2f bottomTex = new Vector2f();
|
|
|
|
Vector3f normal = new Vector3f();
|
|
|
|
Vector3f tangent = new Vector3f();
|
|
|
|
|
|
|
|
int s = this.getSize()-1;
|
|
|
|
|
|
|
|
if (right != null) { // right side
|
|
|
|
for (int i=0; i<s+1; i++) {
|
|
|
|
rootPoint.set(s, this.getHeightmapHeight(s,i), i);
|
|
|
|
leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i);
|
|
|
|
rightPoint.set(s+1, right.getHeightmapHeight(1,i), i);
|
|
|
|
this.getTex(s,i, rootTex);
|
|
|
|
this.getTex(s-1,i, leftTex);
|
|
|
|
right.getTex(1,i, rightTex);
|
|
|
|
if (i == 0) { // top
|
|
|
|
if (top == null) {
|
|
|
|
bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
|
|
|
|
this.getTex(s,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), 0);
|
|
|
|
} else {
|
|
|
|
topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1);
|
|
|
|
bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
|
|
|
|
top.getTex(s,s-1, topTex);
|
|
|
|
this.getTex(s,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*(s+1)-1);
|
|
|
|
// update top right corner
|
|
|
|
/*if (topRight != null) {
|
|
|
|
VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer topRightTB = topRight.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)topRightTB.getData(), (s+1)*s);
|
|
|
|
topRightNB.setUpdateNeeded();
|
|
|
|
topRightTB.setUpdateNeeded();
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
} else if (i == s) { // bottom
|
|
|
|
if (bottom == null) {
|
|
|
|
topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
|
|
|
|
this.getTex(s,i-1, topTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, topTex, rootTex, leftTex, null, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i+1)-1);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*(i));
|
|
|
|
} else {
|
|
|
|
topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
|
|
|
|
bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1);
|
|
|
|
this.getTex(s,i-1, topTex);
|
|
|
|
bottom.getTex(s,1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), s);
|
|
|
|
/*if (bottomRight != null) {
|
|
|
|
VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer bottomRightTB = bottomRight.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)bottomRightTB.getData(), 0);
|
|
|
|
bottomRightNB.setUpdateNeeded();
|
|
|
|
bottomRightTB.setUpdateNeeded();
|
|
|
|
}*/
|
|
|
|
downNB.setUpdateNeeded();
|
|
|
|
downTB.setUpdateNeeded();
|
|
|
|
}
|
|
|
|
} else { // all in the middle
|
|
|
|
topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
|
|
|
|
bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
|
|
|
|
this.getTex(s,i-1, topTex);
|
|
|
|
this.getTex(s,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i+1)-1);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
right.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
|
|
|
|
right.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bottom != null) {
|
|
|
|
for (int i=0; i<s+1; i++) {
|
|
|
|
rootPoint.set(i, this.getHeightmapHeight(i,s), s);
|
|
|
|
topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1);
|
|
|
|
bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1);
|
|
|
|
this.getTex(i,s, rootTex);
|
|
|
|
this.getTex(i,s-1, topTex);
|
|
|
|
bottom.getTex(i,1, bottomTex);
|
|
|
|
if (i == 0) { // left
|
|
|
|
/*if (left == null) {
|
|
|
|
rightPoint.set(i+1, this.getHeight(i+1,s), s);
|
|
|
|
averageNormalsTangents(v, t, indexes, topPoint, rootPoint, null, bottomPoint, rightPoint, topTex, rootTex, null, bottomTex, rightTex, normal, tangent);
|
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), 0);
|
|
|
|
} else {
|
|
|
|
leftPoint.set(i-1, left.getHeight(s-1,s), s);
|
|
|
|
rightPoint.set(i+1, this.getHeight(i+1,s), s);
|
|
|
|
left.getTex(i-1,s, leftTex);
|
|
|
|
this.getTex(i+1,s, rightTex);
|
|
|
|
averageNormalsTangents(v, t, indexes, topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), 0);
|
|
|
|
}*/
|
|
|
|
} else if (i == s) { // right
|
|
|
|
|
|
|
|
// handled by right side
|
|
|
|
|
|
|
|
} else { // all in the middle
|
|
|
|
leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s);
|
|
|
|
rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s);
|
|
|
|
this.getTex(i-1,s, leftTex);
|
|
|
|
this.getTex(i+1,s, rightTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(s)+i);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
|
|
|
|
bottom.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left != null) { // left side
|
|
|
|
for (int i=0; i<s+1; i++) {
|
|
|
|
rootPoint.set(0, this.getHeightmapHeight(0,i), i);
|
|
|
|
leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i);
|
|
|
|
rightPoint.set(1, this.getHeightmapHeight(1,i), i);
|
|
|
|
this.getTex(0,i, rootTex);
|
|
|
|
left.getTex(s-1,i, leftTex);
|
|
|
|
this.getTex(1,i, rightTex);
|
|
|
|
if (i == 0) { // top
|
|
|
|
if (top == null) {
|
|
|
|
bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
|
|
|
|
this.getTex(0,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s+1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), s+1);
|
|
|
|
} else {
|
|
|
|
topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1);
|
|
|
|
bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
|
|
|
|
top.getTex(0,i-1, topTex);
|
|
|
|
this.getTex(0,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), 0);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*s);
|
|
|
|
/*if (topLeft != null) {
|
|
|
|
VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer topLeftTB = topLeft.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)topLeftTB.getData(), (s+1)*(s+1)-1);
|
|
|
|
topLeftNB.setUpdateNeeded();
|
|
|
|
topLeftTB.setUpdateNeeded();
|
|
|
|
}*/
|
|
|
|
topNB.setUpdateNeeded();
|
|
|
|
}
|
|
|
|
} else if (i == s) { // bottom
|
|
|
|
|
|
|
|
// handled by bottom
|
|
|
|
|
|
|
|
/*if (bottomLeft != null) {
|
|
|
|
VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer bottomLeftTB = bottomLeft.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)bottomLeftTB.getData(), s);
|
|
|
|
bottomLeftNB.setUpdateNeeded();
|
|
|
|
bottomLeftTB.setUpdateNeeded();
|
|
|
|
}*/
|
|
|
|
|
|
|
|
} else { // all in the middle
|
|
|
|
topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);
|
|
|
|
bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
|
|
|
|
this.getTex(0,i-1, topTex);
|
|
|
|
this.getTex(0,i+1, bottomTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i));
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i));
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), (s+1)*(i+1)-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
left.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
|
|
|
|
left.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top != null) { // top side
|
|
|
|
for (int i=0; i<s+1; i++) {
|
|
|
|
rootPoint.set(i, this.getHeightmapHeight(i,0), 0);
|
|
|
|
topPoint.set(i, top.getHeightmapHeight(i,s-1), -1);
|
|
|
|
bottomPoint.set(i, this.getHeightmapHeight(i,1), 1);
|
|
|
|
this.getTex(i,s, rootTex);
|
|
|
|
top.getTex(i,s-1, topTex);
|
|
|
|
this.getTex(i,1, bottomTex);
|
|
|
|
if (i == 0) { // left
|
|
|
|
|
|
|
|
// handled by left side
|
|
|
|
|
|
|
|
} else if (i == s) { // right
|
|
|
|
|
|
|
|
// handled by right side
|
|
|
|
|
|
|
|
} else { // all in the middle
|
|
|
|
leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0);
|
|
|
|
rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0);
|
|
|
|
this.getTex(i-1,0, leftTex);
|
|
|
|
this.getTex(i+1,0, rightTex);
|
2011-08-24 16:12:38 +00:00
|
|
|
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);
|
2011-03-14 12:55:32 +00:00
|
|
|
VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);
|
|
|
|
VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);
|
|
|
|
VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), i);
|
|
|
|
BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i);
|
|
|
|
BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*(s)+i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
top.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
|
|
|
|
this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();
|
|
|
|
}
|
|
|
|
|
2011-08-24 16:12:38 +00:00
|
|
|
protected void averageNormalsTangents(
|
2011-03-14 12:55:32 +00:00
|
|
|
Vector3f topPoint,
|
|
|
|
Vector3f rootPoint,
|
2011-08-24 16:12:38 +00:00
|
|
|
Vector3f leftPoint,
|
|
|
|
Vector3f bottomPoint,
|
|
|
|
Vector3f rightPoint,
|
2011-03-14 12:55:32 +00:00
|
|
|
Vector2f topTex,
|
|
|
|
Vector2f rootTex,
|
|
|
|
Vector2f leftTex,
|
|
|
|
Vector2f bottomTex,
|
|
|
|
Vector2f rightTex,
|
|
|
|
Vector3f normal,
|
|
|
|
Vector3f tangent)
|
|
|
|
{
|
2011-08-24 16:12:38 +00:00
|
|
|
Vector3f scale = getWorldScale();
|
|
|
|
|
2011-03-14 12:55:32 +00:00
|
|
|
Vector3f n1 = Vector3f.ZERO;
|
|
|
|
if (topPoint != null && leftPoint != null) {
|
2011-09-06 05:31:24 +00:00
|
|
|
n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
Vector3f n2 = Vector3f.ZERO;
|
|
|
|
if (leftPoint != null && bottomPoint != null) {
|
2011-09-06 05:31:24 +00:00
|
|
|
n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
Vector3f n3 = Vector3f.ZERO;
|
|
|
|
if (rightPoint != null && bottomPoint != null) {
|
2011-09-06 05:31:24 +00:00
|
|
|
n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
Vector3f n4 = Vector3f.ZERO;
|
|
|
|
if (rightPoint != null && topPoint != null) {
|
2011-09-06 05:31:24 +00:00
|
|
|
n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
2011-08-24 16:12:38 +00:00
|
|
|
|
|
|
|
Vector3f binormal = new Vector3f();
|
|
|
|
if (topPoint != null && rightPoint != null)
|
|
|
|
LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),topPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,topTex}, tangent, binormal);
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());
|
|
|
|
}
|
|
|
|
|
2011-09-06 05:31:24 +00:00
|
|
|
private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {
|
2011-03-14 12:55:32 +00:00
|
|
|
Vector3f normal = new Vector3f();
|
|
|
|
normal.set(firstPoint).subtractLocal(rootPoint)
|
|
|
|
.crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();
|
|
|
|
return normal;
|
|
|
|
}
|
2011-09-06 05:31:24 +00:00
|
|
|
|
|
|
|
protected Vector3f getMeshNormal(int x, int z) {
|
|
|
|
if (x >= size || z >= size)
|
|
|
|
return null; // out of range
|
|
|
|
|
|
|
|
int index = (z*size+x)*3;
|
|
|
|
FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();
|
|
|
|
Vector3f normal = new Vector3f();
|
|
|
|
normal.x = nb.get(index);
|
|
|
|
normal.y = nb.get(index+1);
|
|
|
|
normal.z = nb.get(index+2);
|
|
|
|
return normal;
|
|
|
|
}
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Locks the mesh (sets it static) to improve performance.
|
|
|
|
* But it it not editable then. Set unlock to make it editable.
|
|
|
|
*/
|
|
|
|
public void lockMesh() {
|
|
|
|
getMesh().setStatic();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unlocks the mesh (sets it dynamic) to make it editable.
|
|
|
|
* It will be editable but performance will be reduced.
|
|
|
|
* Call lockMesh to improve performance.
|
|
|
|
*/
|
|
|
|
public void unlockMesh() {
|
|
|
|
getMesh().setDynamic();
|
|
|
|
}
|
|
|
|
|
2011-06-15 15:14:45 +00:00
|
|
|
/**
|
2011-07-05 19:42:37 +00:00
|
|
|
* Returns the offset amount this terrain patch uses for textures.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @return The current offset amount.
|
|
|
|
*/
|
|
|
|
public float getOffsetAmount() {
|
|
|
|
return offsetAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the step scale that stretches the height map.
|
|
|
|
*
|
|
|
|
* @return The current step scale.
|
|
|
|
*/
|
|
|
|
public Vector3f getStepScale() {
|
|
|
|
return stepScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the total size of the terrain.
|
|
|
|
*
|
|
|
|
* @return The terrain's total size.
|
|
|
|
*/
|
|
|
|
public int getTotalSize() {
|
|
|
|
return totalSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-05 19:42:37 +00:00
|
|
|
* Returns the size of this terrain patch.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
2011-07-05 19:42:37 +00:00
|
|
|
* @return The current patch size.
|
2011-06-15 15:14:45 +00:00
|
|
|
*/
|
|
|
|
public int getSize() {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current offset amount. This is used when building texture
|
|
|
|
* coordinates.
|
|
|
|
*
|
|
|
|
* @return The current offset amount.
|
|
|
|
*/
|
|
|
|
public Vector2f getOffset() {
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the value for the current offset amount to use when building texture
|
|
|
|
* coordinates. Note that this does <b>NOT </b> rebuild the terrain at all.
|
2011-07-05 19:42:37 +00:00
|
|
|
* This is mostly used for outside constructors of terrain patches.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @param offset
|
|
|
|
* The new texture offset.
|
|
|
|
*/
|
|
|
|
public void setOffset(Vector2f offset) {
|
|
|
|
this.offset = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-05 19:42:37 +00:00
|
|
|
* Sets the size of this terrain patch. Note that this does <b>NOT </b>
|
2011-06-15 15:14:45 +00:00
|
|
|
* rebuild the terrain at all. This is mostly used for outside constructors
|
2011-07-05 19:42:37 +00:00
|
|
|
* of terrain patches.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @param size
|
|
|
|
* The new size.
|
|
|
|
*/
|
|
|
|
public void setSize(int size) {
|
|
|
|
this.size = size;
|
|
|
|
|
|
|
|
maxLod = -1; // reset it
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the total size of the terrain . Note that this does <b>NOT </b>
|
|
|
|
* rebuild the terrain at all. This is mostly used for outside constructors
|
2011-07-05 19:42:37 +00:00
|
|
|
* of terrain patches.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @param totalSize
|
|
|
|
* The new total size.
|
|
|
|
*/
|
|
|
|
public void setTotalSize(int totalSize) {
|
|
|
|
this.totalSize = totalSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-05 19:42:37 +00:00
|
|
|
* Sets the step scale of this terrain patch's height map. Note that this
|
2011-06-15 15:14:45 +00:00
|
|
|
* does <b>NOT </b> rebuild the terrain at all. This is mostly used for
|
2011-07-05 19:42:37 +00:00
|
|
|
* outside constructors of terrain patches.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @param stepScale
|
|
|
|
* The new step scale.
|
|
|
|
*/
|
|
|
|
public void setStepScale(Vector3f stepScale) {
|
|
|
|
this.stepScale = stepScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the offset of this terrain texture map. Note that this does <b>NOT
|
|
|
|
* </b> rebuild the terrain at all. This is mostly used for outside
|
2011-07-05 19:42:37 +00:00
|
|
|
* constructors of terrain patches.
|
2011-06-15 15:14:45 +00:00
|
|
|
*
|
|
|
|
* @param offsetAmount
|
|
|
|
* The new texture offset.
|
|
|
|
*/
|
|
|
|
public void setOffsetAmount(float offsetAmount) {
|
|
|
|
this.offsetAmount = offsetAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return Returns the quadrant.
|
|
|
|
*/
|
|
|
|
public short getQuadrant() {
|
|
|
|
return quadrant;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param quadrant
|
|
|
|
* The quadrant to set.
|
|
|
|
*/
|
|
|
|
public void setQuadrant(short quadrant) {
|
|
|
|
this.quadrant = quadrant;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getLod() {
|
|
|
|
return lod;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLod(int lod) {
|
|
|
|
this.lod = lod;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getPreviousLod() {
|
|
|
|
return previousLod;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setPreviousLod(int previousLod) {
|
|
|
|
this.previousLod = previousLod;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int getLodLeft() {
|
|
|
|
return lodLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setLodLeft(int lodLeft) {
|
|
|
|
this.lodLeft = lodLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int getLodTop() {
|
|
|
|
return lodTop;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setLodTop(int lodTop) {
|
|
|
|
this.lodTop = lodTop;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int getLodRight() {
|
|
|
|
return lodRight;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setLodRight(int lodRight) {
|
|
|
|
this.lodRight = lodRight;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int getLodBottom() {
|
|
|
|
return lodBottom;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setLodBottom(int lodBottom) {
|
|
|
|
this.lodBottom = lodBottom;
|
|
|
|
}
|
2011-09-09 23:34:15 +00:00
|
|
|
|
|
|
|
/*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
|
2011-03-14 12:55:32 +00:00
|
|
|
this.lodCalculatorFactory = lodCalculatorFactory;
|
|
|
|
setLodCalculator(lodCalculatorFactory.createCalculator(this));
|
2011-09-09 23:34:15 +00:00
|
|
|
}*/
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
|
|
|
|
if (refreshFlags != 0)
|
|
|
|
throw new IllegalStateException("Scene graph must be updated" +
|
|
|
|
" before checking collision");
|
|
|
|
|
|
|
|
if (other instanceof BoundingVolume)
|
|
|
|
if (!getWorldBound().intersects((BoundingVolume)other))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(other instanceof Ray)
|
|
|
|
return collideWithRay((Ray)other, results);
|
|
|
|
else if (other instanceof BoundingVolume)
|
|
|
|
return collideWithBoundingVolume((BoundingVolume)other, results);
|
|
|
|
else {
|
|
|
|
throw new UnsupportedCollisionException("TerrainPatch cannnot collide with "+other.getClass().getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int collideWithRay(Ray ray, CollisionResults results) {
|
|
|
|
// This should be handled in the root terrain quad
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int collideWithBoundingVolume(BoundingVolume boundingVolume, CollisionResults results) {
|
|
|
|
if (boundingVolume instanceof BoundingBox)
|
|
|
|
return collideWithBoundingBox((BoundingBox)boundingVolume, results);
|
|
|
|
else if(boundingVolume instanceof BoundingSphere) {
|
|
|
|
BoundingSphere sphere = (BoundingSphere) boundingVolume;
|
|
|
|
BoundingBox bbox = new BoundingBox(boundingVolume.getCenter().clone(), sphere.getRadius(),
|
|
|
|
sphere.getRadius(),
|
|
|
|
sphere.getRadius());
|
|
|
|
return collideWithBoundingBox(bbox, results);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected Vector3f worldCoordinateToLocal(Vector3f loc) {
|
|
|
|
Vector3f translated = new Vector3f();
|
|
|
|
translated.x = loc.x/getWorldScale().x - getWorldTranslation().x;
|
|
|
|
translated.y = loc.y/getWorldScale().y - getWorldTranslation().y;
|
|
|
|
translated.z = loc.z/getWorldScale().z - getWorldTranslation().z;
|
|
|
|
return translated;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This most definitely is not optimized.
|
|
|
|
*/
|
|
|
|
private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) {
|
|
|
|
|
|
|
|
// test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time
|
|
|
|
Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
|
|
|
|
Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
|
|
|
|
Vector3f bottomLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
|
|
|
|
Vector3f bottomRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
|
|
|
|
|
|
|
|
Triangle t = getTriangle(topLeft.x, topLeft.z);
|
|
|
|
if (t != null && bbox.collideWith(t, results) > 0)
|
|
|
|
return 1;
|
|
|
|
t = getTriangle(topRight.x, topRight.z);
|
|
|
|
if (t != null && bbox.collideWith(t, results) > 0)
|
|
|
|
return 1;
|
|
|
|
t = getTriangle(bottomLeft.x, bottomLeft.z);
|
|
|
|
if (t != null && bbox.collideWith(t, results) > 0)
|
|
|
|
return 1;
|
|
|
|
t = getTriangle(bottomRight.x, bottomRight.z);
|
|
|
|
if (t != null && bbox.collideWith(t, results) > 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
// box is larger than the points on the terrain, so test against the points
|
|
|
|
for (float z=topLeft.z; z<bottomLeft.z; z+=1) {
|
|
|
|
for (float x=topLeft.x; x<topRight.x; x+=1) {
|
|
|
|
|
|
|
|
if (x < 0 || z < 0 || x >= size || z >= size)
|
|
|
|
continue;
|
|
|
|
t = getTriangle(x,z);
|
|
|
|
if (t != null && bbox.collideWith(t, results) > 0)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void write(JmeExporter ex) throws IOException {
|
2011-07-19 00:00:50 +00:00
|
|
|
// the mesh is removed, and reloaded when read() is called
|
|
|
|
// this reduces the save size to 10% by not saving the mesh
|
|
|
|
Mesh temp = getMesh();
|
|
|
|
mesh = null;
|
|
|
|
|
2011-03-14 12:55:32 +00:00
|
|
|
super.write(ex);
|
|
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
|
|
oc.write(size, "size", 16);
|
|
|
|
oc.write(totalSize, "totalSize", 16);
|
|
|
|
oc.write(quadrant, "quadrant", (short)0);
|
|
|
|
oc.write(stepScale, "stepScale", Vector3f.UNIT_XYZ);
|
|
|
|
oc.write(offset, "offset", Vector3f.UNIT_XYZ);
|
|
|
|
oc.write(offsetAmount, "offsetAmount", 0);
|
2011-09-09 23:34:15 +00:00
|
|
|
//oc.write(lodCalculator, "lodCalculator", null);
|
|
|
|
//oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
|
2011-03-14 12:55:32 +00:00
|
|
|
oc.write(lodEntropy, "lodEntropy", null);
|
|
|
|
oc.write(geomap, "geomap", null);
|
2011-07-19 00:00:50 +00:00
|
|
|
|
|
|
|
setMesh(temp);
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void read(JmeImporter im) throws IOException {
|
|
|
|
super.read(im);
|
|
|
|
InputCapsule ic = im.getCapsule(this);
|
|
|
|
size = ic.readInt("size", 16);
|
|
|
|
totalSize = ic.readInt("totalSize", 16);
|
|
|
|
quadrant = ic.readShort("quadrant", (short)0);
|
|
|
|
stepScale = (Vector3f) ic.readSavable("stepScale", Vector3f.UNIT_XYZ);
|
|
|
|
offset = (Vector2f) ic.readSavable("offset", Vector3f.UNIT_XYZ);
|
|
|
|
offsetAmount = ic.readFloat("offsetAmount", 0);
|
2011-09-09 23:34:15 +00:00
|
|
|
//lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
|
|
|
|
//lodCalculator.setTerrainPatch(this);
|
|
|
|
//lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
|
2011-03-14 12:55:32 +00:00
|
|
|
lodEntropy = ic.readFloatArray("lodEntropy", null);
|
|
|
|
geomap = (LODGeomap) ic.readSavable("geomap", null);
|
2011-07-19 00:00:50 +00:00
|
|
|
|
|
|
|
Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
|
|
|
|
setMesh(regen);
|
2011-08-31 15:49:53 +00:00
|
|
|
//TangentBinormalGenerator.generate(this); // note that this will be removed
|
|
|
|
ensurePositiveVolumeBBox();
|
2011-03-14 12:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public TerrainPatch clone() {
|
|
|
|
TerrainPatch clone = new TerrainPatch();
|
|
|
|
clone.name = name.toString();
|
|
|
|
clone.size = size;
|
|
|
|
clone.totalSize = totalSize;
|
|
|
|
clone.quadrant = quadrant;
|
|
|
|
clone.stepScale = stepScale.clone();
|
|
|
|
clone.offset = offset.clone();
|
|
|
|
clone.offsetAmount = offsetAmount;
|
|
|
|
//clone.lodCalculator = lodCalculator.clone();
|
|
|
|
//clone.lodCalculator.setTerrainPatch(clone);
|
2011-09-09 23:34:15 +00:00
|
|
|
//clone.setLodCalculator(lodCalculatorFactory.clone());
|
2011-03-14 12:55:32 +00:00
|
|
|
clone.geomap = new LODGeomap(size, geomap.getHeightData());
|
|
|
|
clone.setLocalTranslation(getLocalTranslation().clone());
|
|
|
|
Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false);
|
|
|
|
clone.setMesh(m);
|
|
|
|
clone.setMaterial(material.clone());
|
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
2011-08-31 15:49:53 +00:00
|
|
|
protected void ensurePositiveVolumeBBox() {
|
|
|
|
if (getModelBound() instanceof BoundingBox) {
|
|
|
|
if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) {
|
|
|
|
// a correction so the box always has a volume
|
|
|
|
((BoundingBox)getModelBound()).setYExtent(0.001f);
|
|
|
|
updateWorldBound();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-14 12:55:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
}
|