mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/small-fixes' into 'master'
Caverns See merge request veloren/veloren!2763
This commit is contained in:
commit
3357008601
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6275,6 +6275,7 @@ dependencies = [
|
||||
"ordered-float 2.8.0",
|
||||
"profiling",
|
||||
"rand 0.8.4",
|
||||
"rand_chacha 0.3.1",
|
||||
"rayon",
|
||||
"rodio",
|
||||
"ron",
|
||||
|
@ -131,6 +131,9 @@ void main() {
|
||||
|
||||
vec3 emitted_light, reflected_light;
|
||||
|
||||
// Prevent the sky affecting light when underground
|
||||
float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
|
||||
|
||||
// float point_shadow = shadow_at(f_pos, f_norm);
|
||||
// vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
|
||||
// vec3 emitted_light, reflected_light;
|
||||
@ -143,6 +146,11 @@ void main() {
|
||||
// vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0));
|
||||
float max_light = 0.0;
|
||||
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x*//*-cam_to_frag*/sun_view_dir/*view_dir*/, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, /*vec3(0.0)*/k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
||||
emitted_light *= not_underground;
|
||||
reflected_light *= not_underground;
|
||||
|
||||
// Global illumination when underground (silly)
|
||||
emitted_light += (1.0 - not_underground) * 0.05;
|
||||
// reflected_light *= f_light * point_shadow * shade_frac;
|
||||
// emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW);
|
||||
// max_light *= f_light * point_shadow * shade_frac;
|
||||
|
@ -179,6 +179,10 @@ void main() {
|
||||
vec3 reflect_color = get_sky_color(/*reflect_ray_dir*/beam_view_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true);
|
||||
reflect_color = get_cloud_color(reflect_color, reflect_ray_dir, cam_pos.xyz, time_of_day.x, 100000.0, 0.1);
|
||||
reflect_color *= f_light;
|
||||
|
||||
// Prevent the sky affecting light when underground
|
||||
float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
|
||||
reflect_color *= not_underground;
|
||||
// /*const */vec3 water_color = srgb_to_linear(vec3(0.2, 0.5, 1.0));
|
||||
// /*const */vec3 water_color = srgb_to_linear(vec3(0.8, 0.9, 1.0));
|
||||
// NOTE: Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths.
|
||||
@ -254,6 +258,11 @@ void main() {
|
||||
|
||||
float max_light = 0.0;
|
||||
max_light += get_sun_diffuse2(sun_info, moon_info, norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
||||
emitted_light *= not_underground;
|
||||
reflected_light *= not_underground;
|
||||
|
||||
// Global illumination when underground (silly)
|
||||
emitted_light += (1.0 - not_underground) * 0.05;
|
||||
// Apply cloud layer to sky
|
||||
// reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow * shade_frac;
|
||||
// emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow * max(shade_frac, MIN_SHADOW);
|
||||
|
@ -16,7 +16,7 @@ float cloud_broad(vec3 pos) {
|
||||
}
|
||||
|
||||
// Returns vec4(r, g, b, density)
|
||||
vec4 cloud_at(vec3 pos, float dist, out vec3 emission) {
|
||||
vec4 cloud_at(vec3 pos, float dist, out vec3 emission, out float not_underground) {
|
||||
// Natural attenuation of air (air naturally attenuates light that passes through it)
|
||||
// Simulate the atmosphere thinning as you get higher. Not physically accurate, but then
|
||||
// it can't be since Veloren's world is flat, not spherical.
|
||||
@ -134,7 +134,7 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) {
|
||||
//moon_access *= suppress_mist;
|
||||
|
||||
// Prevent clouds and mist appearing underground (but fade them out gently)
|
||||
float not_underground = clamp(1.0 - (alt - (pos.z - focus_off.z)) / 80.0 + dist * 0.001, 0, 1);
|
||||
not_underground = clamp(1.0 - (alt - (pos.z - focus_off.z)) / 80.0 + dist * 0.001, 0, 1);
|
||||
sun_access *= not_underground;
|
||||
moon_access *= not_underground;
|
||||
float vapor_density = (mist + cloud) * not_underground;
|
||||
@ -220,8 +220,9 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
|
||||
cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality);
|
||||
|
||||
vec3 emission;
|
||||
float not_underground; // Used to prevent sunlight leaking underground
|
||||
// `sample` is a reserved keyword
|
||||
vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission);
|
||||
vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission, not_underground);
|
||||
|
||||
vec2 density_integrals = max(sample_.zw, vec2(0));
|
||||
|
||||
@ -239,7 +240,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
|
||||
// Add the directed light light scattered into the camera by the clouds and the atmosphere (global illumination)
|
||||
sun_color * sun_scatter * get_sun_brightness() * (sun_access * (1.0 - cloud_darken) /*+ sky_color * global_scatter_factor*/) +
|
||||
moon_color * moon_scatter * get_moon_brightness() * (moon_access * (1.0 - cloud_darken) /*+ sky_color * global_scatter_factor*/) +
|
||||
sky_light * (1.0 - global_darken) +
|
||||
sky_light * (1.0 - global_darken) * not_underground +
|
||||
emission * density_integrals.y;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ vec3 glow_light(vec3 pos) {
|
||||
#if (SHADOW_MODE <= SHADOW_MODE_NONE)
|
||||
return GLOW_COLOR;
|
||||
#else
|
||||
return GLOW_COLOR * (1.0 + (noise_3d(vec3(pos.xy * 0.005, tick.x * 0.5)) - 0.5) * 1.0);
|
||||
return GLOW_COLOR * (1.0 + (noise_3d(vec3(pos.xy * 0.005, tick.x * 0.5)) - 0.5) * 0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -253,8 +253,12 @@ void main() {
|
||||
|
||||
// Computing light attenuation from water.
|
||||
vec3 emitted_light, reflected_light;
|
||||
|
||||
// Prevent the sky affecting light when underground
|
||||
float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
|
||||
|
||||
// To account for prior saturation
|
||||
/*float */f_light = faces_fluid ? 1.0 : f_light * sqrt(f_light);
|
||||
/*float */f_light = faces_fluid ? not_underground : f_light * sqrt(f_light);
|
||||
|
||||
emitted_light = vec3(1.0);
|
||||
reflected_light = vec3(1.0);
|
||||
@ -268,7 +272,7 @@ void main() {
|
||||
max_light *= f_light;
|
||||
|
||||
// TODO: Apply AO after this
|
||||
vec3 glow = glow_light(f_pos) * (pow(f_glow, 6) * 5 + pow(f_glow, 1.5) * 2);
|
||||
vec3 glow = glow_light(f_pos) * (pow(f_glow, 3) * 5 + pow(f_glow, 2.0) * 2);
|
||||
reflected_light += glow;
|
||||
|
||||
max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
||||
|
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-5.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-6.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-6.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-7.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_long-7.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_med-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/grass_short-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/lillypad-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/mycel-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/mycel-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/mycel-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/mycel-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/mycel-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/mycel-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cavern/mycel-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/cavern/mycel-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -3258,6 +3258,7 @@ CookingPot: Some((
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Ensnaring Vines
|
||||
EnsnaringVines: Some((
|
||||
variations: [
|
||||
(
|
||||
@ -3310,4 +3311,164 @@ Bones: Some((
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Short Cavern Grass Blue
|
||||
CavernGrassBlueShort: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_short-0",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_short-1",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_short-2",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_short-3",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_short-4",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Medium Cavern Grass Blue
|
||||
CavernGrassBlueMedium: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_med-0",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_med-1",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_med-2",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_med-3",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Long Cavern Grass Blue
|
||||
CavernGrassBlueLong: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-0",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-1",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-2",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-3",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-4",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-5",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-6",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.grass_long-7",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Cavern Lillypads Blue
|
||||
CavernLillypadBlue: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.lillypad-0",
|
||||
offset: (-5.5, -5.5, -1.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.lillypad-1",
|
||||
offset: (-5.5, -5.5, -1.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.lillypad-2",
|
||||
offset: (-5.5, -5.5, -1.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.lillypad-3",
|
||||
offset: (-5.5, -5.5, -1.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.lillypad-4",
|
||||
offset: (-5.5, -5.5, -1.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// Cavern Hanging Mycel Blue
|
||||
CavernMycelBlue: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.mycel-0",
|
||||
offset: (-0.5, -0.5, -21.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.mycel-1",
|
||||
offset: (-0.5, -0.5, -31.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.mycel-2",
|
||||
offset: (-0.5, -0.5, -14.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.cavern.mycel-3",
|
||||
offset: (-0.5, -0.5, -40.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.1,
|
||||
)),
|
||||
)
|
||||
|
12
assets/world/features.ron
Normal file
12
assets/world/features.ron
Normal file
@ -0,0 +1,12 @@
|
||||
#![enable(unwrap_newtypes)]
|
||||
#![enable(implicit_some)]
|
||||
|
||||
(
|
||||
caverns: false, // TODO: Disabled by default until cave overhaul
|
||||
caves: true,
|
||||
shrubs: true,
|
||||
trees: true,
|
||||
scatter: true,
|
||||
paths: true,
|
||||
spots: true,
|
||||
)
|
@ -67,7 +67,7 @@
|
||||
deep_stone_color: (125, 120, 130),
|
||||
layer: (
|
||||
bridge: (80, 80, 100),
|
||||
stalagtite: (90, 71, 112),
|
||||
stalactite: (90, 71, 112),
|
||||
cave_floor: (42, 39, 82),
|
||||
cave_roof: (38, 21, 79),
|
||||
dirt: (69, 48, 15),
|
||||
|
@ -644,6 +644,11 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
|
||||
g: 206,
|
||||
b: 64,
|
||||
},
|
||||
GlowingMushroom => Rgb {
|
||||
r: 50,
|
||||
g: 250,
|
||||
b: 250,
|
||||
},
|
||||
Misc => Rgb {
|
||||
r: 255,
|
||||
g: 0,
|
||||
|
@ -48,7 +48,8 @@ make_case_elim!(
|
||||
// 0x32 <= x < 0x40 is reserved for future earths/muds/gravels/sands/etc.
|
||||
Wood = 0x40,
|
||||
Leaves = 0x41,
|
||||
// 0x42 <= x < 0x50 is reserved for future tree parts
|
||||
GlowingMushroom = 0x42,
|
||||
// 0x43 <= x < 0x50 is reserved for future tree parts
|
||||
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
|
||||
// often want to experiment with new kinds of block without allocating them a
|
||||
// dedicated block kind.
|
||||
@ -177,7 +178,8 @@ impl Block {
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
match self.kind() {
|
||||
BlockKind::Lava => Some(24),
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => Some(12),
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => Some(10),
|
||||
BlockKind::GlowingMushroom => Some(20),
|
||||
_ => match self.get_sprite()? {
|
||||
SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24),
|
||||
SpriteKind::Ember => Some(20),
|
||||
@ -188,7 +190,11 @@ impl Block {
|
||||
| SpriteKind::Orb => Some(16),
|
||||
SpriteKind::Velorite
|
||||
| SpriteKind::VeloriteFrag
|
||||
| SpriteKind::Cauldron
|
||||
| SpriteKind::CavernGrassBlueShort
|
||||
| SpriteKind::CavernGrassBlueMedium
|
||||
| SpriteKind::CavernGrassBlueLong
|
||||
| SpriteKind::CavernLillypadBlue
|
||||
| SpriteKind::CavernMycelBlue
|
||||
| SpriteKind::CeilingMushroom => Some(6),
|
||||
SpriteKind::CaveMushroom
|
||||
| SpriteKind::CookingPot
|
||||
|
@ -71,6 +71,22 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
self.sub_chunks.iter().map(SubChunk::num_groups).sum()
|
||||
}
|
||||
|
||||
/// Iterate through the voxels in this chunk, attempting to avoid those that
|
||||
/// are unchanged (i.e: match the `below` and `above` voxels). This is
|
||||
/// generally useful for performance reasons.
|
||||
pub fn iter_changed(&self) -> impl Iterator<Item = (Vec3<i32>, &V)> + '_ {
|
||||
self.sub_chunks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, sc)| sc.num_groups() > 0)
|
||||
.map(move |(i, sc)| {
|
||||
let z_offset = self.z_offset + i as i32 * SubChunkSize::<S>::SIZE.z as i32;
|
||||
sc.vol_iter(Vec3::zero(), SubChunkSize::<S>::SIZE.map(|e| e as i32))
|
||||
.map(move |(pos, vox)| (pos + Vec3::unit_z() * z_offset, vox))
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
// Returns the index (in self.sub_chunks) of the SubChunk that contains
|
||||
// layer z; note that this index changes when more SubChunks are prepended
|
||||
#[inline]
|
||||
|
@ -177,6 +177,11 @@ make_case_elim!(
|
||||
WitchWindow = 0x96,
|
||||
SmokeDummy = 0x97,
|
||||
Bones = 0x98,
|
||||
CavernGrassBlueShort = 0x99,
|
||||
CavernGrassBlueMedium = 0x9A,
|
||||
CavernGrassBlueLong = 0x9B,
|
||||
CavernLillypadBlue = 0x9C,
|
||||
CavernMycelBlue = 0x9D,
|
||||
}
|
||||
);
|
||||
|
||||
@ -263,7 +268,7 @@ impl SpriteKind {
|
||||
| SpriteKind::Tin
|
||||
| SpriteKind::Silver
|
||||
| SpriteKind::Gold => 0.6,
|
||||
SpriteKind::EnsnaringVines => 0.1,
|
||||
SpriteKind::EnsnaringVines | SpriteKind::CavernLillypadBlue => 0.1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -851,7 +851,7 @@ impl Server {
|
||||
let ecs = self.state.ecs_mut();
|
||||
let slow_jobs = ecs.write_resource::<SlowJobPool>();
|
||||
|
||||
index.reload_colors_if_changed(|index| {
|
||||
index.reload_if_changed(|index| {
|
||||
let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
|
||||
let client = ecs.read_storage::<Client>();
|
||||
let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
|
||||
|
@ -106,6 +106,7 @@ native-dialog = { version = "0.5.2", optional = true }
|
||||
num = "0.4"
|
||||
ordered-float = { version = "2.0.1", default-features = false }
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
rayon = "1.5"
|
||||
rodio = {version = "0.14", default-features = false, features = ["vorbis"]}
|
||||
ron = {version = "0.6", default-features = false}
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::hud::CraftingTab;
|
||||
use common::{
|
||||
terrain::{BlockKind, SpriteKind, TerrainChunk},
|
||||
vol::{IntoVolIterator, RectRasterableVol},
|
||||
};
|
||||
use common::terrain::{BlockKind, SpriteKind, TerrainChunk};
|
||||
use common_base::span;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -60,106 +58,94 @@ impl BlocksOfInterest {
|
||||
let mut cricket3 = Vec::new();
|
||||
let mut frogs = Vec::new();
|
||||
|
||||
chunk
|
||||
.vol_iter(
|
||||
Vec3::new(0, 0, chunk.get_min_z()),
|
||||
Vec3::new(
|
||||
TerrainChunk::RECT_SIZE.x as i32,
|
||||
TerrainChunk::RECT_SIZE.y as i32,
|
||||
chunk.get_max_z(),
|
||||
),
|
||||
)
|
||||
.for_each(|(pos, block)| {
|
||||
match block.kind() {
|
||||
BlockKind::Leaves if thread_rng().gen_range(0..16) == 0 => leaves.push(pos),
|
||||
BlockKind::WeakRock if thread_rng().gen_range(0..6) == 0 => drip.push(pos),
|
||||
BlockKind::Grass => {
|
||||
if thread_rng().gen_range(0..16) == 0 {
|
||||
grass.push(pos);
|
||||
}
|
||||
match thread_rng().gen_range(0..8192) {
|
||||
1 => cricket1.push(pos),
|
||||
2 => cricket2.push(pos),
|
||||
3 => cricket3.push(pos),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
BlockKind::Water
|
||||
if chunk.meta().contains_river() && thread_rng().gen_range(0..16) == 0 =>
|
||||
{
|
||||
river.push(pos)
|
||||
},
|
||||
BlockKind::Lava if thread_rng().gen_range(0..5) == 0 => {
|
||||
fires.push(pos + Vec3::unit_z())
|
||||
},
|
||||
BlockKind::Snow if thread_rng().gen_range(0..16) == 0 => snow.push(pos),
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => {
|
||||
fires.push(pos);
|
||||
smokers.push(pos);
|
||||
},
|
||||
Some(SpriteKind::SmokeDummy) => {
|
||||
smokers.push(pos);
|
||||
},
|
||||
// Offset positions to account for block height.
|
||||
// TODO: Is this a good idea?
|
||||
Some(SpriteKind::StreetLamp) => fire_bowls.push(pos + Vec3::unit_z() * 2),
|
||||
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::StreetLampTall) => {
|
||||
fire_bowls.push(pos + Vec3::unit_z() * 3);
|
||||
},
|
||||
Some(SpriteKind::WallSconce) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||
Some(SpriteKind::CrystalHigh) => fireflies.push(pos),
|
||||
Some(SpriteKind::Reed) => {
|
||||
reeds.push(pos);
|
||||
fireflies.push(pos);
|
||||
if thread_rng().gen_range(0..12) == 0 {
|
||||
frogs.push(pos);
|
||||
}
|
||||
},
|
||||
Some(SpriteKind::CaveMushroom) => fireflies.push(pos),
|
||||
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||
Some(SpriteKind::CraftingBench) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Forge) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle)))
|
||||
},
|
||||
Some(SpriteKind::TanningRack) => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
Some(SpriteKind::SpinningWheel) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Loom) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Cauldron) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
|
||||
},
|
||||
Some(SpriteKind::Anvil) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
|
||||
},
|
||||
Some(SpriteKind::CookingPot) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
|
||||
},
|
||||
let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
|
||||
|
||||
chunk.iter_changed().for_each(|(pos, block)| {
|
||||
match block.kind() {
|
||||
BlockKind::Leaves if rng.gen_range(0..16) == 0 => leaves.push(pos),
|
||||
BlockKind::WeakRock if rng.gen_range(0..6) == 0 => drip.push(pos),
|
||||
BlockKind::Grass => {
|
||||
if rng.gen_range(0..16) == 0 {
|
||||
grass.push(pos);
|
||||
}
|
||||
match rng.gen_range(0..8192) {
|
||||
1 => cricket1.push(pos),
|
||||
2 => cricket2.push(pos),
|
||||
3 => cricket3.push(pos),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
BlockKind::Water if chunk.meta().contains_river() && rng.gen_range(0..16) == 0 => {
|
||||
river.push(pos)
|
||||
},
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
BlockKind::Lava if rng.gen_range(0..5) == 0 => fires.push(pos + Vec3::unit_z()),
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => {
|
||||
fires.push(pos);
|
||||
smokers.push(pos);
|
||||
},
|
||||
}
|
||||
if block.is_collectible() {
|
||||
interactables.push((pos, Interaction::Collect));
|
||||
}
|
||||
if let Some(glow) = block.get_glow() {
|
||||
lights.push((pos, glow));
|
||||
}
|
||||
});
|
||||
// Offset positions to account for block height.
|
||||
// TODO: Is this a good idea?
|
||||
Some(SpriteKind::StreetLamp) => fire_bowls.push(pos + Vec3::unit_z() * 2),
|
||||
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::StreetLampTall) => fire_bowls.push(pos + Vec3::unit_z() * 4),
|
||||
Some(SpriteKind::WallSconce) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||
Some(SpriteKind::CrystalHigh) => fireflies.push(pos),
|
||||
Some(SpriteKind::Reed) => {
|
||||
reeds.push(pos);
|
||||
fireflies.push(pos);
|
||||
if rng.gen_range(0..12) == 0 {
|
||||
frogs.push(pos);
|
||||
}
|
||||
},
|
||||
Some(SpriteKind::CaveMushroom) => fireflies.push(pos),
|
||||
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||
Some(SpriteKind::CraftingBench) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::SmokeDummy) => {
|
||||
smokers.push(pos);
|
||||
},
|
||||
Some(SpriteKind::Forge) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle)))
|
||||
},
|
||||
Some(SpriteKind::TanningRack) => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
Some(SpriteKind::SpinningWheel) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Loom) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Cauldron) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
|
||||
},
|
||||
Some(SpriteKind::Anvil) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
|
||||
},
|
||||
Some(SpriteKind::CookingPot) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
}
|
||||
if block.is_collectible() {
|
||||
interactables.push((pos, Interaction::Collect));
|
||||
}
|
||||
if let Some(glow) = block.get_glow() {
|
||||
lights.push((pos, glow));
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
leaves,
|
||||
|
@ -17,7 +17,12 @@ fn main() {
|
||||
let mut focus = Vec2::<f32>::zero();
|
||||
let mut zoom = 1.0;
|
||||
let colors = &**index.colors().read();
|
||||
let index = IndexRef { colors, index };
|
||||
let features = &**index.features().read();
|
||||
let index = IndexRef {
|
||||
colors,
|
||||
features,
|
||||
index,
|
||||
};
|
||||
|
||||
while win.is_open() {
|
||||
let mut buf = vec![0; W * H];
|
||||
|
@ -414,7 +414,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
downhill_chunk.river.river_kind,
|
||||
Some(RiverKind::Lake { .. })
|
||||
))
|
||||
&& (river_chunk.water_alt > downhill_chunk.water_alt + 8.0)
|
||||
&& (river_chunk.water_alt > downhill_chunk.water_alt + 0.0)
|
||||
}
|
||||
|
||||
/// Determine the altitude of a river based on the altitude of the
|
||||
@ -452,7 +452,14 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
// they do not result in artifacts, even in edge cases. The exact configuration
|
||||
// of this code is the product of hundreds of hours of testing and
|
||||
// refinement and I ask that you do not take that effort lightly.
|
||||
let (river_water_level, in_river, lake_water_level, lake_dist, water_dist, unbounded_water_level) = neighbor_river_data.iter().copied().fold(
|
||||
let (
|
||||
river_water_level,
|
||||
in_river,
|
||||
lake_water_level,
|
||||
lake_dist,
|
||||
water_dist,
|
||||
unbounded_water_level,
|
||||
) = neighbor_river_data.iter().copied().fold(
|
||||
(
|
||||
WeightedSum::default().with_max(base_sea_level),
|
||||
false,
|
||||
@ -461,7 +468,18 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
None,
|
||||
WeightedSum::default().with_max(base_sea_level),
|
||||
),
|
||||
|(mut river_water_level, mut in_river, mut lake_water_level, mut lake_dist, water_dist, mut unbounded_water_level), (river_chunk_idx, river_chunk, river, dist_info)| match (river.river_kind, dist_info) {
|
||||
|(
|
||||
mut river_water_level,
|
||||
mut in_river,
|
||||
lake_water_level,
|
||||
mut lake_dist,
|
||||
water_dist,
|
||||
mut unbounded_water_level,
|
||||
),
|
||||
(river_chunk_idx, river_chunk, river, dist_info)| match (
|
||||
river.river_kind,
|
||||
dist_info,
|
||||
) {
|
||||
(
|
||||
Some(kind),
|
||||
Some((_, _, river_width, (river_t, (river_pos, _), downhill_chunk))),
|
||||
@ -480,7 +498,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
|
||||
match kind {
|
||||
RiverKind::River { .. } => {
|
||||
// Alt of river water *is* the alt of land (ignoring gorge, which gets applied later)
|
||||
// Alt of river water *is* the alt of land (ignoring gorge, which gets
|
||||
// applied later)
|
||||
let river_water_alt = river_water_alt(
|
||||
river_chunk.alt.max(river_chunk.water_alt),
|
||||
downhill_chunk.alt.max(downhill_chunk.water_alt),
|
||||
@ -488,14 +507,15 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
is_waterfall(river_chunk_idx, river_chunk, downhill_chunk),
|
||||
);
|
||||
|
||||
river_water_level = river_water_level
|
||||
.with(river_water_alt, near_center);
|
||||
river_water_level =
|
||||
river_water_level.with(river_water_alt, near_center);
|
||||
|
||||
if river_edge_dist <= 0.0 {
|
||||
in_river = true;
|
||||
}
|
||||
},
|
||||
// Slightly wider threshold is chosen in case the lake bounds are a bit wrong
|
||||
// Slightly wider threshold is chosen in case the lake bounds are a bit
|
||||
// wrong
|
||||
RiverKind::Lake { .. } | RiverKind::Ocean => {
|
||||
let lake_water_alt = if matches!(kind, RiverKind::Ocean) {
|
||||
base_sea_level
|
||||
@ -509,32 +529,63 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
};
|
||||
|
||||
if river_edge_dist > 0.0 && river_width > lake_width * 0.99 {
|
||||
let unbounded_water_alt = lake_water_alt - ((river_edge_dist - 8.0).max(0.0) / 5.0).powf(2.0);
|
||||
let unbounded_water_alt = lake_water_alt
|
||||
- ((river_edge_dist - 8.0).max(0.0) / 5.0).powf(2.0);
|
||||
unbounded_water_level = unbounded_water_level
|
||||
.with(unbounded_water_alt, 1.0 / (1.0 + river_edge_dist * 5.0))
|
||||
.with_max(unbounded_water_alt);
|
||||
.with(unbounded_water_alt, 1.0 / (1.0 + river_edge_dist * 5.0));
|
||||
//.with_max(unbounded_water_alt);
|
||||
}
|
||||
|
||||
river_water_level = river_water_level
|
||||
.with(lake_water_alt, near_center);
|
||||
river_water_level = river_water_level.with(lake_water_alt, near_center);
|
||||
|
||||
lake_dist = lake_dist.min(river_edge_dist);
|
||||
|
||||
// Lake border prevents a lake failing to propagate its altitude to nearby rivers
|
||||
if river_edge_dist <= 0.0 {
|
||||
lake_water_level = lake_water_level
|
||||
// Make sure the closest lake is prioritised
|
||||
.with(lake_water_alt, near_center + 0.1 / (1.0 + river_edge_dist));
|
||||
// Lake border prevents a lake failing to propagate its altitude to
|
||||
// nearby rivers
|
||||
let off = 0.0;
|
||||
let len = 3.0;
|
||||
if river_edge_dist <= off {
|
||||
// lake_water_level = lake_water_level
|
||||
// // Make sure the closest lake is prioritised
|
||||
// .with(lake_water_alt, near_center + 0.1 / (1.0 +
|
||||
// river_edge_dist)); // .with_min(lake_water_alt);
|
||||
//
|
||||
river_water_level = river_water_level.with_min(
|
||||
lake_water_alt
|
||||
+ ((((river_dist - river_width * 0.5) as f32 + len - off)
|
||||
.max(0.0))
|
||||
/ len)
|
||||
.powf(1.5)
|
||||
* 32.0,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let river_edge_dist_unclamped = (river_dist - river_width * 0.5) as f32;
|
||||
let water_dist = Some(water_dist.unwrap_or(river_edge_dist_unclamped).min(river_edge_dist_unclamped));
|
||||
let water_dist = Some(
|
||||
water_dist
|
||||
.unwrap_or(river_edge_dist_unclamped)
|
||||
.min(river_edge_dist_unclamped),
|
||||
);
|
||||
|
||||
(river_water_level, in_river, lake_water_level, lake_dist, water_dist, unbounded_water_level)
|
||||
(
|
||||
river_water_level,
|
||||
in_river,
|
||||
lake_water_level,
|
||||
lake_dist,
|
||||
water_dist,
|
||||
unbounded_water_level,
|
||||
)
|
||||
},
|
||||
(_, _) => (river_water_level, in_river, lake_water_level, lake_dist, water_dist, unbounded_water_level),
|
||||
(_, _) => (
|
||||
river_water_level,
|
||||
in_river,
|
||||
lake_water_level,
|
||||
lake_dist,
|
||||
water_dist,
|
||||
unbounded_water_level,
|
||||
),
|
||||
},
|
||||
);
|
||||
let unbounded_water_level = unbounded_water_level.eval_or(base_sea_level);
|
||||
@ -679,7 +730,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
)
|
||||
.with_min(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt))
|
||||
} else {
|
||||
const GORGE: f32 = 0.5;
|
||||
const GORGE: f32 = 0.25;
|
||||
const BANK_SCALE: f32 = 24.0;
|
||||
// Weighting of this riverbank on nearby terrain (higher when closer to
|
||||
// the river). This 'pulls' the riverbank
|
||||
|
@ -1,3 +1,6 @@
|
||||
use common::assets;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct Config {
|
||||
pub sea_level: f32,
|
||||
pub mountain_scale: f32,
|
||||
@ -69,3 +72,20 @@ pub const CONFIG: Config = Config {
|
||||
river_min_height: 0.25,
|
||||
river_width_to_depth: 8.0,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Features {
|
||||
pub caverns: bool,
|
||||
pub caves: bool,
|
||||
pub shrubs: bool,
|
||||
pub trees: bool,
|
||||
pub scatter: bool,
|
||||
pub paths: bool,
|
||||
pub spots: bool,
|
||||
}
|
||||
|
||||
impl assets::Asset for Features {
|
||||
type Loader = assets::RonLoader;
|
||||
|
||||
const EXTENSION: &'static str = "ron";
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
layer::wildlife::{self, DensityFn, SpawnEntry},
|
||||
site::{economy::TradeInformation, Site},
|
||||
Colors,
|
||||
Colors, Features,
|
||||
};
|
||||
use common::{
|
||||
assets::{AssetExt, AssetHandle},
|
||||
@ -13,6 +13,7 @@ use noise::{Seedable, SuperSimplex};
|
||||
use std::sync::Arc;
|
||||
|
||||
const WORLD_COLORS_MANIFEST: &str = "world.style.colors";
|
||||
const WORLD_FEATURES_MANIFEST: &str = "world.features";
|
||||
|
||||
pub struct Index {
|
||||
pub seed: u32,
|
||||
@ -22,6 +23,7 @@ pub struct Index {
|
||||
pub trade: TradeInformation,
|
||||
pub wildlife_spawns: Vec<(AssetHandle<SpawnEntry>, DensityFn)>,
|
||||
colors: AssetHandle<Arc<Colors>>,
|
||||
features: AssetHandle<Arc<Features>>,
|
||||
}
|
||||
|
||||
/// An owned reference to indexed data.
|
||||
@ -32,6 +34,7 @@ pub struct Index {
|
||||
#[derive(Clone)]
|
||||
pub struct IndexOwned {
|
||||
colors: Arc<Colors>,
|
||||
features: Arc<Features>,
|
||||
index: Arc<Index>,
|
||||
}
|
||||
|
||||
@ -47,6 +50,7 @@ impl Deref for IndexOwned {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IndexRef<'a> {
|
||||
pub colors: &'a Colors,
|
||||
pub features: &'a Features,
|
||||
pub index: &'a Index,
|
||||
}
|
||||
|
||||
@ -60,6 +64,7 @@ impl Index {
|
||||
/// NOTE: Panics if the color manifest cannot be loaded.
|
||||
pub fn new(seed: u32) -> Self {
|
||||
let colors = Arc::<Colors>::load_expect(WORLD_COLORS_MANIFEST);
|
||||
let features = Arc::<Features>::load_expect(WORLD_FEATURES_MANIFEST);
|
||||
let wildlife_spawns = wildlife::spawn_manifest()
|
||||
.into_iter()
|
||||
.map(|(e, f)| (SpawnEntry::load_expect(e), f))
|
||||
@ -73,11 +78,14 @@ impl Index {
|
||||
trade: Default::default(),
|
||||
wildlife_spawns,
|
||||
colors,
|
||||
features,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn colors(&self) -> AssetHandle<Arc<Colors>> { self.colors }
|
||||
|
||||
pub fn features(&self) -> AssetHandle<Arc<Features>> { self.features }
|
||||
|
||||
pub fn get_site_prices(&self, site_id: SiteId) -> Option<SitePrices> {
|
||||
self.sites
|
||||
.recreate_id(site_id)
|
||||
@ -89,10 +97,12 @@ impl Index {
|
||||
impl IndexOwned {
|
||||
pub fn new(index: Index) -> Self {
|
||||
let colors = index.colors.cloned();
|
||||
let features = index.features.cloned();
|
||||
|
||||
Self {
|
||||
index: Arc::new(index),
|
||||
colors,
|
||||
features,
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,13 +113,12 @@ impl IndexOwned {
|
||||
/// solution.
|
||||
///
|
||||
/// Ideally, this should be called about once per tick.
|
||||
pub fn reload_colors_if_changed<R>(
|
||||
&mut self,
|
||||
reload: impl FnOnce(&mut Self) -> R,
|
||||
) -> Option<R> {
|
||||
self.index.colors.reloaded_global().then(move || {
|
||||
// Reload the color from the asset handle, which is updated automatically
|
||||
pub fn reload_if_changed<R>(&mut self, reload: impl FnOnce(&mut Self) -> R) -> Option<R> {
|
||||
let reloaded = self.index.colors.reloaded_global() || self.index.features.reloaded_global();
|
||||
reloaded.then(move || {
|
||||
// Reload the fields from the asset handle, which is updated automatically
|
||||
self.colors = self.index.colors.cloned();
|
||||
self.features = self.index.features.cloned();
|
||||
reload(self)
|
||||
})
|
||||
}
|
||||
@ -117,6 +126,7 @@ impl IndexOwned {
|
||||
pub fn as_index_ref(&self) -> IndexRef {
|
||||
IndexRef {
|
||||
colors: &self.colors,
|
||||
features: &self.features,
|
||||
index: &self.index,
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ pub use self::{
|
||||
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
util::{FastNoise, RandomField, Sampler},
|
||||
config::CONFIG,
|
||||
util::{FastNoise, RandomField, RandomPerm, Sampler},
|
||||
Canvas, IndexRef,
|
||||
};
|
||||
use common::{
|
||||
@ -20,19 +21,20 @@ use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use noise::NoiseFn;
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
f32,
|
||||
ops::{Mul, Range, Sub},
|
||||
ops::{Add, Mul, Range, Sub},
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Colors {
|
||||
pub bridge: (u8, u8, u8),
|
||||
pub stalagtite: (u8, u8, u8),
|
||||
pub stalactite: (u8, u8, u8),
|
||||
pub cave_floor: (u8, u8, u8),
|
||||
pub cave_roof: (u8, u8, u8),
|
||||
pub dirt: (u8, u8, u8),
|
||||
@ -170,11 +172,11 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
let floor_dist = pit_condition as i32 * pit_depth as i32;
|
||||
let vein_condition =
|
||||
cave_depth % 12.0 > 11.5 && cave_x > 0.1 && cave_x < 0.6 && cave_depth > 200.0;
|
||||
let stalagtite_condition = cave_depth > 150.0;
|
||||
let stalactite_condition = cave_depth > 150.0;
|
||||
let vein_depth = 3;
|
||||
let vein_floor = cave_base - vein_depth;
|
||||
// Stalagtites
|
||||
let stalagtites = info
|
||||
let stalactites = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
@ -188,18 +190,18 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
)
|
||||
.mul(45.0) as i32;
|
||||
|
||||
// Generate stalagtites if there's something for them to hold on to
|
||||
// Generate stalactites if there's something for them to hold on to
|
||||
if canvas
|
||||
.get(Vec3::new(wpos2d.x, wpos2d.y, cave_roof))
|
||||
.is_filled()
|
||||
&& stalagtite_condition
|
||||
&& stalactite_condition
|
||||
{
|
||||
for z in cave_roof - stalagtites..cave_roof {
|
||||
for z in cave_roof - stalactites..cave_roof {
|
||||
canvas.set(
|
||||
Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
Block::new(
|
||||
BlockKind::WeakRock,
|
||||
noisy_color(info.index().colors.layer.stalagtite.into(), 8),
|
||||
noisy_color(info.index().colors.layer.stalactite.into(), 8),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -570,3 +572,396 @@ pub fn apply_coral_to(canvas: &mut Canvas) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
|
||||
let info = canvas.info();
|
||||
|
||||
let canvern_nz_at = |wpos2d: Vec2<i32>| {
|
||||
// Horizontal average scale of caverns
|
||||
let scale = 2048.0;
|
||||
// How common should they be? (0.0 - 1.0)
|
||||
let common = 0.15;
|
||||
|
||||
let cavern_nz = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / scale).into_array()) as f32;
|
||||
((cavern_nz * 0.5 + 0.5 - (1.0 - common)).max(0.0) / common).powf(common * 2.0)
|
||||
};
|
||||
|
||||
// Get cavern attributes at a position
|
||||
let cavern_at = |wpos2d| {
|
||||
let alt = info.land().get_alt_approx(wpos2d);
|
||||
|
||||
// Range of heights for the caverns
|
||||
let height_range = 16.0..250.0;
|
||||
// Minimum distance below the surface
|
||||
let surface_clearance = 64.0;
|
||||
|
||||
let cavern_avg_height = Lerp::lerp(
|
||||
height_range.start,
|
||||
height_range.end,
|
||||
info.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / 300.0).into_array()) as f32
|
||||
* 0.5
|
||||
+ 0.5,
|
||||
);
|
||||
|
||||
let cavern_avg_alt =
|
||||
CONFIG.sea_level.min(alt * 0.25) - height_range.end - surface_clearance;
|
||||
|
||||
let cavern = canvern_nz_at(wpos2d);
|
||||
let cavern_height = cavern * cavern_avg_height;
|
||||
|
||||
// Stalagtites
|
||||
let stalactite = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get(wpos2d.map(|e| e as f64 * 0.015).into_array())
|
||||
.sub(0.5)
|
||||
.max(0.0)
|
||||
.mul((cavern_height as f64 - 5.0).mul(0.15).clamped(0.0, 1.0))
|
||||
.mul(32.0 + cavern_avg_height as f64);
|
||||
|
||||
let hill = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / 96.0).into_array()) as f32
|
||||
* cavern
|
||||
* 24.0;
|
||||
let rugged = 0.4; // How bumpy should the floor be relative to the ceiling?
|
||||
let cavern_bottom = (cavern_avg_alt - cavern_height * rugged + hill) as i32;
|
||||
let cavern_avg_bottom =
|
||||
(cavern_avg_alt - ((height_range.start + height_range.end) * 0.5) * rugged) as i32;
|
||||
let cavern_top = (cavern_avg_alt + cavern_height) as i32;
|
||||
let cavern_avg_top = (cavern_avg_alt + cavern_avg_height) as i32;
|
||||
|
||||
// Stalagmites rise up to meet stalactites
|
||||
let stalagmite = stalactite;
|
||||
|
||||
let floor = stalagmite as i32;
|
||||
|
||||
(
|
||||
cavern_bottom,
|
||||
cavern_top,
|
||||
cavern_avg_bottom,
|
||||
cavern_avg_top,
|
||||
floor,
|
||||
stalactite,
|
||||
cavern_avg_bottom as i32 + 16, // Water level
|
||||
)
|
||||
};
|
||||
|
||||
let mut mushroom_cache = HashMap::new();
|
||||
|
||||
struct Mushroom {
|
||||
pos: Vec3<i32>,
|
||||
stalk: f32,
|
||||
head_color: Rgb<u8>,
|
||||
}
|
||||
|
||||
// Get mushroom block, if any, at a position
|
||||
let mut get_mushroom = |wpos: Vec3<i32>, dynamic_rng: &mut R| {
|
||||
for (wpos2d, seed) in info.chunks().gen_ctx.structure_gen.get(wpos.xy()) {
|
||||
let mushroom = if let Some(mushroom) =
|
||||
mushroom_cache.entry(wpos2d).or_insert_with(|| {
|
||||
let mut rng = RandomPerm::new(seed);
|
||||
let (cavern_bottom, cavern_top, _, _, floor, _, water_level) =
|
||||
cavern_at(wpos2d);
|
||||
let pos = wpos2d.with_z(cavern_bottom + floor);
|
||||
if rng.gen_bool(0.15)
|
||||
&& cavern_top - cavern_bottom > 32
|
||||
&& pos.z as i32 > water_level - 2
|
||||
{
|
||||
Some(Mushroom {
|
||||
pos,
|
||||
stalk: 12.0 + rng.gen::<f32>().powf(2.0) * 35.0,
|
||||
head_color: Rgb::new(
|
||||
50,
|
||||
rng.gen_range(70..110),
|
||||
rng.gen_range(100..200),
|
||||
),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
mushroom
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let warp_freq = 1.0 / 32.0;
|
||||
let warp_amp = Vec3::new(12.0, 12.0, 12.0);
|
||||
let wposf_warped = wposf.map(|e| e as f32)
|
||||
+ Vec3::new(
|
||||
FastNoise::new(seed).get(wposf * warp_freq) as f32,
|
||||
FastNoise::new(seed + 1).get(wposf * warp_freq) as f32,
|
||||
FastNoise::new(seed + 2).get(wposf * warp_freq) as f32,
|
||||
) * warp_amp
|
||||
* (wposf.z as f32 - mushroom.pos.z as f32)
|
||||
.mul(0.1)
|
||||
.clamped(0.0, 1.0);
|
||||
|
||||
let rpos = wposf_warped - mushroom.pos.map(|e| e as f32).map(|e| e as f32);
|
||||
|
||||
let stalk_radius = 2.5f32;
|
||||
let head_radius = 18.0f32;
|
||||
let head_height = 16.0;
|
||||
|
||||
let dist_sq = rpos.xy().magnitude_squared();
|
||||
if dist_sq < head_radius.powi(2) {
|
||||
let dist = dist_sq.sqrt();
|
||||
let head_dist = ((rpos - Vec3::unit_z() * mushroom.stalk)
|
||||
/ Vec2::broadcast(head_radius).with_z(head_height))
|
||||
.magnitude();
|
||||
|
||||
let stalk = mushroom.stalk + Lerp::lerp(head_height * 0.5, 0.0, dist / head_radius);
|
||||
|
||||
// Head
|
||||
if rpos.z > stalk
|
||||
&& rpos.z <= mushroom.stalk + head_height
|
||||
&& dist
|
||||
< head_radius * (1.0 - (rpos.z - mushroom.stalk) / head_height).powf(0.125)
|
||||
{
|
||||
if head_dist < 0.85 {
|
||||
let radial = (rpos.x.atan2(rpos.y) * 10.0).sin() * 0.5 + 0.5;
|
||||
return Some(Block::new(
|
||||
BlockKind::GlowingMushroom,
|
||||
Rgb::new(30, 50 + (radial * 100.0) as u8, 100 - (radial * 50.0) as u8),
|
||||
));
|
||||
} else if head_dist < 1.0 {
|
||||
return Some(Block::new(BlockKind::Wood, mushroom.head_color));
|
||||
}
|
||||
}
|
||||
|
||||
if rpos.z <= mushroom.stalk + head_height - 1.0
|
||||
&& dist_sq
|
||||
< (stalk_radius * Lerp::lerp(1.5, 0.75, rpos.z / mushroom.stalk)).powi(2)
|
||||
{
|
||||
// Stalk
|
||||
return Some(Block::new(BlockKind::Wood, Rgb::new(25, 60, 90)));
|
||||
} else if ((mushroom.stalk - 0.1)..(mushroom.stalk + 0.9)).contains(&rpos.z) // Hanging orbs
|
||||
&& dist > head_radius * 0.85
|
||||
&& dynamic_rng.gen_bool(0.1)
|
||||
{
|
||||
use SpriteKind::*;
|
||||
let sprites = if dynamic_rng.gen_bool(0.1) {
|
||||
&[Beehive, Lantern] as &[_]
|
||||
} else {
|
||||
&[Orb, CavernMycelBlue, CavernMycelBlue] as &[_]
|
||||
};
|
||||
return Some(Block::air(*sprites.choose(dynamic_rng).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
canvas.foreach_col(|canvas, wpos2d, _col| {
|
||||
if canvern_nz_at(wpos2d) <= 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let (
|
||||
cavern_bottom,
|
||||
cavern_top,
|
||||
cavern_avg_bottom,
|
||||
cavern_avg_top,
|
||||
floor,
|
||||
stalactite,
|
||||
water_level,
|
||||
) = cavern_at(wpos2d);
|
||||
|
||||
let mini_stalactite = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get(wpos2d.map(|e| e as f64 * 0.08).into_array())
|
||||
.sub(0.5)
|
||||
.max(0.0)
|
||||
.mul(
|
||||
((cavern_top - cavern_bottom) as f64 - 5.0)
|
||||
.mul(0.15)
|
||||
.clamped(0.0, 1.0),
|
||||
)
|
||||
.mul(24.0 + (cavern_avg_top - cavern_avg_bottom) as f64 * 0.2);
|
||||
let stalactite_height = (stalactite + mini_stalactite) as i32;
|
||||
|
||||
let moss_common = 1.5;
|
||||
let moss = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get(wpos2d.map(|e| e as f64 * 0.035).into_array())
|
||||
.sub(1.0 - moss_common)
|
||||
.max(0.0)
|
||||
.mul(1.0 / moss_common)
|
||||
.powf(8.0 * moss_common)
|
||||
.mul(
|
||||
((cavern_top - cavern_bottom) as f64)
|
||||
.mul(0.15)
|
||||
.clamped(0.0, 1.0),
|
||||
)
|
||||
.mul(16.0 + (cavern_avg_top - cavern_avg_bottom) as f64 * 0.35);
|
||||
|
||||
let plant_factor = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get(wpos2d.map(|e| e as f64 * 0.015).into_array())
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.powf(2.0);
|
||||
|
||||
let is_vine = |wpos: Vec3<f32>, dynamic_rng: &mut R| {
|
||||
let wpos = wpos + wpos.xy().yx().with_z(0.0) * 0.2; // A little twist
|
||||
let dims = Vec2::new(7.0, 256.0); // Long and thin
|
||||
let vine_posf = (wpos + Vec2::new(0.0, (wpos.x / dims.x).floor() * 733.0)) / dims; // ~Random offset
|
||||
let vine_pos = vine_posf.map(|e| e.floor() as i32);
|
||||
let mut rng = RandomPerm::new(((vine_pos.x << 16) | vine_pos.y) as u32); // Rng for vine attributes
|
||||
if rng.gen_bool(0.2) {
|
||||
let vine_height = (cavern_avg_top - cavern_avg_bottom).max(64) as f32;
|
||||
let vine_base = cavern_avg_bottom as f32 + rng.gen_range(48.0..vine_height);
|
||||
let vine_y = (vine_posf.y.fract() - 0.5).abs() * 2.0 * dims.y;
|
||||
let vine_reach = (vine_y * 0.05).powf(2.0).min(1024.0);
|
||||
let vine_z = vine_base + vine_reach;
|
||||
if Vec2::new(vine_posf.x.fract() * 2.0 - 1.0, (wpos.z - vine_z) / 5.0)
|
||||
.magnitude_squared()
|
||||
< 1.0f32
|
||||
{
|
||||
let kind = if dynamic_rng.gen_bool(0.025) {
|
||||
BlockKind::GlowingRock
|
||||
} else {
|
||||
BlockKind::Leaves
|
||||
};
|
||||
Some(Block::new(
|
||||
kind,
|
||||
Rgb::new(
|
||||
85,
|
||||
(vine_y + vine_reach).mul(0.05).sin().mul(35.0).add(85.0) as u8,
|
||||
20,
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let cavern_top = cavern_top as i32;
|
||||
let mut last_kind = BlockKind::Rock;
|
||||
for z in cavern_bottom - 1..cavern_top {
|
||||
use SpriteKind::*;
|
||||
|
||||
let wpos = wpos2d.with_z(z);
|
||||
let wposf = wpos.map(|e| e as f32);
|
||||
|
||||
let block = if z < cavern_bottom {
|
||||
if z > water_level + dynamic_rng.gen_range(4..16) {
|
||||
Block::new(BlockKind::Grass, Rgb::new(10, 75, 90))
|
||||
} else {
|
||||
Block::new(BlockKind::Rock, Rgb::new(50, 40, 10))
|
||||
}
|
||||
} else if z < cavern_bottom + floor {
|
||||
Block::new(BlockKind::WeakRock, Rgb::new(110, 120, 150))
|
||||
} else if z > cavern_top - stalactite_height {
|
||||
if dynamic_rng.gen_bool(0.0035) {
|
||||
// Glowing rock in stalactites
|
||||
Block::new(BlockKind::GlowingRock, Rgb::new(30, 150, 120))
|
||||
} else {
|
||||
Block::new(BlockKind::WeakRock, Rgb::new(110, 120, 150))
|
||||
}
|
||||
} else if let Some(mushroom_block) = get_mushroom(wpos, dynamic_rng) {
|
||||
mushroom_block
|
||||
} else if z > cavern_top - moss as i32 {
|
||||
let kind = if dynamic_rng
|
||||
.gen_bool(0.05 / (1.0 + ((cavern_top - z).max(0) as f64).mul(0.1)))
|
||||
{
|
||||
BlockKind::GlowingMushroom
|
||||
} else {
|
||||
BlockKind::Leaves
|
||||
};
|
||||
Block::new(kind, Rgb::new(50, 120, 160))
|
||||
} else if z < water_level {
|
||||
Block::water(SpriteKind::Empty).with_sprite(
|
||||
if z == cavern_bottom + floor && dynamic_rng.gen_bool(0.01) {
|
||||
*[
|
||||
SpriteKind::Seagrass,
|
||||
SpriteKind::SeaGrapes,
|
||||
SpriteKind::SeaweedTemperate,
|
||||
SpriteKind::StonyCoral,
|
||||
]
|
||||
.choose(dynamic_rng)
|
||||
.unwrap()
|
||||
} else {
|
||||
SpriteKind::Empty
|
||||
},
|
||||
)
|
||||
} else if z == water_level
|
||||
&& dynamic_rng.gen_bool(Lerp::lerp(0.0, 0.05, plant_factor))
|
||||
&& last_kind == BlockKind::Water
|
||||
{
|
||||
Block::air(SpriteKind::CavernLillypadBlue)
|
||||
} else if z == cavern_bottom + floor
|
||||
&& dynamic_rng.gen_bool(Lerp::lerp(0.0, 0.5, plant_factor))
|
||||
&& last_kind == BlockKind::Grass
|
||||
{
|
||||
Block::air(
|
||||
*if dynamic_rng.gen_bool(0.9) {
|
||||
// High density
|
||||
&[
|
||||
CavernGrassBlueShort,
|
||||
CavernGrassBlueMedium,
|
||||
CavernGrassBlueLong,
|
||||
] as &[_]
|
||||
} else if dynamic_rng.gen_bool(0.5) {
|
||||
// Medium density
|
||||
&[CaveMushroom] as &[_]
|
||||
} else {
|
||||
// Low density
|
||||
&[LeafyPlant, Fern, Pyrebloom, Moonbell, Welwitch, GrassBlue] as &[_]
|
||||
}
|
||||
.choose(dynamic_rng)
|
||||
.unwrap(),
|
||||
)
|
||||
} else if z == cavern_top - 1 && dynamic_rng.gen_bool(0.001) {
|
||||
Block::air(
|
||||
*[CrystalHigh, CeilingMushroom, Orb, CavernMycelBlue]
|
||||
.choose(dynamic_rng)
|
||||
.unwrap(),
|
||||
)
|
||||
} else if let Some(vine) = is_vine(wposf, dynamic_rng)
|
||||
.or_else(|| is_vine(wposf.xy().yx().with_z(wposf.z), dynamic_rng))
|
||||
{
|
||||
vine
|
||||
} else {
|
||||
Block::empty()
|
||||
};
|
||||
|
||||
last_kind = block.kind();
|
||||
|
||||
let block = if block.is_filled() {
|
||||
Block::new(
|
||||
block.kind(),
|
||||
block.get_color().unwrap_or_default().map(|e| {
|
||||
(e as f32 * dynamic_rng.gen_range(0.95..1.05)).clamped(0.0, 255.0) as u8
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
block
|
||||
};
|
||||
|
||||
let _ = canvas.set(wpos, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ pub mod util;
|
||||
// Reexports
|
||||
pub use crate::{
|
||||
canvas::{Canvas, CanvasInfo},
|
||||
config::CONFIG,
|
||||
config::{Features, CONFIG},
|
||||
land::Land,
|
||||
};
|
||||
pub use block::BlockGen;
|
||||
@ -52,7 +52,8 @@ use common::{
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use common_net::msg::{world_msg, WorldMapMsg};
|
||||
use rand::Rng;
|
||||
use rand::{prelude::*, Rng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
@ -329,7 +330,7 @@ impl World {
|
||||
};
|
||||
|
||||
// Only use for rng affecting dynamic elements like chests and entities!
|
||||
let mut dynamic_rng = rand::thread_rng();
|
||||
let mut dynamic_rng = ChaCha8Rng::from_seed(thread_rng().gen());
|
||||
|
||||
// Apply layers (paths, caves, etc.)
|
||||
let mut canvas = Canvas {
|
||||
@ -346,12 +347,27 @@ impl World {
|
||||
entities: Vec::new(),
|
||||
};
|
||||
|
||||
layer::apply_caves_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_paths_to(&mut canvas);
|
||||
layer::apply_spots_to(&mut canvas, &mut dynamic_rng);
|
||||
if index.features.caverns {
|
||||
layer::apply_caverns_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.caves {
|
||||
layer::apply_caves_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.shrubs {
|
||||
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.trees {
|
||||
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.scatter {
|
||||
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.paths {
|
||||
layer::apply_paths_to(&mut canvas);
|
||||
}
|
||||
if index.features.spots {
|
||||
layer::apply_spots_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
// layer::apply_coral_to(&mut canvas);
|
||||
|
||||
// Apply site generation
|
||||
@ -364,7 +380,7 @@ impl World {
|
||||
entities: canvas.entities,
|
||||
};
|
||||
|
||||
let gen_entity_pos = |dynamic_rng: &mut rand::rngs::ThreadRng| {
|
||||
let gen_entity_pos = |dynamic_rng: &mut ChaCha8Rng| {
|
||||
let lpos2d = TerrainChunkSize::RECT_SIZE
|
||||
.map(|sz| dynamic_rng.gen::<u32>().rem_euclid(sz) as i32);
|
||||
let mut lpos = Vec3::new(
|
||||
|
Loading…
Reference in New Issue
Block a user