#include "terrain.hpp" #include "settings.hpp" using namespace cgp; // Evaluate 3D position of the terrain for any (x,y) float evaluate_terrain_height(float x, float y) { //p, h, sigma -> positions, heights and wideness of bell curves vec2 p[] = { {-10, -10}, {5, 5}, {-3, 4}, {6, 4} }; float h[] = { 3.0f, -1.5f, 1.0f, 2.0f }; float sigma[] = { 10.0f, 3.0f, 4.0f, 4.0f }; float z = 0; for (int i = 0; i < 4; i++) { float d = norm(vec2(x, y) - p[i]) / sigma[i]; z += h[i] * std::exp(-d * d); } perlin_noise_parameters parameters; parameters.terrain_height = terrain_length()/10; parameters.octave = 9; parameters.frequency_gain = 2.4; parameters.persistency = 0.33; float perlin_noise = parameters.terrain_height * noise_perlin(vec2(3*x / terrain_length(), 3 * y / terrain_length()), parameters.octave, parameters.persistency, parameters.frequency_gain); float border_mountain = 1 / (0.02 + 0.003 * std::min(std::max(0.0f, terrain_length()/2 - 10 - std::abs(x)), std::max(0.0f, terrain_length()/2 - 10 - std::abs(y)))); return perlin_noise+border_mountain; } mesh create_terrain_mesh(int N) { mesh terrain; // temporary terrain storage (CPU only) terrain.position.resize(N*N); terrain.uv.resize(N * N); terrain.color.resize(N * N); perlin_noise_parameters parameters; parameters.terrain_height = terrain_length()*6/250; parameters.octave = 6; parameters.frequency_gain = 17; parameters.persistency = 0.26; float perlin_noise; // Fill terrain geometry for(int ku=0; ku<N; ++ku) { for(int kv=0; kv<N; ++kv) { // Compute local parametric coordinates (u,v) \in [0,1] float u = ku/(N-1.0f); float v = kv/(N-1.0f); // Compute the real coordinates (x,y) of the terrain in [-terrain_length/2, +terrain_length/2] float x = (u - 0.5f) * terrain_length(); float y = (v - 0.5f) * terrain_length(); // Compute the surface height function at the given sampled coordinate float z = evaluate_terrain_height(x,y); // Store vertex coordinates terrain.position[kv+N*ku] = {x,y,z}; terrain.uv[kv+N*ku] = {20*u,20*v}; // *20 so that the texture isn't too zoomed in (low res) vec3 rock_color = { 0.6,0.6,0.6 }; vec3 grass_color = { 0,0.3,0 }; vec3 snow_color = { 1,1,1 }; //blending parameter for color, la roche est la couleur de base, on rajoute avec les parametres b et b2 exponentiels la neige puis l'herbe (en s'assurant que b+b2 <= 1) perlin_noise = parameters.terrain_height * noise_perlin(vec2(3 * x / terrain_length(), 3 * y / terrain_length()), parameters.octave, parameters.persistency, parameters.frequency_gain); float b = std::min(1.0, exp((z + perlin_noise - terrain_length()/10) / 2) / exp(6)); float b2 = std::min(std::max(0.0, 1.0 - b), exp((terrain_length() / 10 - z + perlin_noise-2) / 2) / exp(6)); terrain.color[kv + N * ku] = (1-b)*rock_color + b * 1.5 * snow_color; terrain.color[kv + N * ku] = (1 - b2) * terrain.color[kv + N * ku] + b2 * grass_color; } } // Generate triangle organization // Parametric surface with uniform grid sampling: generate 2 triangles for each grid cell for(int ku=0; ku<N-1; ++ku) { for(int kv=0; kv<N-1; ++kv) { unsigned int idx = kv + N*ku; // current vertex offset uint3 triangle_1 = {idx, idx+1+N, idx+1}; uint3 triangle_2 = {idx, idx+N, idx+1+N}; terrain.connectivity.push_back(triangle_1); terrain.connectivity.push_back(triangle_2); } } // need to call this function to fill the other buffer with default values (normal, color, etc) terrain.fill_empty_field(); return terrain; } std::vector<cgp::vec3> generate_positions_on_terrain(int N) { std::vector<vec3> rand_pos; float x, y; float d = terrain_length() / 2.0f; for (int i = 0; i < N; i++) { x = rand_interval(-d, d); y = rand_interval(-d, d); rand_pos.push_back({ x,y, evaluate_terrain_height(x, y) }); } return rand_pos; } vec3 terrain_orientation(float x, float y) { float z = evaluate_terrain_height(x, y); float step = 0.1f; vec3 u = { step, 0, evaluate_terrain_height(x + step,y) - z }; vec3 v = { 0, step, evaluate_terrain_height(x,y + step) - z }; return cross(v, u); }