Skip to content

Commit dab094b

Browse files
committed
Terrain rendering improvements.
Added a specific terrain phong shader. Added multiple textures to represent terrain types calculated by factor in the phong_terrain.frag shader. Converted mesh generation for terrain to use indexing. Additionally the normals are now averaged over the verts for smoother lighting.
1 parent 0fc8ea4 commit dab094b

File tree

7 files changed

+361
-30
lines changed

7 files changed

+361
-30
lines changed

source/Component/Terrain.cpp

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "System/AssetManager.hpp"
44
#include "Utility/MeshBuilder.hpp"
55
#include "Utility/PerlinNoise.hpp"
6+
#include "Utility/Stopwatch.hpp"
67

78
#include "imgui.h"
89

@@ -15,7 +16,12 @@ Component::Terrain::Terrain(const glm::vec3& p_position, int p_size_x, int p_siz
1516
, m_lacunarity{2.f}
1617
, m_persistence{0.5f}
1718
, 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{}
1925
, m_seed{Utility::get_random_number<unsigned int>()}
2026
, m_mesh{generate_mesh(m_seed)}
2127
{}
@@ -28,7 +34,12 @@ Component::Terrain::Terrain(const Terrain& p_other) noexcept
2834
, m_lacunarity{p_other.m_lacunarity}
2935
, m_persistence{p_other.m_persistence}
3036
, 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}
3243
, m_seed{p_other.m_seed}
3344
, m_mesh{generate_mesh(m_seed)} // TODO: Implement a Data::Mesh copy.
3445
{}
@@ -42,7 +53,12 @@ Component::Terrain& Component::Terrain::operator=(const Terrain& p_other) noexce
4253
m_lacunarity = p_other.m_lacunarity;
4354
m_persistence = p_other.m_persistence;
4455
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;
4662
m_seed = p_other.m_seed;
4763
m_mesh = generate_mesh(m_seed); // TODO: Implement a Data::Mesh copy.
4864
return *this;
@@ -68,31 +84,92 @@ float Component::Terrain::compute_height(float p_x, float p_z, const siv::Perlin
6884

6985
Data::Mesh Component::Terrain::generate_mesh(unsigned int p_seed) noexcept
7086
{
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);
7487
const siv::PerlinNoise perlin{p_seed};
88+
std::vector<Data::Vertex> unique_verts;
89+
std::vector<unsigned int> indices;
7590

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)
7894
{
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);
84100
}
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;
85113

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};
87158
}
88159

89160
void Component::Terrain::draw_UI(System::AssetManager& p_asset_manager)
90161
{
91162
if (ImGui::TreeNode("Terrain"))
92163
{
93164
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);
96173

97174
ImGui::SeparatorText("Generation settings");
98175
static bool m_regen_on_changes = true;
@@ -112,12 +189,25 @@ void Component::Terrain::draw_UI(System::AssetManager& p_asset_manager)
112189
changed = true;
113190
m_seed = Utility::get_random_number<unsigned int>();
114191
}
192+
193+
static auto most_recent_time_taken_s = std::optional<float>{};
115194
if (ImGui::Button("Re-generate terrain") || (changed && m_regen_on_changes))
195+
{
196+
Utility::Stopwatch stopwatch;
116197
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+
}
117206

118207
ImGui::SameLine();
119208
ImGui::Checkbox("Regen on changes", &m_regen_on_changes);
120209

210+
121211
ImGui::TreePop();
122212
}
123213
}

source/Component/Terrain.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@ namespace Component
2828
float m_lacunarity;
2929
float m_persistence;
3030
int m_octaves;
31-
TextureRef m_texture;
31+
32+
TextureRef m_grass_tex;
33+
TextureRef m_gravel_tex;
34+
TextureRef m_ground_tex;
35+
TextureRef m_rock_tex;
36+
TextureRef m_sand_tex;
37+
TextureRef m_snow_tex;
38+
3239
unsigned int m_seed; // Seed used to generate m_mesh.
3340
Data::Mesh m_mesh;
3441

0 commit comments

Comments
 (0)