* @return the height at the given point\r
*/\r
public float getHeight(Vector2f xz);\r
+ \r
+ /**\r
+ * Get the normal vector for the surface of the terrain at the specified\r
+ * X-Z coordinate. This normal vector can be a close approximation. It does not\r
+ * take into account any normal maps on the material.\r
+ * @param xz the X-Z world coordinate\r
+ * @return the normal vector at the given point\r
+ */\r
+ public Vector3f getNormal(Vector2f xz);\r
\r
/**\r
* Get the heightmap height at the specified X-Z coordinate. This does not\r
\r
Vector3f n1 = Vector3f.ZERO;\r
if (topPoint != null && leftPoint != null) {\r
- n1 = getNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));\r
+ n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));\r
}\r
Vector3f n2 = Vector3f.ZERO;\r
if (leftPoint != null && bottomPoint != null) {\r
- n2 = getNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));\r
+ n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));\r
}\r
Vector3f n3 = Vector3f.ZERO;\r
if (rightPoint != null && bottomPoint != null) {\r
- n3 = getNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));\r
+ n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));\r
}\r
Vector3f n4 = Vector3f.ZERO;\r
if (rightPoint != null && topPoint != null) {\r
- n4 = getNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));\r
+ n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));\r
}\r
\r
Vector3f binormal = new Vector3f();\r
normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());\r
}\r
\r
- private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {\r
+ private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {\r
Vector3f normal = new Vector3f();\r
normal.set(firstPoint).subtractLocal(rootPoint)\r
.crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();\r
return normal;\r
}\r
+ \r
+ protected Vector3f getMeshNormal(int x, int z) {\r
+ if (x >= size || z >= size)\r
+ return null; // out of range\r
+ \r
+ int index = (z*size+x)*3;\r
+ FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();\r
+ Vector3f normal = new Vector3f();\r
+ normal.x = nb.get(index);\r
+ normal.y = nb.get(index+1);\r
+ normal.z = nb.get(index+2);\r
+ return normal;\r
+ }\r
\r
/**\r
* Locks the mesh (sets it static) to improve performance.\r
return Float.NaN;\r
}\r
\r
+ protected Vector3f getMeshNormal(int x, int z) {\r
+ int quad = findQuadrant(x, z);\r
+ int split = (size + 1) >> 1;\r
+ if (children != null) {\r
+ for (int i = children.size(); --i >= 0;) {\r
+ Spatial spat = children.get(i);\r
+ int col = x;\r
+ int row = z;\r
+ boolean match = false;\r
+\r
+ // get the childs quadrant\r
+ int childQuadrant = 0;\r
+ if (spat instanceof TerrainQuad) {\r
+ childQuadrant = ((TerrainQuad) spat).getQuadrant();\r
+ } else if (spat instanceof TerrainPatch) {\r
+ childQuadrant = ((TerrainPatch) spat).getQuadrant();\r
+ }\r
+\r
+ if (childQuadrant == 1 && (quad & 1) != 0) {\r
+ match = true;\r
+ } else if (childQuadrant == 2 && (quad & 2) != 0) {\r
+ row = z - split + 1;\r
+ match = true;\r
+ } else if (childQuadrant == 3 && (quad & 4) != 0) {\r
+ col = x - split + 1;\r
+ match = true;\r
+ } else if (childQuadrant == 4 && (quad & 8) != 0) {\r
+ col = x - split + 1;\r
+ row = z - split + 1;\r
+ match = true;\r
+ }\r
+\r
+ if (match) {\r
+ if (spat instanceof TerrainQuad) {\r
+ return ((TerrainQuad) spat).getMeshNormal(col, row);\r
+ } else if (spat instanceof TerrainPatch) {\r
+ return ((TerrainPatch) spat).getMeshNormal(col, row);\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ return null;\r
+ }\r
\r
public float getHeight(Vector2f xz) {\r
// offset\r
float x = (float)(((xz.x - getLocalTranslation().x) / getLocalScale().x) + (float)totalSize / 2f);\r
float z = (float)(((xz.y - getLocalTranslation().z) / getLocalScale().z) + (float)totalSize / 2f);\r
- float height = getHeight(x, z, xz);\r
+ float height = getHeight(x, z);\r
height *= getLocalScale().y;\r
return height;\r
}\r
* @param x coordinate translated into actual (positive) terrain grid coordinates\r
* @param y coordinate translated into actual (positive) terrain grid coordinates\r
*/\r
- protected float getHeight(float x, float z, Vector2f xz) {\r
+ protected float getHeight(float x, float z) {\r
x-=0.5f;\r
z-=0.5f;\r
float col = FastMath.floor(x);\r
}\r
}\r
\r
+ public Vector3f getNormal(Vector2f xz) {\r
+ // offset\r
+ float x = (float)(((xz.x - getLocalTranslation().x) / getLocalScale().x) + (float)totalSize / 2f);\r
+ float z = (float)(((xz.y - getLocalTranslation().z) / getLocalScale().z) + (float)totalSize / 2f);\r
+ Vector3f normal = getNormal(x, z, xz);\r
+ \r
+ return normal;\r
+ }\r
+ \r
+ protected Vector3f getNormal(float x, float z, Vector2f xz) {\r
+ x-=0.5f;\r
+ z-=0.5f;\r
+ float col = FastMath.floor(x);\r
+ float row = FastMath.floor(z);\r
+ boolean onX = false;\r
+ if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on\r
+ onX = true;\r
+ // v1--v2 ^\r
+ // | / | |\r
+ // | / | |\r
+ // v3--v4 | Z\r
+ // |\r
+ // <-------Y\r
+ // X \r
+ Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z));\r
+ Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z));\r
+ Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z));\r
+ Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z));\r
+ \r
+ return n1.add(n2).add(n3).add(n4).normalize();\r
+ }\r
+ \r
public void setHeight(Vector2f xz, float height) {\r
List<Vector2f> coord = new ArrayList<Vector2f>();\r
coord.add(xz);\r
package jme3test.terrain;
import com.jme3.app.SimpleApplication;
-import com.jme3.bounding.BoundingBox;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
-import com.jme3.export.Savable;
-import com.jme3.export.binary.BinaryExporter;
-import com.jme3.export.binary.BinaryImporter;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
-import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
-import com.jme3.scene.Node;
+import com.jme3.scene.debug.Arrow;
import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import jme3tools.converters.ImageToAwt;
import org.novyon.noise.ShaderUtils;
import org.novyon.noise.basis.FilteredBasis;
private boolean lowerTerrain = false;
private Geometry marker;
+ private Geometry markerNormal;
public static void main(String[] args) {
TerrainTestModifyHeight app = new TerrainTestModifyHeight();
float h = terrain.getHeight(new Vector2f(intersection.x, intersection.z));
Vector3f tl = terrain.getWorldTranslation();
marker.setLocalTranslation(tl.add(new Vector3f(intersection.x, h, intersection.z)) );
+ markerNormal.setLocalTranslation(tl.add(new Vector3f(intersection.x, h, intersection.z)) );
+
+ Vector3f normal = terrain.getNormal(new Vector2f(intersection.x, intersection.z));
+ ((Arrow)markerNormal.getMesh()).setArrowExtent(normal);
}
}
matWire.getAdditionalRenderState().setWireframe(true);
matWire.setColor("Color", ColorRGBA.Green);
- //createTerrain();
- createTerrainGrid();
+ createTerrain();
+ //createTerrainGrid();
DirectionalLight light = new DirectionalLight();
light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
}
private void createMarker() {
+ // collision marker
Sphere sphere = new Sphere(8, 8, 0.5f);
marker = new Geometry("Marker");
marker.setMesh(sphere);
marker.setMaterial(mat);
rootNode.attachChild(marker);
+
+ // surface normal marker
+ Arrow arrow = new Arrow(new Vector3f(0,1,0));
+ markerNormal = new Geometry("MarkerNormal");
+ markerNormal.setMesh(arrow);
+ markerNormal.setMaterial(mat);
+ rootNode.attachChild(markerNormal);
}
}