added water
This commit is contained in:
@@ -369,6 +369,12 @@ impl TerrainApp {
|
||||
&mut self.params.snowline_height,
|
||||
0..=255,
|
||||
);
|
||||
labeled_int_slider(
|
||||
ui,
|
||||
"Water Table Height",
|
||||
&mut self.params.water_table_height,
|
||||
0..=255,
|
||||
);
|
||||
labeled_float_slider(
|
||||
ui,
|
||||
"Rock Slope Angle",
|
||||
|
||||
+77
-14
@@ -22,6 +22,7 @@ pub struct GenerationParams {
|
||||
pub erosion_detail: f32,
|
||||
pub erosion_octaves: usize,
|
||||
pub snowline_height: u8,
|
||||
pub water_table_height: u8,
|
||||
pub rock_slope_angle_degrees: f32,
|
||||
}
|
||||
|
||||
@@ -38,6 +39,7 @@ impl Default for GenerationParams {
|
||||
erosion_detail: 1.5,
|
||||
erosion_octaves: 5,
|
||||
snowline_height: 220,
|
||||
water_table_height: 0,
|
||||
rock_slope_angle_degrees: 15.0,
|
||||
}
|
||||
}
|
||||
@@ -54,6 +56,8 @@ pub struct GeneratedImages {
|
||||
#[allow(dead_code)]
|
||||
pub grass_mask_image: GrayImage,
|
||||
#[allow(dead_code)]
|
||||
pub water_mask_image: GrayImage,
|
||||
#[allow(dead_code)]
|
||||
pub rock_mask_image: GrayImage,
|
||||
#[allow(dead_code)]
|
||||
pub snow_mask_image: GrayImage,
|
||||
@@ -135,6 +139,10 @@ fn clamp01(x: f32) -> f32 {
|
||||
clamp(x, 0.0, 1.0)
|
||||
}
|
||||
|
||||
fn mix_u8(a: u8, b: u8, t: f32) -> u8 {
|
||||
mix(a as f32, b as f32, clamp01(t)).round() as u8
|
||||
}
|
||||
|
||||
fn hash22(p: Vec2, random_seed: u64) -> Vec2 {
|
||||
let scrambled_x = splitmix64(random_seed ^ 0x9E37_79B9_7F4A_7C15);
|
||||
let scrambled_y = splitmix64(random_seed ^ 0xBF58_476D_1CE4_E5B9);
|
||||
@@ -401,10 +409,15 @@ pub fn generate(params: &GenerationParams) -> GeneratedImages {
|
||||
width,
|
||||
height,
|
||||
params.snowline_height,
|
||||
params.water_table_height,
|
||||
params.rock_slope_angle_degrees,
|
||||
);
|
||||
let terrain_composite_image =
|
||||
build_soft_light_composite(&terrain_layers.terrain_color_image, &bumpmap_image);
|
||||
let terrain_composite_image = build_soft_light_composite(
|
||||
&terrain_layers.terrain_color_image,
|
||||
&bumpmap_image,
|
||||
&heightmap_image,
|
||||
&terrain_layers.water_mask_image,
|
||||
);
|
||||
|
||||
GeneratedImages {
|
||||
width,
|
||||
@@ -414,6 +427,7 @@ pub fn generate(params: &GenerationParams) -> GeneratedImages {
|
||||
terrain_color_image: terrain_layers.terrain_color_image,
|
||||
terrain_composite_image,
|
||||
grass_mask_image: terrain_layers.grass_mask_image,
|
||||
water_mask_image: terrain_layers.water_mask_image,
|
||||
rock_mask_image: terrain_layers.rock_mask_image,
|
||||
snow_mask_image: terrain_layers.snow_mask_image,
|
||||
}
|
||||
@@ -422,6 +436,7 @@ pub fn generate(params: &GenerationParams) -> GeneratedImages {
|
||||
struct TerrainLayers {
|
||||
terrain_color_image: RgbaImage,
|
||||
grass_mask_image: GrayImage,
|
||||
water_mask_image: GrayImage,
|
||||
rock_mask_image: GrayImage,
|
||||
snow_mask_image: GrayImage,
|
||||
}
|
||||
@@ -460,22 +475,28 @@ fn build_terrain_layers(
|
||||
width: usize,
|
||||
height: usize,
|
||||
snowline_height: u8,
|
||||
water_table_height: u8,
|
||||
rock_slope_angle_degrees: f32,
|
||||
) -> TerrainLayers {
|
||||
let mut terrain_color_image = RgbaImage::new(width as u32, height as u32);
|
||||
let mut grass_mask_image = GrayImage::new(width as u32, height as u32);
|
||||
let mut water_mask_image = GrayImage::new(width as u32, height as u32);
|
||||
let mut rock_mask_image = GrayImage::new(width as u32, height as u32);
|
||||
let mut snow_mask_image = GrayImage::new(width as u32, height as u32);
|
||||
|
||||
let grass_color = Rgba([92, 140, 66, 255]);
|
||||
let low_grass_color = [78, 146, 64];
|
||||
let high_grass_color = [150, 156, 88];
|
||||
let water_color = Rgba([40, 70, 150, 255]);
|
||||
let sand_color = [210, 195, 145];
|
||||
let rock_color = Rgba([122, 112, 98, 255]);
|
||||
let snow_color = Rgba([245, 248, 252, 255]);
|
||||
let snow_color = Rgba([255, 250, 250, 255]);
|
||||
let slope_threshold_radians = rock_slope_angle_degrees.clamp(0.0, 89.9).to_radians();
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let idx = y * width + x;
|
||||
let height_value = heightmap[idx].clamp(0.0, 1.0);
|
||||
let u8_height = (height_value * 255.0).round() as u8;
|
||||
|
||||
let left = sample_height(heightmap, width, height, x.saturating_sub(1), y);
|
||||
let right = sample_height(heightmap, width, height, (x + 1).min(width - 1), y);
|
||||
@@ -486,26 +507,56 @@ fn build_terrain_layers(
|
||||
let dy = (down - up) * 0.5 * height as f32;
|
||||
let slope_angle = dx.hypot(dy).atan();
|
||||
|
||||
let grass_t = clamp01((height_value - 0.08) / 0.52);
|
||||
let grass_color = [
|
||||
mix_u8(low_grass_color[0], high_grass_color[0], grass_t),
|
||||
mix_u8(low_grass_color[1], high_grass_color[1], grass_t),
|
||||
mix_u8(low_grass_color[2], high_grass_color[2], grass_t),
|
||||
];
|
||||
let is_water = if water_table_height == 0 {
|
||||
false
|
||||
} else {
|
||||
u8_height < water_table_height
|
||||
};
|
||||
|
||||
let sand_width = 4.0;
|
||||
let sand_t = if water_table_height == 0 || is_water {
|
||||
0.0
|
||||
} else {
|
||||
let diff = (u8_height - water_table_height) as f32;
|
||||
(1.0 - (diff / sand_width)).clamp(0.0, 1.0)
|
||||
};
|
||||
|
||||
let base_color = Rgba([
|
||||
mix_u8(grass_color[0], sand_color[0], sand_t),
|
||||
mix_u8(grass_color[1], sand_color[1], sand_t),
|
||||
mix_u8(grass_color[2], sand_color[2], sand_t),
|
||||
255,
|
||||
]);
|
||||
|
||||
let is_snow = if snowline_height == 0 {
|
||||
true
|
||||
} else if snowline_height == u8::MAX {
|
||||
false
|
||||
} else {
|
||||
(height_value * 255.0).round() > snowline_height as f32
|
||||
u8_height > snowline_height
|
||||
};
|
||||
let is_rock = slope_angle >= slope_threshold_radians;
|
||||
|
||||
grass_mask_image.put_pixel(x as u32, y as u32, Luma([255]));
|
||||
water_mask_image.put_pixel(x as u32, y as u32, Luma([if is_water { 255 } else { 0 }]));
|
||||
rock_mask_image.put_pixel(x as u32, y as u32, Luma([if is_rock { 255 } else { 0 }]));
|
||||
snow_mask_image.put_pixel(x as u32, y as u32, Luma([if is_snow { 255 } else { 0 }]));
|
||||
|
||||
let mut color = grass_color;
|
||||
if is_rock {
|
||||
color = rock_color;
|
||||
}
|
||||
if is_snow {
|
||||
color = snow_color;
|
||||
}
|
||||
let color = if is_water {
|
||||
water_color
|
||||
} else if is_snow {
|
||||
snow_color
|
||||
} else if is_rock {
|
||||
rock_color
|
||||
} else {
|
||||
base_color
|
||||
};
|
||||
|
||||
terrain_color_image.put_pixel(x as u32, y as u32, color);
|
||||
}
|
||||
@@ -514,18 +565,30 @@ fn build_terrain_layers(
|
||||
TerrainLayers {
|
||||
terrain_color_image,
|
||||
grass_mask_image,
|
||||
water_mask_image,
|
||||
rock_mask_image,
|
||||
snow_mask_image,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_soft_light_composite(color_image: &RgbaImage, bumpmap_image: &GrayImage) -> RgbaImage {
|
||||
fn build_soft_light_composite(
|
||||
color_image: &RgbaImage,
|
||||
bumpmap_image: &GrayImage,
|
||||
heightmap_image: &GrayImage,
|
||||
water_mask_image: &GrayImage,
|
||||
) -> RgbaImage {
|
||||
let mut composite = RgbaImage::new(color_image.width(), color_image.height());
|
||||
|
||||
for y in 0..color_image.height() {
|
||||
for x in 0..color_image.width() {
|
||||
let base = color_image.get_pixel(x, y).0;
|
||||
let blend = bumpmap_image.get_pixel(x, y).0[0] as f32 / 255.0;
|
||||
let is_water = water_mask_image.get_pixel(x, y).0[0] > 0;
|
||||
|
||||
let blend = if is_water {
|
||||
heightmap_image.get_pixel(x, y).0[0] as f32 / 255.0
|
||||
} else {
|
||||
bumpmap_image.get_pixel(x, y).0[0] as f32 / 255.0
|
||||
};
|
||||
|
||||
let r = soft_light_channel(base[0] as f32 / 255.0, blend);
|
||||
let g = soft_light_channel(base[1] as f32 / 255.0, blend);
|
||||
|
||||
Reference in New Issue
Block a user