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