using UnityEngine;
using System.Collections;
using System.IO;
namespace Gaia
{
///
/// Unity integration extension to the heightmap class
///
public class UnityHeightMap : HeightMap
{
public Bounds m_boundsWU = new Bounds();
///
/// Create a unity heightmap
///
public UnityHeightMap() : base()
{
}
///
/// Create a unity heightmap by loading from a source file
///
/// Paht of file to load
public UnityHeightMap(string path) : base(path)
{
m_boundsWU.size = new Vector3(m_widthX, 0f, m_depthZ);
m_isDirty = false;
}
///
/// Create a unity heightmap by loading a TextAsset
///
/// The text asset to be loaded
public UnityHeightMap(TextAsset source) : base(source.bytes)
{
m_boundsWU.size = new Vector3(m_widthX, 0f, m_depthZ);
m_isDirty = false;
}
///
/// Create a unity heightmap by replicating a source file
///
/// Source heightmap
public UnityHeightMap(UnityHeightMap source) : base(source)
{
m_boundsWU = source.m_boundsWU;
m_isDirty = false;
}
///
/// Create from terrain
///
///
public UnityHeightMap(Terrain terrain) : base()
{
LoadFromTerrain(terrain);
}
///
/// Create a heightmap by reading in and processing an image file
///
/// Bounds in world units
/// Source file
/// Width
/// Depth
public UnityHeightMap(Bounds bounds, string sourceFile) : base(sourceFile)
{
m_boundsWU = bounds;
m_isDirty = false;
}
///
/// Initializes a new instance of the class from a texture.
///
/// Texture.
public UnityHeightMap(Texture2D texture, GaiaConstants.ImageChannel channel = GaiaConstants.ImageChannel.R) : base()
{
LoadFromTexture2D(texture, channel);
m_isDirty = false;
}
///
/// Get bounds in world units
///
/// Terrain bounds in world units
public Bounds GetBoundsWU()
{
return m_boundsWU;
}
///
/// Get position in world units
///
/// Position in world units
public Vector3 GetPositionWU()
{
Vector3 pos = m_boundsWU.center - m_boundsWU.extents;
return pos;
}
///
/// Set the bounds in world units
///
///
public void SetBoundsWU(Bounds bounds)
{
m_boundsWU = bounds;
m_isDirty = true;
}
///
/// Set the position in world units
///
///
public void SetPositionWU(Vector3 position)
{
m_boundsWU.center = position;
m_isDirty = true;
}
///
/// Load this height map from the supplied terrain
///
/// Terrain to load
public void LoadFromTerrain(Terrain terrain)
{
Reset();
m_boundsWU.center = terrain.transform.position;
m_boundsWU.size = terrain.terrainData.size;
m_boundsWU.center += m_boundsWU.extents;
m_widthX = terrain.terrainData.heightmapResolution;
m_depthZ = terrain.terrainData.heightmapResolution;
m_widthInvX = 1f / (float)(m_widthX);
m_depthInvZ = 1f / (float)(m_depthZ);
m_heights = terrain.terrainData.GetHeights(0, 0, m_widthX, m_depthZ);
m_isPowerOf2 = Gaia.GaiaUtils.Math_IsPowerOf2(m_widthX) && Gaia.GaiaUtils.Math_IsPowerOf2(m_depthZ);
m_isDirty = false;
}
///
/// Update the supplied terrain with the content of this heightmap - will scale if necessary
///
/// Terrain to uppdate
public void SaveToTerrain(Terrain terrain)
{
if (terrain == null)
{
return;
}
//Get terrain stats
int terWidth = terrain.terrainData.heightmapResolution;
int terDepth = terrain.terrainData.heightmapResolution;
//Direct one to one mapping
if (m_widthX == terWidth && m_depthZ == terDepth)
{
terrain.terrainData.SetHeights(0, 0, m_heights);
m_isDirty = false;
return;
}
//Build new array and scale it to the size of the terrain
float[,] heights = new float[terWidth, terDepth];
for (int x = 0; x < terWidth; x++)
{
for (int z = 0; z < terDepth; z++)
{
heights[x,z] = this[((float)x / (float)terWidth), ((float)z / (float)terDepth)];
}
}
//And apply it
terrain.terrainData.SetHeights(0, 0, heights);
m_isDirty = false;
}
public void LoadFromTexture2D(Texture2D texture, GaiaConstants.ImageChannel channel)
{
//Check if it is readable - if not then make it readable
Gaia.GaiaUtils.MakeTextureReadable(texture);
//Make sure its not compressed
Gaia.GaiaUtils.MakeTextureUncompressed(texture);
//And load
m_widthX = texture.width;
m_depthZ = texture.height;
m_widthInvX = 1f / (float)(m_widthX);
m_depthInvZ = 1f / (float)(m_depthZ);
m_heights = new float[m_widthX, m_depthZ];
m_isPowerOf2 = Gaia.GaiaUtils.Math_IsPowerOf2(m_widthX) && Gaia.GaiaUtils.Math_IsPowerOf2(m_depthZ);
if (channel == GaiaConstants.ImageChannel.R)
{
for (int x = 0; x < m_widthX; x++)
{
for (int z = 0; z < m_depthZ; z++)
{
m_heights[x, z] = texture.GetPixel(x, z).r;
}
}
}
else if (channel == GaiaConstants.ImageChannel.G)
{
for (int x = 0; x < m_widthX; x++)
{
for (int z = 0; z < m_depthZ; z++)
{
m_heights[x, z] = texture.GetPixel(x, z).g;
}
}
}
else if (channel == GaiaConstants.ImageChannel.B)
{
for (int x = 0; x < m_widthX; x++)
{
for (int z = 0; z < m_depthZ; z++)
{
m_heights[x, z] = texture.GetPixel(x, z).b;
}
}
}
else
{
for (int x = 0; x < m_widthX; x++)
{
for (int z = 0; z < m_depthZ; z++)
{
m_heights[x, z] = texture.GetPixel(x, z).a;
}
}
}
m_isDirty = false;
}
///
/// Read heightmap from the supplied RAW file supplied as a text asset
///
/// True on success
public void ReadRawFromTextAsset(TextAsset asset)
{
using (Stream s = new MemoryStream(asset.bytes))
{
using (BinaryReader br = new BinaryReader(s))
{
m_widthX = m_depthZ = Mathf.CeilToInt(Mathf.Sqrt(s.Length / 2));
m_widthInvX = 1f / (float)(m_widthX);
m_depthInvZ = 1f / (float)(m_depthZ);
m_heights = new float[m_widthX, m_depthZ];
m_isPowerOf2 = Gaia.GaiaUtils.Math_IsPowerOf2(m_widthX) && Gaia.GaiaUtils.Math_IsPowerOf2(m_depthZ);
for (int hmX = 0; hmX < m_widthX; hmX++)
{
for (int hmZ = 0; hmZ < m_depthZ; hmZ++)
{
m_heights[hmX, hmZ] = (float)br.ReadUInt16() / 65535.0f;
}
}
}
s.Close();
}
m_isDirty = false;
}
///
/// Returns a heightmap that only contains the single color channel info of the original map
///
/// The color channel to return
///
public UnityHeightMap GetColorChannelHeightMap()
{
UnityHeightMap returnMap = new UnityHeightMap(this);
for (int hmX = 0; hmX < m_widthX; hmX++)
{
for (int hmZ = 0; hmZ < m_depthZ; hmZ++)
{
returnMap.m_heights[hmX, hmZ] = this.m_heights[hmX, hmZ] / 3f;
}
}
return returnMap;
}
///
/// Calculate the normals for the heightmap
///
/// Normals for the heightmap
public Texture2D CalculateNormals()
{
float terrainHeight = m_widthX / 2f;
int width = m_widthX;
int height = m_depthZ;
float ux = 1.0f / (width - 1.0f);
float uy = 1.0f / (height - 1.0f);
float scaleX = terrainHeight / (float)m_widthX;
float scaleY = terrainHeight / (float)m_depthZ;
float[] heights = Heights1D();
Texture2D normalMap = new Texture2D(width, height, TextureFormat.RGBAFloat, false, true);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int xp1 = (x == width - 1) ? x : x + 1;
int xn1 = (x == 0) ? x : x - 1;
int yp1 = (y == height - 1) ? y : y + 1;
int yn1 = (y == 0) ? y : y - 1;
float l = heights[xn1 + y * width] * scaleX;
float r = heights[xp1 + y * width] * scaleX;
float b = heights[x + yn1 * width] * scaleY;
float t = heights[x + yp1 * width] * scaleY;
float dx = (r - l) / (2.0f * ux);
float dy = (t - b) / (2.0f * uy);
Vector3 normal;
normal.x = -dx;
normal.y = -dy;
normal.z = 1;
normal.Normalize();
Color pixel;
pixel.r = normal.x * 0.5f + 0.5f;
pixel.g = normal.y * 0.5f + 0.5f;
pixel.b = normal.z;
pixel.a = 1.0f;
normalMap.SetPixel(x, y, pixel);
}
}
normalMap.Apply();
return normalMap;
}
}
}