-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHeightmapToMesh.cs
More file actions
140 lines (107 loc) · 4.54 KB
/
HeightmapToMesh.cs
File metadata and controls
140 lines (107 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using UnityEngine;
[ExecuteInEditMode]
public class HeightmapToMesh : MonoBehaviour
{
public MeshFilter meshFilter;
public Texture2D heightmap;
[Space]
[Tooltip("This value should be the same as the source terrain's height as configured in its settings")]
public float heightScale = 100;
[Range(8, 8197)]
[Tooltip("Uniform scale of the output mesh")]
public int size = 512;
[Range(1, 128)]
[Tooltip("Distance between vertices in meters/units")]
public int vertexDistance = 32;
private const int MIN_SIZE = 16;
private const int MAX_SIZE = 16392;
private void OnEnable()
{
if (!meshFilter) meshFilter = GetComponent<MeshFilter>();
}
private void OnValidate()
{
if (!meshFilter || !heightmap) return;
meshFilter.sharedMesh = CreateMeshFromHeightmap(heightmap, size, heightScale, vertexDistance);
}
//Note: standalone function, can be moved to an editor script
public static Mesh CreateMeshFromHeightmap(Texture2D heightmap, int size, float heightScale, int vertexDistance)
{
Mesh m = new Mesh();
m.name = heightmap.name + "_mesh";
int m_size = Mathf.Clamp(size, MIN_SIZE, MAX_SIZE);
float vertexDensity = (m_size / vertexDistance);
vertexDensity = Mathf.Clamp(vertexDensity, 1, 256);
int xAmount = (int)vertexDensity + 1;
int yAmount = (int)vertexDensity + 1;
int vertexCount = xAmount * yAmount;
int triangleCount = (int)vertexDensity * (int)vertexDensity * 6;
Vector3[] vertices = new Vector3[vertexCount];
Vector2[] uvs = new Vector2[vertexCount];
int[] triangles = new int[triangleCount];
int vertIndex = 0;
//Ensure mesh retains size when changing vertex distance
float scaleX = m_size / vertexDensity;
float scaleY = m_size / vertexDensity;
//Downscale heightmap texture if size is smaller than heightmap
Texture2D m_heightmap = CopyAndScaleTexture(heightmap, m_size);
for (int y = 0; y < xAmount; y++)
{
for (int x = 0; x < yAmount; x++)
{
//Texture sample positions
//0-1 range
float sX = (float)x / (float)xAmount;
//Scale by texel size
sX = Mathf.FloorToInt(m_heightmap.width * sX);
float sY = (float)y / (float)yAmount;
sY = Mathf.FloorToInt(m_heightmap.height * sY);
float height = m_heightmap.GetPixel((int)sX, (int)sY).r * heightScale;
vertices[vertIndex] = new Vector3(x * scaleX, height, y * scaleY);
//Planar UV mapping on Y-axis
uvs[vertIndex++] = new Vector2(x * (1f / vertexDensity), y * (1f / vertexDensity));
}
}
vertIndex = 0;
for (int y = 0; y < vertexDensity; y++)
{
for (int x = 0; x < vertexDensity; x++)
{
triangles[vertIndex] = (y * xAmount) + x;
triangles[vertIndex + 1] = ((y + 1) * xAmount) + x;
triangles[vertIndex + 2] = (y * xAmount) + x + 1;
triangles[vertIndex + 3] = ((y + 1) * xAmount) + x;
triangles[vertIndex + 4] = ((y + 1) * xAmount) + x + 1;
triangles[vertIndex + 5] = (y * xAmount) + x + 1;
vertIndex += 6;
}
}
m.vertices = vertices;
m.uv = uvs;
m.triangles = triangles;
m.RecalculateNormals();
m.RecalculateTangents();
m.RecalculateBounds();
return m;
}
//Even if it doesn't require scaling, passing the texture through the GPU also has the benefit of the source heightmap not needing to be readable
public static Texture2D CopyAndScaleTexture(Texture2D src, int resolution)
{
//Never upscale
if (resolution > src.width) resolution = src.width;
Rect texRect = new Rect(0, 0, resolution, resolution);
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGBFloat);
rt.isPowerOfTwo = false;
//Render quad
Graphics.SetRenderTarget(rt);
GL.LoadPixelMatrix(0, 1, 1, 0);
GL.Clear(true, true, Color.black);
Graphics.DrawTexture(new Rect(0, 0, 1, 1), src);
//GPU back to CPU
Texture2D result = new Texture2D(resolution, resolution, TextureFormat.RGBAFloat, false, true);
result.ReadPixels(texRect, 0, 0, false);
result.name = src.name;
result.Apply();
return result;
}
}