* @return the normal image.
*/
public static Image constructNormalMap(final float[] heightmap, final int side, final double heightScale,
final double xGridSpacing, final double zGridSpacing) {
int x, z;
final Vector3 n = new Vector3();
final Vector3 n2 = new Vector3();
final ByteBuffer data = ByteBuffer.allocateDirect(side * side * 3);
final Image normalMap = new Image(ImageDataFormat.RGB, PixelDataType.UnsignedByte, side, side, data, null);
for (z = 0; z < side; ++z) {
for (x = 0; x < side; ++x) {
if (x == 0 || z == 0 || x == side - 1 || z == side - 1) {
n.set(0, 0, 1);
} else {
// change across "x" from point to our "left" to point on our "right"
double dXh = heightmap[z * side + x - 1] - heightmap[z * side + x + 1];
if (dXh != 0) {
// alter by our height scale
dXh *= heightScale;
// determine slope of perpendicular line
final double slopeX = 2.0 * xGridSpacing / dXh;
// now plug into cos(arctan(x)) to get unit length vector
n.setX(Math.copySign(1.0 / Math.sqrt(1 + slopeX * slopeX), dXh));
n.setY(0);
n.setZ(Math.abs(slopeX * n.getX()));
} else {
n.set(0, 0, 1);
}
// change across "z" from point "above" us to point "below" us
double dZh = heightmap[(z - 1) * side + x] - heightmap[(z + 1) * side + x];
if (dZh != 0) {
// alter by our height scale
dZh *= heightScale;
// determine slope of perpendicular line
final double slopeZ = 2.0 * zGridSpacing / dZh;
// now plug into cos(arctan(x)) to get unit length vector
n2.setX(0);
n2.setY(Math.copySign(1.0 / Math.sqrt(1 + slopeZ * slopeZ), dZh));
n2.setZ(Math.abs(slopeZ * n2.getY()));
} else {
n2.set(0, 0, 1);
}
// add together the vectors across X and Z and normalize to get final normal
n.addLocal(n2).normalizeLocal();
}