3
3
#include " System/AssetManager.hpp"
4
4
#include " Utility/MeshBuilder.hpp"
5
5
#include " Utility/PerlinNoise.hpp"
6
+ #include " Utility/Stopwatch.hpp"
6
7
7
8
#include " imgui.h"
8
9
@@ -15,7 +16,12 @@ Component::Terrain::Terrain(const glm::vec3& p_position, int p_size_x, int p_siz
15
16
, m_lacunarity{2 .f }
16
17
, m_persistence{0 .5f }
17
18
, m_octaves{4 }
18
- , m_texture{}
19
+ , m_grass_tex{}
20
+ , m_gravel_tex{}
21
+ , m_ground_tex{}
22
+ , m_rock_tex{}
23
+ , m_sand_tex{}
24
+ , m_snow_tex{}
19
25
, m_seed{Utility::get_random_number<unsigned int >()}
20
26
, m_mesh{generate_mesh (m_seed)}
21
27
{}
@@ -28,7 +34,12 @@ Component::Terrain::Terrain(const Terrain& p_other) noexcept
28
34
, m_lacunarity{p_other.m_lacunarity }
29
35
, m_persistence{p_other.m_persistence }
30
36
, m_octaves{p_other.m_octaves }
31
- , m_texture{p_other.m_texture }
37
+ , m_grass_tex{p_other.m_grass_tex }
38
+ , m_gravel_tex{p_other.m_gravel_tex }
39
+ , m_ground_tex{p_other.m_ground_tex }
40
+ , m_rock_tex{p_other.m_rock_tex }
41
+ , m_sand_tex{p_other.m_sand_tex }
42
+ , m_snow_tex{p_other.m_snow_tex }
32
43
, m_seed{p_other.m_seed }
33
44
, m_mesh{generate_mesh (m_seed)} // TODO: Implement a Data::Mesh copy.
34
45
{}
@@ -42,7 +53,12 @@ Component::Terrain& Component::Terrain::operator=(const Terrain& p_other) noexce
42
53
m_lacunarity = p_other.m_lacunarity ;
43
54
m_persistence = p_other.m_persistence ;
44
55
m_octaves = p_other.m_octaves ;
45
- m_texture = p_other.m_texture ;
56
+ m_grass_tex = p_other.m_grass_tex ;
57
+ m_gravel_tex = p_other.m_gravel_tex ;
58
+ m_ground_tex = p_other.m_ground_tex ;
59
+ m_rock_tex = p_other.m_rock_tex ;
60
+ m_sand_tex = p_other.m_sand_tex ;
61
+ m_snow_tex = p_other.m_snow_tex ;
46
62
m_seed = p_other.m_seed ;
47
63
m_mesh = generate_mesh (m_seed); // TODO: Implement a Data::Mesh copy.
48
64
return *this ;
@@ -68,31 +84,92 @@ float Component::Terrain::compute_height(float p_x, float p_z, const siv::Perlin
68
84
69
85
Data::Mesh Component::Terrain::generate_mesh (unsigned int p_seed) noexcept
70
86
{
71
- // Use perlin noise to generate a heightmap in the xz plane.
72
- auto mb = Utility::MeshBuilder<Data::Vertex, OpenGL::PrimitiveMode::Triangles>{};
73
- mb.reserve ((m_size_x * m_size_z) * 6 );
74
87
const siv::PerlinNoise perlin{p_seed};
88
+ std::vector<Data::Vertex> unique_verts;
89
+ std::vector<unsigned int > indices;
75
90
76
- for (float x = 0 ; x < m_size_x; x++)
77
- for (float z = 0 ; z < m_size_z; z++)
91
+ for (int z = 0 ; z <= m_size_z; ++z)
92
+ {
93
+ for (int x = 0 ; x <= m_size_x; ++x)
78
94
{
79
- mb. add_quad (
80
- glm::vec3 (x + 1 , compute_height (x + 1 , z, perlin), z),
81
- glm::vec3 (x + 1 , compute_height (x + 1 , z + 1 , perlin), z + 1 ),
82
- glm::vec3 (x, compute_height (x, z, perlin), z),
83
- glm::vec3 (x, compute_height (x, z + 1 , perlin), z + 1 ) );
95
+ Data::Vertex vert;
96
+ auto y = compute_height (( float )x, ( float ) z, perlin);
97
+ vert. position = {x, y, z};
98
+ vert. normal = { 0 . f , 0 . f , 0 . f }; // Calculate normals later
99
+ unique_verts. push_back (vert );
84
100
}
101
+ }
102
+
103
+ // Generate indices for each quad (two triangles per quad)
104
+ for (int z = 0 ; z < m_size_z; ++z)
105
+ {
106
+ for (int x = 0 ; x < m_size_x; ++x)
107
+ {
108
+ // Get the indices for the four corners of the current quad
109
+ unsigned int top_left = z * (m_size_x + 1 ) + x;
110
+ unsigned int top_right = top_left + 1 ;
111
+ unsigned int bottom_left = (z + 1 ) * (m_size_x + 1 ) + x;
112
+ unsigned int bottom_right = bottom_left + 1 ;
85
113
86
- return mb.get_mesh ();
114
+ // First triangle (top-left, bottom-left, top-right)
115
+ indices.push_back (top_left);
116
+ indices.push_back (bottom_left);
117
+ indices.push_back (top_right);
118
+ // Second triangle (top-right, bottom-left, bottom-right)
119
+ indices.push_back (top_right);
120
+ indices.push_back (bottom_left);
121
+ indices.push_back (bottom_right);
122
+
123
+ // Calculate normals for the first triangle then accumulate the normal to each of the triangle's vertices
124
+ glm::vec3 v0 = unique_verts[top_left].position ;
125
+ glm::vec3 v1 = unique_verts[bottom_left].position ;
126
+ glm::vec3 v2 = unique_verts[top_right].position ;
127
+ glm::vec3 normal = glm::normalize (glm::cross (v1 - v0, v2 - v0));
128
+ unique_verts[top_left].normal += normal;
129
+ unique_verts[bottom_left].normal += normal;
130
+ unique_verts[top_right].normal += normal;
131
+
132
+ // Calculate normals for the second triangle
133
+ // Accumulate the normal to each of the triangle's vertices
134
+ v0 = unique_verts[top_right].position ;
135
+ v1 = unique_verts[bottom_left].position ;
136
+ v2 = unique_verts[bottom_right].position ;
137
+ normal = glm::normalize (glm::cross (v1 - v0, v2 - v0));
138
+ unique_verts[top_right].normal += normal;
139
+ unique_verts[bottom_left].normal += normal;
140
+ unique_verts[bottom_right].normal += normal;
141
+
142
+ // Calculate UV coordinates based on vertex position within the terrain grid.
143
+ // UV coordinates range from 0.0 to size_x or size_z
144
+ float u = static_cast <float >(x);
145
+ float v = static_cast <float >(z);
146
+ unique_verts[top_left].uv = {u, v + 1 .0f };
147
+ unique_verts[bottom_left].uv = {u, v};
148
+ unique_verts[top_right].uv = {u + 1 .0f , v + 1 .0f };
149
+ unique_verts[bottom_right].uv = {u + 1 .0f , v};
150
+ }
151
+ }
152
+
153
+ // Normalize the vertex normals
154
+ for (auto & vert : unique_verts)
155
+ vert.normal = glm::normalize (vert.normal );
156
+
157
+ return Data::Mesh{std::move (unique_verts), std::move (indices), OpenGL::PrimitiveMode::Triangles};
87
158
}
88
159
89
160
void Component::Terrain::draw_UI (System::AssetManager& p_asset_manager)
90
161
{
91
162
if (ImGui::TreeNode (" Terrain" ))
92
163
{
93
164
m_mesh.draw_UI ();
94
- ImGui::SeparatorText (" Mesh settings" );
95
- p_asset_manager.draw_texture_selector (" Texture" , m_texture);
165
+
166
+ ImGui::SeparatorText (" Textures" );
167
+ p_asset_manager.draw_texture_selector (" Grass texture" , m_grass_tex);
168
+ p_asset_manager.draw_texture_selector (" Gravel texture" , m_gravel_tex);
169
+ p_asset_manager.draw_texture_selector (" Rock texture" , m_rock_tex);
170
+ p_asset_manager.draw_texture_selector (" Ground texture" , m_ground_tex);
171
+ p_asset_manager.draw_texture_selector (" Sand texture" , m_sand_tex);
172
+ p_asset_manager.draw_texture_selector (" Snow texture" , m_snow_tex);
96
173
97
174
ImGui::SeparatorText (" Generation settings" );
98
175
static bool m_regen_on_changes = true ;
@@ -112,12 +189,25 @@ void Component::Terrain::draw_UI(System::AssetManager& p_asset_manager)
112
189
changed = true ;
113
190
m_seed = Utility::get_random_number<unsigned int >();
114
191
}
192
+
193
+ static auto most_recent_time_taken_s = std::optional<float >{};
115
194
if (ImGui::Button (" Re-generate terrain" ) || (changed && m_regen_on_changes))
195
+ {
196
+ Utility::Stopwatch stopwatch;
116
197
m_mesh = generate_mesh (m_seed);
198
+ most_recent_time_taken_s = stopwatch.getTime <std::ratio<1 , 1 >, float >();
199
+ }
200
+ if (most_recent_time_taken_s)
201
+ {
202
+ ImGui::SameLine ();
203
+ auto formatted_time = Utility::format_number (*most_recent_time_taken_s, 1 );
204
+ ImGui::Text_Manual (" %ss" , formatted_time.c_str ());
205
+ }
117
206
118
207
ImGui::SameLine ();
119
208
ImGui::Checkbox (" Regen on changes" , &m_regen_on_changes);
120
209
210
+
121
211
ImGui::TreePop ();
122
212
}
123
213
}
0 commit comments