-
Notifications
You must be signed in to change notification settings - Fork 2
/
Terrain.cs
174 lines (149 loc) · 5.04 KB
/
Terrain.cs
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using Godot;
using System;
struct Corner
{
public Vector3 Vertex;
public Vector2 UV;
}
[Tool]
partial class Terrain : MeshInstance3D
{
[Export] public Vector2I Size = new(10, 10);
[Export] public Noise Noise;
[Export] public bool ShouldUpdate = true;
[Export] public bool Smooth = false;
private Vector3[] _coords;
public override void _Ready()
{
ShouldUpdate = true;
}
public override void _Process(double delta)
{
if (ShouldUpdate)
{
CreateCoords();
ShouldUpdate = false;
}
}
private void CreateCoords()
{
_coords = new Vector3[(Size.X + 1) * (Size.Y + 1)];
for (var x = 0; x <= Size.X; x++)
{
for (var z = 0; z <= Size.Y; z++)
{
// Sample a smooth noise to get a smooth set of points
float y = Noise.GetNoise2D(x, z) * 15;
y = Mathf.Floor(y) / 2f;
_coords[x * Size.Y + z] = new Vector3(x, y, z);
}
}
// UpdateDebug();
CreateMesh();
}
private void UpdateDebug()
{
// Remove all the previous spheres
foreach (var child in GetChildren())
{
if (child is MeshInstance3D meshInstance)
{
RemoveChild(meshInstance);
}
}
// For each coordinate, create a sphere to help us visually see the points on the terrain
foreach (var coord in _coords)
{
var meshInstance = new MeshInstance3D();
var mesh = new SphereMesh();
mesh.Radius = 0.1f;
mesh.Height = 0.1f;
meshInstance.Mesh = mesh;
meshInstance.Position = coord;
AddChild(meshInstance);
}
}
private Color VertexColor(float height)
{
if (height > 2)
{
return Colors.Red;
}
if (height > -2)
{
return Colors.Black;
}
return Colors.Green;
}
private void CreateMesh()
{
var surfaceTool = new SurfaceTool();
surfaceTool.Begin(Mesh.PrimitiveType.Triangles);
for (var x = 0; x < Size.X - 1; x += 1)
{
for (var z = 0; z < Size.Y - 1; z += 1)
{
var coordIdx = (x * Size.Y + z);
var topLeftWrap = new Corner() { Vertex = _coords[coordIdx], UV = new Vector2(0, 0) };
var topRightWrap = new Corner() { Vertex = _coords[coordIdx + 1], UV = new Vector2(1, 0) };
var bottomLeftWrap = new Corner() { Vertex = _coords[coordIdx + Size.X], UV = new Vector2(0, 1) };
var bottomRightWrap = new Corner() { Vertex = _coords[coordIdx + 1 + Size.X], UV = new Vector2(1, 1) };
Corner[] corners;
if (TriangulationCheck(topLeftWrap.Vertex, bottomRightWrap.Vertex))
{
corners = new []
{
bottomLeftWrap,
bottomRightWrap,
topLeftWrap,
bottomRightWrap,
topRightWrap,
topLeftWrap
};
}
else
{
corners = new []
{
bottomLeftWrap,
topRightWrap,
topLeftWrap,
bottomLeftWrap,
bottomRightWrap,
topRightWrap,
};
}
foreach (var corner in corners)
{
surfaceTool.SetUV(corner.UV);
surfaceTool.SetColor(VertexColor(corner.Vertex.Y));
if (!Smooth)
{
surfaceTool.SetSmoothGroup(uint.MaxValue);
}
surfaceTool.AddVertex(corner.Vertex);
}
}
}
// The reason we use surface tool - it can calculate normals for us
surfaceTool.GenerateNormals();
// Finally we generate the mesh
var arrMesh = new ArrayMesh();
surfaceTool.Commit(arrMesh);
Mesh = arrMesh;
// Setup the terrain shader
// This should probably be a saved resource rather than configured via code
var shader = new ShaderMaterial();
shader.Shader = GD.Load<Shader>("res://Terrain.gdshader");
shader.SetShaderParameter("grassTexture", GD.Load<Texture>("res://grass.jpg"));
shader.SetShaderParameter("rockTexture", GD.Load<Texture>("res://rock.jpg"));
shader.SetShaderParameter("sandTexture", GD.Load<Texture>("res://sand.jpg"));
shader.SetShaderParameter("lineThickness", 0.02f);
shader.SetShaderParameter("lineVisibility", 0.5f);
arrMesh.SurfaceSetMaterial(0, shader);
}
private bool TriangulationCheck(Vector3 coord0, Vector3 coord1)
{
return coord0.Y == coord1.Y;
}
}