mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/worldgen' into 'master'
Lighting, worldgen, smooth shadows, colour blending See merge request veloren/veloren!511
This commit is contained in:
commit
b57031804e
@ -28,8 +28,15 @@ uniform u_bones {
|
||||
out vec4 tgt_color;
|
||||
|
||||
void main() {
|
||||
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) + light_at(f_pos, f_norm);
|
||||
vec3 surf_color = srgb_to_linear(model_col.rgb * f_col) * 4.0 * light;
|
||||
vec3 light, diffuse_light, ambient_light;
|
||||
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
|
||||
float point_shadow = shadow_at(f_pos, f_norm);
|
||||
diffuse_light *= point_shadow;
|
||||
ambient_light *= point_shadow;
|
||||
vec3 point_light = light_at(f_pos, f_norm);
|
||||
light += point_light;
|
||||
diffuse_light += point_light;
|
||||
vec3 surf_color = illuminate(srgb_to_linear(model_col.rgb * f_col), light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
|
||||
|
@ -31,16 +31,16 @@ void main() {
|
||||
mat4 combined_mat = model_mat * bones[v_bone_idx].bone_mat;
|
||||
|
||||
f_pos = (
|
||||
combined_mat *
|
||||
combined_mat *
|
||||
vec4(v_pos, 1)).xyz;
|
||||
|
||||
f_col = v_col;
|
||||
|
||||
// Calculate normal here rather than for each pixel in the fragment shader
|
||||
f_norm = (
|
||||
combined_mat *
|
||||
f_norm = normalize((
|
||||
combined_mat *
|
||||
vec4(v_norm, 0.0)
|
||||
).xyz;
|
||||
).xyz);
|
||||
|
||||
gl_Position = proj_mat * view_mat * vec4(f_pos, 1);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ in float f_light;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec3 model_offs;
|
||||
float load_time;
|
||||
};
|
||||
|
||||
out vec4 tgt_color;
|
||||
@ -37,8 +38,15 @@ void main() {
|
||||
|
||||
vec3 norm = warp_normal(f_norm, f_pos, tick.x);
|
||||
|
||||
vec3 light = get_sun_diffuse(norm, time_of_day.x) * f_light + light_at(f_pos, norm);
|
||||
vec3 surf_color = f_col * light;
|
||||
vec3 light, diffuse_light, ambient_light;
|
||||
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
|
||||
float point_shadow = shadow_at(f_pos, f_norm);
|
||||
diffuse_light *= f_light * point_shadow;
|
||||
ambient_light *= f_light, point_shadow;
|
||||
vec3 point_light = light_at(f_pos, f_norm);
|
||||
light += point_light;
|
||||
diffuse_light += point_light;
|
||||
vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
|
||||
@ -52,7 +60,7 @@ void main() {
|
||||
// 0 = 100% reflection, 1 = translucent water
|
||||
float passthrough = pow(dot(faceforward(norm, norm, cam_to_frag), -cam_to_frag), 1.0);
|
||||
|
||||
vec4 color = mix(vec4(reflect_color, 1.0), vec4(surf_color, 0.5 / (1.0 + light * 2.0)), passthrough);
|
||||
vec4 color = mix(vec4(reflect_color, 1.0), vec4(surf_color, 0.5 / (1.0 + diffuse_light * 2.0)), passthrough);
|
||||
|
||||
tgt_color = mix(color, vec4(fog_color, 0.0), fog_level);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ in uint v_col_light;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec3 model_offs;
|
||||
float load_time;
|
||||
};
|
||||
|
||||
out vec3 f_pos;
|
||||
|
@ -8,6 +8,7 @@ uniform u_globals {
|
||||
vec4 time_of_day;
|
||||
vec4 tick;
|
||||
vec4 screen_res;
|
||||
uvec4 light_count;
|
||||
uvec4 light_shadow_count;
|
||||
uvec4 medium;
|
||||
ivec4 select_pos;
|
||||
};
|
||||
|
@ -8,8 +8,22 @@ uniform u_lights {
|
||||
Light lights[32];
|
||||
};
|
||||
|
||||
struct Shadow {
|
||||
vec4 shadow_pos_radius;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
uniform u_shadows {
|
||||
Shadow shadows[24];
|
||||
};
|
||||
|
||||
#include <srgb.glsl>
|
||||
|
||||
vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) {
|
||||
float avg_col = (color.r + color.g + color.b) / 3.0;
|
||||
return ((color - avg_col) * light + (diffuse + ambience) * avg_col) * diffuse;
|
||||
}
|
||||
|
||||
float attenuation_strength(vec3 rpos) {
|
||||
return 1.0 / (rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z);
|
||||
}
|
||||
@ -19,7 +33,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
|
||||
|
||||
vec3 light = vec3(0);
|
||||
|
||||
for (uint i = 0u; i < light_count.x; i++) {
|
||||
for (uint i = 0u; i < light_shadow_count.x; i ++) {
|
||||
|
||||
// Only access the array once
|
||||
Light L = lights[i];
|
||||
@ -29,21 +43,35 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
|
||||
// Pre-calculate difference between light and fragment
|
||||
vec3 difference = light_pos - wpos;
|
||||
|
||||
float strength = attenuation_strength(difference);
|
||||
float strength = pow(attenuation_strength(difference), 0.6);
|
||||
|
||||
// Multiply the vec3 only once
|
||||
vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a);
|
||||
|
||||
// This is commented out to avoid conditional branching. See here: https://community.khronos.org/t/glsl-float-multiply-by-zero/104391
|
||||
// if (max(max(color.r, color.g), color.b) < 0.002) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Old: light += color * clamp(dot(normalize(difference), wnorm), LIGHT_AMBIENCE, 1.0);
|
||||
|
||||
// The dot product cannot be greater than one, so no need to clamp max value
|
||||
// Also, rather than checking if it is smaller than LIGHT_AMBIENCE, add LIGHT_AMBIENCE instead
|
||||
light += color * (max(0, dot(normalize(difference), wnorm)) + LIGHT_AMBIENCE);
|
||||
}
|
||||
return light;
|
||||
}
|
||||
}
|
||||
|
||||
float shadow_at(vec3 wpos, vec3 wnorm) {
|
||||
float shadow = 1.0;
|
||||
|
||||
for (uint i = 0u; i < light_shadow_count.y; i ++) {
|
||||
|
||||
// Only access the array once
|
||||
Shadow S = shadows[i];
|
||||
|
||||
vec3 shadow_pos = S.shadow_pos_radius.xyz;
|
||||
float radius = S.shadow_pos_radius.w;
|
||||
|
||||
vec3 diff = shadow_pos - wpos;
|
||||
if (diff.z >= 0.0) {
|
||||
diff.z = diff.z * 0.1;
|
||||
}
|
||||
|
||||
float shade = max(pow(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, 0.25) / pow(radius * radius * 0.5, 0.25), 0.5);
|
||||
|
||||
shadow = min(shadow, shade);
|
||||
}
|
||||
return min(shadow, 1.0);
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ const float PI = 3.141592;
|
||||
const vec3 SKY_DAY_TOP = vec3(0.1, 0.2, 0.9);
|
||||
const vec3 SKY_DAY_MID = vec3(0.02, 0.08, 0.8);
|
||||
const vec3 SKY_DAY_BOT = vec3(0.02, 0.01, 0.3);
|
||||
const vec3 DAY_LIGHT = vec3(1.3, 0.9, 1.1);
|
||||
const vec3 DAY_LIGHT = vec3(1.2, 1.0, 1.0);
|
||||
const vec3 SUN_HALO_DAY = vec3(0.35, 0.35, 0.0);
|
||||
|
||||
const vec3 SKY_DUSK_TOP = vec3(0.06, 0.1, 0.20);
|
||||
const vec3 SKY_DUSK_MID = vec3(0.35, 0.1, 0.15);
|
||||
const vec3 SKY_DUSK_BOT = vec3(0.0, 0.1, 0.13);
|
||||
const vec3 DUSK_LIGHT = vec3(3.0, 0.65, 0.3);
|
||||
const vec3 DUSK_LIGHT = vec3(3.0, 1.0, 0.3);
|
||||
const vec3 SUN_HALO_DUSK = vec3(0.6, 0.1, 0.0);
|
||||
|
||||
const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025);
|
||||
const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02);
|
||||
@ -26,14 +28,14 @@ vec3 get_sun_dir(float time_of_day) {
|
||||
return sun_dir;
|
||||
}
|
||||
|
||||
const float PERSISTENT_AMBIANCE = 0.1;
|
||||
|
||||
float get_sun_brightness(vec3 sun_dir) {
|
||||
return max(-sun_dir.z + 0.6, 0.0);
|
||||
return max(-sun_dir.z + 0.6, 0.0) * 0.8;
|
||||
}
|
||||
|
||||
const float PERSISTENT_AMBIANCE = 0.008;
|
||||
|
||||
vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
|
||||
const float SUN_AMBIANCE = 0.15;
|
||||
void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light) {
|
||||
const float SUN_AMBIANCE = 0.1;
|
||||
|
||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||
|
||||
@ -51,9 +53,11 @@ vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
|
||||
max(-sun_dir.z, 0)
|
||||
);
|
||||
|
||||
vec3 diffuse_light = (SUN_AMBIANCE + max(dot(-norm, sun_dir), 0.0) * sun_color) * sun_light + PERSISTENT_AMBIANCE;
|
||||
vec3 sun_chroma = sun_color * sun_light;
|
||||
|
||||
return diffuse_light;
|
||||
light = sun_chroma + PERSISTENT_AMBIANCE;
|
||||
diffuse_light = sun_chroma * (dot(-norm, sun_dir) * 0.5 + 0.5) + PERSISTENT_AMBIANCE;
|
||||
ambient_light = vec3(SUN_AMBIANCE * sun_light);
|
||||
}
|
||||
|
||||
// This has been extracted into a function to allow quick exit when detecting a star.
|
||||
@ -99,7 +103,7 @@ vec3 get_sky_color(vec3 dir, float time_of_day, bool with_stars) {
|
||||
mix(
|
||||
SKY_DUSK_TOP + star,
|
||||
SKY_NIGHT_TOP + star,
|
||||
max(sun_dir.z, 0)
|
||||
max(pow(sun_dir.z, 0.2), 0)
|
||||
),
|
||||
SKY_DAY_TOP,
|
||||
max(-sun_dir.z, 0)
|
||||
@ -109,7 +113,7 @@ vec3 get_sky_color(vec3 dir, float time_of_day, bool with_stars) {
|
||||
mix(
|
||||
SKY_DUSK_MID,
|
||||
SKY_NIGHT_MID,
|
||||
max(sun_dir.z, 0)
|
||||
max(pow(sun_dir.z, 0.2), 0)
|
||||
),
|
||||
SKY_DAY_MID,
|
||||
max(-sun_dir.z, 0)
|
||||
@ -119,7 +123,7 @@ vec3 get_sky_color(vec3 dir, float time_of_day, bool with_stars) {
|
||||
mix(
|
||||
SKY_DUSK_BOT,
|
||||
SKY_NIGHT_BOT,
|
||||
max(sun_dir.z, 0)
|
||||
max(pow(sun_dir.z, 0.2), 0)
|
||||
),
|
||||
SKY_DAY_BOT,
|
||||
max(-sun_dir.z, 0)
|
||||
@ -135,12 +139,17 @@ vec3 get_sky_color(vec3 dir, float time_of_day, bool with_stars) {
|
||||
max(dir.z, 0)
|
||||
);
|
||||
|
||||
vec3 sun_halo_color = mix(
|
||||
SUN_HALO_DUSK,
|
||||
SUN_HALO_DAY,
|
||||
max(-sun_dir.z, 0)
|
||||
);
|
||||
|
||||
// Sun
|
||||
|
||||
const vec3 SUN_HALO_COLOR = vec3(1.5, 0.35, 0.0) * 0.3;
|
||||
const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0;
|
||||
|
||||
vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * SUN_HALO_COLOR;
|
||||
vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * sun_halo_color;
|
||||
vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.0045, 0.0), 1000.0) * SUN_SURF_COLOR;
|
||||
vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1);
|
||||
|
||||
|
@ -160,6 +160,8 @@ vec3 hsv2rgb(vec3 c) {
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
const float FXAA_SCALE = 1.5;
|
||||
|
||||
void main() {
|
||||
vec2 uv = (f_pos + 1.0) * 0.5;
|
||||
|
||||
@ -167,7 +169,7 @@ void main() {
|
||||
uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1);
|
||||
}
|
||||
|
||||
vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy, screen_res.xy);
|
||||
vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy * FXAA_SCALE, screen_res.xy * FXAA_SCALE);
|
||||
//vec4 fxaa_color = texture(src_color, uv);
|
||||
|
||||
vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a);
|
||||
|
@ -16,8 +16,15 @@ const float RENDER_DIST = 112.0;
|
||||
const float FADE_DIST = 32.0;
|
||||
|
||||
void main() {
|
||||
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm);
|
||||
vec3 surf_color = f_col * light;
|
||||
vec3 light, diffuse_light, ambient_light;
|
||||
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
|
||||
float point_shadow = shadow_at(f_pos, f_norm);
|
||||
diffuse_light *= f_light * point_shadow;
|
||||
ambient_light *= f_light, point_shadow;
|
||||
vec3 point_light = light_at(f_pos, f_norm);
|
||||
light += point_light;
|
||||
diffuse_light += point_light;
|
||||
vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
|
||||
|
@ -27,6 +27,8 @@ void main() {
|
||||
inst_mat[2] = inst_mat2;
|
||||
inst_mat[3] = inst_mat3;
|
||||
|
||||
vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz;
|
||||
|
||||
f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz;
|
||||
|
||||
// Wind waving
|
||||
@ -40,6 +42,11 @@ void main() {
|
||||
|
||||
f_col = srgb_to_linear(v_col) * srgb_to_linear(inst_col);
|
||||
|
||||
// Select glowing
|
||||
if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {
|
||||
f_col *= 4.0;
|
||||
}
|
||||
|
||||
f_light = 1.0;
|
||||
|
||||
gl_Position =
|
||||
|
@ -10,6 +10,7 @@ in float f_light;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec3 model_offs;
|
||||
float load_time;
|
||||
};
|
||||
|
||||
out vec4 tgt_color;
|
||||
@ -18,8 +19,15 @@ out vec4 tgt_color;
|
||||
#include <light.glsl>
|
||||
|
||||
void main() {
|
||||
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm);
|
||||
vec3 surf_color = f_col * light;
|
||||
vec3 light, diffuse_light, ambient_light;
|
||||
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
|
||||
float point_shadow = shadow_at(f_pos, f_norm);
|
||||
diffuse_light *= f_light * point_shadow;
|
||||
ambient_light *= f_light * point_shadow;
|
||||
vec3 point_light = light_at(f_pos, f_norm);
|
||||
light += point_light;
|
||||
diffuse_light += point_light;
|
||||
vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
|
||||
|
@ -9,6 +9,7 @@ in uint v_col_light;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec3 model_offs;
|
||||
float load_time;
|
||||
};
|
||||
|
||||
out vec3 f_pos;
|
||||
@ -26,6 +27,8 @@ void main() {
|
||||
float((v_pos_norm >> 16) & 0x1FFFu)
|
||||
) + model_offs;
|
||||
|
||||
f_pos.z *= min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0);
|
||||
|
||||
// TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction.
|
||||
uint norm_axis = (v_pos_norm >> 30) & 0x3u;
|
||||
|
||||
|
BIN
assets/voxygen/voxel/sprite/velorite/velorite.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/velorite/velorite.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/module/human/corner_ground.vox
(Stored with Git LFS)
BIN
assets/world/module/human/corner_ground.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/corner_upstairs.vox
(Stored with Git LFS)
BIN
assets/world/module/human/corner_upstairs.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/window_corner_ground.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/module/human/window_corner_ground.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/module/human/window_corner_upstairs.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/module/human/window_corner_upstairs.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/module/human/window_ground.vox
(Stored with Git LFS)
BIN
assets/world/module/human/window_ground.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/misc/well.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/module/misc/well.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -250,6 +250,10 @@ impl Client {
|
||||
self.postbox.send_message(ClientMsg::BreakBlock(pos));
|
||||
}
|
||||
|
||||
pub fn collect_block(&mut self, pos: Vec3<i32>) {
|
||||
self.postbox.send_message(ClientMsg::CollectBlock(pos));
|
||||
}
|
||||
|
||||
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||
#[allow(dead_code)]
|
||||
pub fn tick(
|
||||
|
@ -1,3 +1,8 @@
|
||||
use crate::{
|
||||
comp,
|
||||
effect::Effect,
|
||||
terrain::{Block, BlockKind},
|
||||
};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
|
||||
@ -70,10 +75,38 @@ impl Armor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum ConsumptionEffect {
|
||||
Health(i32),
|
||||
Xp(i32),
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Consumable {
|
||||
Apple,
|
||||
Potion,
|
||||
Mushroom,
|
||||
Velorite,
|
||||
}
|
||||
|
||||
impl Consumable {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Consumable::Apple => "apple",
|
||||
Consumable::Potion => "potion",
|
||||
Consumable::Mushroom => "mushroom",
|
||||
Consumable::Velorite => "velorite",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Ingredient {
|
||||
Flower,
|
||||
Grass,
|
||||
}
|
||||
|
||||
impl Ingredient {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Ingredient::Flower => "flower",
|
||||
Ingredient::Grass => "grass",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@ -93,9 +126,12 @@ pub enum Item {
|
||||
health_bonus: i32,
|
||||
},
|
||||
Consumable {
|
||||
effect: ConsumptionEffect,
|
||||
kind: Consumable,
|
||||
effect: Effect,
|
||||
},
|
||||
Ingredient {
|
||||
kind: Ingredient,
|
||||
},
|
||||
Ingredient,
|
||||
Debug(Debug),
|
||||
}
|
||||
|
||||
@ -104,8 +140,8 @@ impl Item {
|
||||
match self {
|
||||
Item::Tool { kind, .. } => kind.name(),
|
||||
Item::Armor { kind, .. } => kind.name(),
|
||||
Item::Consumable { .. } => "<consumable>",
|
||||
Item::Ingredient => "<ingredient>",
|
||||
Item::Consumable { kind, .. } => kind.name(),
|
||||
Item::Ingredient { kind } => kind.name(),
|
||||
Item::Debug(_) => "Debugging item",
|
||||
}
|
||||
}
|
||||
@ -115,7 +151,7 @@ impl Item {
|
||||
Item::Tool { .. } => "tool",
|
||||
Item::Armor { .. } => "armour",
|
||||
Item::Consumable { .. } => "consumable",
|
||||
Item::Ingredient => "ingredient",
|
||||
Item::Ingredient { .. } => "ingredient",
|
||||
Item::Debug(_) => "debug",
|
||||
}
|
||||
}
|
||||
@ -123,6 +159,60 @@ impl Item {
|
||||
pub fn description(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.category())
|
||||
}
|
||||
|
||||
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
|
||||
match block.kind() {
|
||||
BlockKind::Apple => Some(Self::apple()),
|
||||
BlockKind::Mushroom => Some(Self::mushroom()),
|
||||
BlockKind::Velorite => Some(Self::velorite()),
|
||||
BlockKind::BlueFlower => Some(Self::flower()),
|
||||
BlockKind::PinkFlower => Some(Self::flower()),
|
||||
BlockKind::PurpleFlower => Some(Self::flower()),
|
||||
BlockKind::RedFlower => Some(Self::flower()),
|
||||
BlockKind::WhiteFlower => Some(Self::flower()),
|
||||
BlockKind::YellowFlower => Some(Self::flower()),
|
||||
BlockKind::Sunflower => Some(Self::flower()),
|
||||
BlockKind::LongGrass => Some(Self::grass()),
|
||||
BlockKind::MediumGrass => Some(Self::grass()),
|
||||
BlockKind::ShortGrass => Some(Self::grass()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// General item constructors
|
||||
|
||||
pub fn apple() -> Self {
|
||||
Item::Consumable {
|
||||
kind: Consumable::Apple,
|
||||
effect: Effect::Health(20, comp::HealthSource::Item),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mushroom() -> Self {
|
||||
Item::Consumable {
|
||||
kind: Consumable::Mushroom,
|
||||
effect: Effect::Health(10, comp::HealthSource::Item),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn velorite() -> Self {
|
||||
Item::Consumable {
|
||||
kind: Consumable::Mushroom,
|
||||
effect: Effect::Xp(250),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flower() -> Self {
|
||||
Item::Ingredient {
|
||||
kind: Ingredient::Flower,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grass() -> Self {
|
||||
Item::Ingredient {
|
||||
kind: Ingredient::Grass,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Item {
|
||||
|
@ -5,7 +5,6 @@ pub mod item;
|
||||
pub use self::item::{Debug, Item, Tool};
|
||||
|
||||
use specs::{Component, HashMapStorage, NullStorage};
|
||||
//use specs_idvs::IDVStorage;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Inventory {
|
||||
@ -46,6 +45,10 @@ impl Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.slots.iter().all(|slot| slot.is_some())
|
||||
}
|
||||
|
||||
/// Get content of a slot
|
||||
pub fn get(&self, cell: usize) -> Option<&Item> {
|
||||
self.slots.get(cell).and_then(Option::as_ref)
|
||||
|
22
common/src/comp/location.rs
Normal file
22
common/src/comp/location.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Waypoint {
|
||||
pos: Vec3<f32>,
|
||||
}
|
||||
|
||||
impl Waypoint {
|
||||
pub fn new(pos: Vec3<f32>) -> Self {
|
||||
Self { pos }
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> Vec3<f32> {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Waypoint {
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
@ -6,6 +6,7 @@ mod controller;
|
||||
mod inputs;
|
||||
mod inventory;
|
||||
mod last;
|
||||
mod location;
|
||||
mod phys;
|
||||
mod player;
|
||||
mod stats;
|
||||
@ -20,7 +21,8 @@ pub use controller::{ControlEvent, Controller, MountState, Mounting};
|
||||
pub use inputs::CanBuild;
|
||||
pub use inventory::{item, Inventory, InventoryUpdate, Item};
|
||||
pub use last::Last;
|
||||
pub use phys::{ForceUpdate, Ori, PhysicsState, Pos, Scale, Vel};
|
||||
pub use location::Waypoint;
|
||||
pub use phys::{ForceUpdate, Mass, Ori, PhysicsState, Pos, Scale, Vel};
|
||||
pub use player::Player;
|
||||
pub use stats::{Equipment, Exp, HealthSource, Level, Stats};
|
||||
pub use visual::LightEmitter;
|
||||
|
@ -34,6 +34,14 @@ impl Component for Scale {
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
||||
|
||||
// Mass
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Mass(pub f32);
|
||||
|
||||
impl Component for Mass {
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
||||
|
||||
// PhysicsState
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PhysicsState {
|
||||
|
@ -2,7 +2,7 @@ use crate::{comp, state::Uid};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum HealthSource {
|
||||
Attack { by: Uid }, // TODO: Implement weapon
|
||||
Suicide,
|
||||
@ -10,6 +10,7 @@ pub enum HealthSource {
|
||||
Revive,
|
||||
Command,
|
||||
LevelUp,
|
||||
Item,
|
||||
Unknown,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
|
8
common/src/effect.rs
Normal file
8
common/src/effect.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::comp;
|
||||
|
||||
/// An effect that may be applied to an entity
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Effect {
|
||||
Health(i32, comp::HealthSource),
|
||||
Xp(i64),
|
||||
}
|
@ -16,6 +16,7 @@ extern crate log;
|
||||
pub mod assets;
|
||||
pub mod clock;
|
||||
pub mod comp;
|
||||
pub mod effect;
|
||||
pub mod event;
|
||||
pub mod figure;
|
||||
pub mod msg;
|
||||
|
@ -19,6 +19,7 @@ pub enum ClientMsg {
|
||||
SetViewDistance(u32),
|
||||
BreakBlock(Vec3<i32>),
|
||||
PlaceBlock(Vec3<i32>, Block),
|
||||
CollectBlock(Vec3<i32>),
|
||||
Ping,
|
||||
Pong,
|
||||
ChatMsg {
|
||||
|
@ -29,6 +29,7 @@ sphynx::sum_type! {
|
||||
Scale(comp::Scale),
|
||||
MountState(comp::MountState),
|
||||
Mounting(comp::Mounting),
|
||||
Mass(comp::Mass),
|
||||
}
|
||||
}
|
||||
// Automatically derive From<T> for EcsCompPhantom
|
||||
@ -48,6 +49,7 @@ sphynx::sum_type! {
|
||||
Scale(PhantomData<comp::Scale>),
|
||||
MountState(PhantomData<comp::MountState>),
|
||||
Mounting(PhantomData<comp::Mounting>),
|
||||
Mass(PhantomData<comp::Mass>),
|
||||
}
|
||||
}
|
||||
impl sphynx::CompPacket for EcsCompPacket {
|
||||
|
@ -55,6 +55,15 @@ impl BlockChange {
|
||||
self.blocks.insert(pos, block);
|
||||
}
|
||||
|
||||
pub fn try_set(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
||||
if !self.blocks.contains_key(&pos) {
|
||||
self.blocks.insert(pos, block);
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.blocks.clear();
|
||||
}
|
||||
@ -122,6 +131,7 @@ impl State {
|
||||
ecs.register_synced::<comp::Scale>();
|
||||
ecs.register_synced::<comp::Mounting>();
|
||||
ecs.register_synced::<comp::MountState>();
|
||||
ecs.register_synced::<comp::Mass>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
ecs.register::<comp::Controller>();
|
||||
@ -146,6 +156,7 @@ impl State {
|
||||
ecs.register::<comp::InventoryUpdate>();
|
||||
ecs.register::<comp::Inventory>();
|
||||
ecs.register::<comp::Admin>();
|
||||
ecs.register::<comp::Waypoint>();
|
||||
|
||||
// Register synced resources used by the ECS.
|
||||
ecs.insert_synced(TimeOfDay(0.0));
|
||||
@ -234,11 +245,16 @@ impl State {
|
||||
self.ecs.write_resource()
|
||||
}
|
||||
|
||||
/// Get a writable reference to this state's terrain.
|
||||
/// Set a block in this state's terrain.
|
||||
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
||||
}
|
||||
|
||||
/// Set a block in this state's terrain. Will return `None` if the block has already been modified this tick.
|
||||
pub fn try_set_block(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
||||
self.ecs.write_resource::<BlockChange>().try_set(pos, block)
|
||||
}
|
||||
|
||||
/// Removes every chunk of the terrain.
|
||||
pub fn clear_terrain(&mut self) {
|
||||
let keys = self
|
||||
|
@ -1,6 +1,6 @@
|
||||
use {
|
||||
crate::{
|
||||
comp::{Body, Mounting, Ori, PhysicsState, Pos, Scale, Vel},
|
||||
comp::{Body, Mass, Mounting, Ori, PhysicsState, Pos, Scale, Vel},
|
||||
event::{EventBus, LocalEvent},
|
||||
state::DeltaTime,
|
||||
terrain::{Block, TerrainGrid},
|
||||
@ -45,6 +45,7 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Mass>,
|
||||
ReadStorage<'a, Body>,
|
||||
WriteStorage<'a, PhysicsState>,
|
||||
WriteStorage<'a, Pos>,
|
||||
@ -61,6 +62,7 @@ impl<'a> System<'a> for Sys {
|
||||
dt,
|
||||
event_bus,
|
||||
scales,
|
||||
masses,
|
||||
bodies,
|
||||
mut physics_states,
|
||||
mut positions,
|
||||
@ -320,9 +322,10 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Apply pushback
|
||||
for (pos, scale, vel, _, _) in (
|
||||
for (pos, scale, mass, vel, _, _) in (
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
&mut velocities,
|
||||
&bodies,
|
||||
!&mountings,
|
||||
@ -330,10 +333,18 @@ impl<'a> System<'a> for Sys {
|
||||
.join()
|
||||
{
|
||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||
for (pos_other, scale_other, _, _) in
|
||||
(&positions, scales.maybe(), &bodies, !&mountings).join()
|
||||
let mass = mass.map(|m| m.0).unwrap_or(scale);
|
||||
for (pos_other, scale_other, mass_other, _, _) in (
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
&bodies,
|
||||
!&mountings,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0);
|
||||
let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other);
|
||||
let diff = Vec2::<f32>::from(pos.0 - pos_other.0);
|
||||
|
||||
let collision_dist = 0.95 * (scale + scale_other);
|
||||
@ -343,8 +354,9 @@ impl<'a> System<'a> for Sys {
|
||||
&& pos.0.z + 1.6 * scale > pos_other.0.z
|
||||
&& pos.0.z < pos_other.0.z + 1.6 * scale_other
|
||||
{
|
||||
vel.0 +=
|
||||
Vec3::from(diff.normalized()) * (collision_dist - diff.magnitude()) * 1.0;
|
||||
let force = (collision_dist - diff.magnitude()) * 2.0 * mass_other
|
||||
/ (mass + mass_other);
|
||||
vel.0 += Vec3::from(diff.normalized()) * force;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,17 @@ pub enum BlockKind {
|
||||
Apple,
|
||||
Mushroom,
|
||||
Liana,
|
||||
Velorite,
|
||||
}
|
||||
|
||||
impl BlockKind {
|
||||
pub fn is_tangible(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => false,
|
||||
kind => !kind.is_fluid(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_air(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => true,
|
||||
@ -54,6 +62,7 @@ impl BlockKind {
|
||||
BlockKind::Apple => true,
|
||||
BlockKind::Mushroom => true,
|
||||
BlockKind::Liana => true,
|
||||
BlockKind::Velorite => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -88,6 +97,7 @@ impl BlockKind {
|
||||
BlockKind::Apple => false,
|
||||
BlockKind::Mushroom => false,
|
||||
BlockKind::Liana => false,
|
||||
BlockKind::Velorite => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -118,6 +128,25 @@ impl BlockKind {
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::BlueFlower => true,
|
||||
BlockKind::PinkFlower => true,
|
||||
BlockKind::PurpleFlower => true,
|
||||
BlockKind::RedFlower => true,
|
||||
BlockKind::WhiteFlower => true,
|
||||
BlockKind::YellowFlower => true,
|
||||
BlockKind::Sunflower => true,
|
||||
BlockKind::LongGrass => true,
|
||||
BlockKind::MediumGrass => true,
|
||||
BlockKind::ShortGrass => true,
|
||||
BlockKind::Apple => true,
|
||||
BlockKind::Mushroom => true,
|
||||
BlockKind::Velorite => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -14,39 +14,44 @@ pub enum DynaError {
|
||||
// V = Voxel
|
||||
// S = Size (replace when const generics are a thing)
|
||||
// M = Metadata
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Dyna<V: Vox, M> {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Dyna<V: Vox, M, A: Access = ColumnAccess> {
|
||||
vox: Vec<V>,
|
||||
meta: M,
|
||||
sz: Vec3<u32>,
|
||||
_phantom: std::marker::PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<V: Vox, M> Dyna<V, M> {
|
||||
impl<V: Vox, M: Clone, A: Access> Clone for Dyna<V, M, A> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
vox: self.vox.clone(),
|
||||
meta: self.meta.clone(),
|
||||
sz: self.sz,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> Dyna<V, M, A> {
|
||||
/// Used to transform a voxel position in the volume into its corresponding index
|
||||
/// in the voxel array.
|
||||
#[inline(always)]
|
||||
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
|
||||
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
|
||||
Some(Self::idx_for_unchecked(sz, pos))
|
||||
Some(A::idx(pos, sz))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to transform a voxel position in the volume into its corresponding index
|
||||
/// in the voxel array.
|
||||
#[inline(always)]
|
||||
fn idx_for_unchecked(sz: Vec3<u32>, pos: Vec3<i32>) -> usize {
|
||||
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M> BaseVol for Dyna<V, M> {
|
||||
impl<V: Vox, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||
type Vox = V;
|
||||
type Error = DynaError;
|
||||
}
|
||||
|
||||
impl<V: Vox, M> SizedVol for Dyna<V, M> {
|
||||
impl<V: Vox, M, A: Access> SizedVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn lower_bound(&self) -> Vec3<i32> {
|
||||
Vec3::zero()
|
||||
@ -58,7 +63,7 @@ impl<V: Vox, M> SizedVol for Dyna<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M> ReadVol for Dyna<V, M> {
|
||||
impl<V: Vox, M, A: Access> ReadVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&V, DynaError> {
|
||||
Self::idx_for(self.sz, pos)
|
||||
@ -67,7 +72,7 @@ impl<V: Vox, M> ReadVol for Dyna<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M> WriteVol for Dyna<V, M> {
|
||||
impl<V: Vox, M, A: Access> WriteVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), DynaError> {
|
||||
Self::idx_for(self.sz, pos)
|
||||
@ -77,7 +82,7 @@ impl<V: Vox, M> WriteVol for Dyna<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, M> IntoPosIterator for &'a Dyna<V, M> {
|
||||
impl<'a, V: Vox, M, A: Access> IntoPosIterator for &'a Dyna<V, M, A> {
|
||||
type IntoIter = DefaultPosIterator;
|
||||
|
||||
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
@ -85,15 +90,15 @@ impl<'a, V: Vox, M> IntoPosIterator for &'a Dyna<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, M> IntoVolIterator<'a> for &'a Dyna<V, M> {
|
||||
type IntoIter = DefaultVolIterator<'a, Dyna<V, M>>;
|
||||
impl<'a, V: Vox, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
|
||||
type IntoIter = DefaultVolIterator<'a, Dyna<V, M, A>>;
|
||||
|
||||
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
Self::IntoIter::new(self, lower_bound, upper_bound)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox + Clone, M> Dyna<V, M> {
|
||||
impl<V: Vox + Clone, M, A: Access> Dyna<V, M, A> {
|
||||
/// Create a new `Dyna` with the provided dimensions and all voxels filled with duplicates of
|
||||
/// the provided voxel.
|
||||
pub fn filled(sz: Vec3<u32>, vox: V, meta: M) -> Self {
|
||||
@ -101,6 +106,7 @@ impl<V: Vox + Clone, M> Dyna<V, M> {
|
||||
vox: vec![vox; sz.product() as usize],
|
||||
meta,
|
||||
sz,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,3 +120,15 @@ impl<V: Vox + Clone, M> Dyna<V, M> {
|
||||
&mut self.meta
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Access {
|
||||
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize;
|
||||
}
|
||||
|
||||
pub struct ColumnAccess;
|
||||
|
||||
impl Access for ColumnAccess {
|
||||
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize {
|
||||
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize
|
||||
}
|
||||
}
|
||||
|
@ -189,14 +189,21 @@ lazy_static! {
|
||||
"explosion",
|
||||
"{}",
|
||||
"/explosion <radius> : Explodes the ground around you",
|
||||
false,
|
||||
true,
|
||||
handle_explosion,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"waypoint",
|
||||
"{}",
|
||||
"/waypoint : Set your waypoint to your current position",
|
||||
true,
|
||||
handle_waypoint,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"adminify",
|
||||
"{}",
|
||||
"/adminify <playername> : Temporarily gives a player admin permissions or removes them",
|
||||
true,
|
||||
false, // TODO: NO
|
||||
handle_adminify,
|
||||
),
|
||||
ChatCommand::new(
|
||||
@ -699,7 +706,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
|
||||
.write_storage::<comp::LightEmitter>()
|
||||
.get_mut(entity)
|
||||
{
|
||||
light.strength = s.max(0.1).min(20.0);
|
||||
light.strength = s.max(0.1).min(10.0);
|
||||
server.clients.notify(
|
||||
entity,
|
||||
ServerMsg::private(String::from("You played with flame strength.")),
|
||||
@ -727,9 +734,9 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
|
||||
offset: Vec3::new(0.5, 0.2, 0.8),
|
||||
col: Rgb::new(1.0, 0.75, 0.3),
|
||||
strength: if let Some(s) = opt_s {
|
||||
s.max(0.0).min(20.0)
|
||||
s.max(0.0).min(10.0)
|
||||
} else {
|
||||
6.0
|
||||
3.0
|
||||
},
|
||||
},
|
||||
);
|
||||
@ -757,6 +764,25 @@ fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_waypoint(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
|
||||
match server.state.read_component_cloned::<comp::Pos>(entity) {
|
||||
Some(pos) => {
|
||||
let _ = server
|
||||
.state
|
||||
.ecs()
|
||||
.write_storage::<comp::Waypoint>()
|
||||
.insert(entity, comp::Waypoint::new(pos.0));
|
||||
server
|
||||
.clients
|
||||
.notify(entity, ServerMsg::private(String::from("Waypoint set!")));
|
||||
}
|
||||
None => server.clients.notify(
|
||||
entity,
|
||||
ServerMsg::private(String::from("You have no position!")),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
|
||||
let ecs = server.state.ecs();
|
||||
|
@ -19,6 +19,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp,
|
||||
effect::Effect,
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg},
|
||||
net::PostOffice,
|
||||
@ -355,6 +356,11 @@ impl Server {
|
||||
ServerEvent::Respawn(entity) => {
|
||||
// Only clients can respawn
|
||||
if let Some(client) = clients.get_mut(&entity) {
|
||||
let respawn_point = state
|
||||
.read_component_cloned::<comp::Waypoint>(entity)
|
||||
.map(|wp| wp.get_pos())
|
||||
.unwrap_or(state.ecs().read_resource::<SpawnPoint>().0);
|
||||
|
||||
client.allow_state(ClientState::Character);
|
||||
state
|
||||
.ecs_mut()
|
||||
@ -365,7 +371,7 @@ impl Server {
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Pos>()
|
||||
.get_mut(entity)
|
||||
.map(|pos| pos.0.z += 20.0);
|
||||
.map(|pos| pos.0 = respawn_point);
|
||||
let _ = state
|
||||
.ecs_mut()
|
||||
.write_storage()
|
||||
@ -765,7 +771,6 @@ impl Server {
|
||||
let mut new_chat_msgs = Vec::new();
|
||||
let mut disconnected_clients = Vec::new();
|
||||
let mut requested_chunks = Vec::new();
|
||||
let mut modified_blocks = Vec::new();
|
||||
let mut dropped_items = Vec::new();
|
||||
|
||||
self.clients.remove_if(|entity, client| {
|
||||
@ -867,6 +872,17 @@ impl Server {
|
||||
stats.equipment.main = item;
|
||||
}
|
||||
}
|
||||
Some(comp::Item::Consumable { effect, .. }) => {
|
||||
state.apply_effect(entity, effect);
|
||||
}
|
||||
Some(item) => {
|
||||
// Re-insert it if unused
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(x, item));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
@ -1003,7 +1019,7 @@ impl Server {
|
||||
.get(entity)
|
||||
.is_some()
|
||||
{
|
||||
modified_blocks.push((pos, Block::empty()));
|
||||
state.set_block(pos, Block::empty());
|
||||
}
|
||||
}
|
||||
ClientMsg::PlaceBlock(pos, block) => {
|
||||
@ -1013,7 +1029,25 @@ impl Server {
|
||||
.get(entity)
|
||||
.is_some()
|
||||
{
|
||||
modified_blocks.push((pos, block));
|
||||
state.try_set_block(pos, block);
|
||||
}
|
||||
}
|
||||
ClientMsg::CollectBlock(pos) => {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
if let Some(block) = block {
|
||||
if block.is_collectible()
|
||||
&& state
|
||||
.ecs()
|
||||
.read_storage::<comp::Inventory>()
|
||||
.get(entity)
|
||||
.map(|inv| !inv.is_full())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if state.try_set_block(pos, Block::empty()).is_some() {
|
||||
comp::Item::try_reclaim_from_block(block)
|
||||
.map(|item| state.give_item(entity, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
||||
@ -1117,10 +1151,6 @@ impl Server {
|
||||
self.generate_chunk(entity, key);
|
||||
}
|
||||
|
||||
for (pos, block) in modified_blocks {
|
||||
self.state.set_block(pos, block);
|
||||
}
|
||||
|
||||
for (pos, ori, item) in dropped_items {
|
||||
let vel = ori.0.normalized() * 5.0
|
||||
+ Vec3::unit_z() * 10.0
|
||||
@ -1384,3 +1414,40 @@ impl Drop for Server {
|
||||
self.clients.notify_registered(ServerMsg::Shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
trait StateExt {
|
||||
fn give_item(&mut self, entity: EcsEntity, item: comp::Item) -> bool;
|
||||
fn apply_effect(&mut self, entity: EcsEntity, effect: Effect);
|
||||
}
|
||||
|
||||
impl StateExt for State {
|
||||
fn give_item(&mut self, entity: EcsEntity, item: comp::Item) -> bool {
|
||||
let success = self
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.push(item).is_none())
|
||||
.unwrap_or(false);
|
||||
if success {
|
||||
self.write_component(entity, comp::InventoryUpdate);
|
||||
}
|
||||
success
|
||||
}
|
||||
|
||||
fn apply_effect(&mut self, entity: EcsEntity, effect: Effect) {
|
||||
match effect {
|
||||
Effect::Health(hp, source) => {
|
||||
self.ecs_mut()
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.health.change_by(hp, source));
|
||||
}
|
||||
Effect::Xp(xp) => {
|
||||
self.ecs_mut()
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.exp.change_by(xp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
use super::{img_ids::Imgs, Event as HudEvent, Fonts, TEXT_COLOR};
|
||||
use super::{
|
||||
img_ids::{Imgs, ImgsRot},
|
||||
Event as HudEvent, Fonts, TEXT_COLOR,
|
||||
};
|
||||
use crate::ui::{ImageFrame, Tooltip, TooltipManager, Tooltipable};
|
||||
use client::Client;
|
||||
use conrod_core::{
|
||||
color,
|
||||
position::Relative,
|
||||
widget::{self, Button, Image, Rectangle /*, Scrollbar*/},
|
||||
widget_ids, /*Color, Colorable,*/ Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
widget_ids, Color, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
@ -21,6 +25,7 @@ widget_ids! {
|
||||
map_title,
|
||||
inv_slots[],
|
||||
items[],
|
||||
tooltip[],
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,15 +36,25 @@ pub struct Bag<'a> {
|
||||
fonts: &'a Fonts,
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
rot_imgs: &'a ImgsRot,
|
||||
tooltip_manager: &'a mut TooltipManager,
|
||||
}
|
||||
|
||||
impl<'a> Bag<'a> {
|
||||
pub fn new(client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
pub fn new(
|
||||
client: &'a Client,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
rot_imgs: &'a ImgsRot,
|
||||
tooltip_manager: &'a mut TooltipManager,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
rot_imgs,
|
||||
tooltip_manager,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,6 +97,23 @@ impl<'a> Widget for Bag<'a> {
|
||||
Some(inv) => inv,
|
||||
None => return None,
|
||||
};
|
||||
// Tooltips
|
||||
let item_tooltip = Tooltip::new({
|
||||
// Edge images [t, b, r, l]
|
||||
// Corner images [tr, tl, br, bl]
|
||||
let edge = &self.rot_imgs.tt_side;
|
||||
let corner = &self.rot_imgs.tt_corner;
|
||||
ImageFrame::new(
|
||||
[edge.cw180, edge.none, edge.cw270, edge.cw90],
|
||||
[corner.none, corner.cw270, corner.cw90, corner.cw180],
|
||||
Color::Rgba(0.08, 0.07, 0.04, 1.0),
|
||||
5.0,
|
||||
)
|
||||
})
|
||||
.title_font_size(15)
|
||||
.desc_font_size(10)
|
||||
.title_text_color(TEXT_COLOR)
|
||||
.desc_text_color(TEXT_COLOR);
|
||||
|
||||
// Bag parts
|
||||
Image::new(self.imgs.bag_bot)
|
||||
@ -133,7 +165,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
let is_selected = Some(i) == state.selected_slot;
|
||||
|
||||
// Slot
|
||||
if Button::image(self.imgs.inv_slot)
|
||||
let slot_widget = Button::image(self.imgs.inv_slot)
|
||||
.top_left_with_margins_on(
|
||||
state.ids.inv_alignment,
|
||||
4.0 + y as f64 * (40.0 + 4.0),
|
||||
@ -146,10 +178,23 @@ impl<'a> Widget for Bag<'a> {
|
||||
} else {
|
||||
color::DARK_YELLOW
|
||||
})
|
||||
.floating(true)
|
||||
.set(state.ids.inv_slots[i], ui)
|
||||
.was_clicked()
|
||||
{
|
||||
.floating(true);
|
||||
|
||||
let slot_widget = if let Some(item) = item {
|
||||
slot_widget
|
||||
.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
&item.description(),
|
||||
&item.category(),
|
||||
&item_tooltip,
|
||||
)
|
||||
.set(state.ids.inv_slots[i], ui)
|
||||
} else {
|
||||
slot_widget.set(state.ids.inv_slots[i], ui)
|
||||
};
|
||||
|
||||
// Item
|
||||
if slot_widget.was_clicked() {
|
||||
let selected_slot = match state.selected_slot {
|
||||
Some(a) => {
|
||||
if a == i {
|
||||
@ -164,7 +209,6 @@ impl<'a> Widget for Bag<'a> {
|
||||
};
|
||||
state.update(|s| s.selected_slot = selected_slot);
|
||||
}
|
||||
|
||||
// Item
|
||||
if item.is_some() {
|
||||
Button::image(self.imgs.potion_red) // TODO: Insert variable image depending on the item displayed in that slot
|
||||
|
@ -1,5 +1,16 @@
|
||||
use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic};
|
||||
|
||||
// TODO: Combine with image_ids, see macro definition
|
||||
rotation_image_ids! {
|
||||
pub struct ImgsRot {
|
||||
<VoxelGraphic>
|
||||
|
||||
// Tooltip Test
|
||||
tt_side: "voxygen/element/frames/tt_test_edge",
|
||||
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
|
||||
}
|
||||
}
|
||||
|
||||
image_ids! {
|
||||
pub struct Imgs {
|
||||
<VoxelGraphic>
|
||||
|
@ -12,6 +12,7 @@ mod skillbar;
|
||||
mod social;
|
||||
mod spell;
|
||||
|
||||
use crate::hud::img_ids::ImgsRot;
|
||||
pub use settings_window::ScaleChange;
|
||||
|
||||
use bag::Bag;
|
||||
@ -365,6 +366,7 @@ pub struct Hud {
|
||||
ids: Ids,
|
||||
imgs: Imgs,
|
||||
fonts: Fonts,
|
||||
rot_imgs: ImgsRot,
|
||||
new_messages: VecDeque<ClientEvent>,
|
||||
inventory_space: usize,
|
||||
show: Show,
|
||||
@ -385,12 +387,15 @@ impl Hud {
|
||||
let ids = Ids::new(ui.id_generator());
|
||||
// Load images.
|
||||
let imgs = Imgs::load(&mut ui).expect("Failed to load images!");
|
||||
// Load rotation images.
|
||||
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load rot images!");
|
||||
// Load fonts.
|
||||
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts!");
|
||||
|
||||
Self {
|
||||
ui,
|
||||
imgs,
|
||||
rot_imgs,
|
||||
fonts,
|
||||
ids,
|
||||
new_messages: VecDeque::new(),
|
||||
@ -428,7 +433,7 @@ impl Hud {
|
||||
debug_info: DebugInfo,
|
||||
) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
let ref mut ui_widgets = self.ui.set_widgets().0;
|
||||
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
|
||||
|
||||
let version = format!("{}-{}", env!("CARGO_PKG_VERSION"), common::util::GIT_HASH);
|
||||
|
||||
@ -723,7 +728,15 @@ impl Hud {
|
||||
|
||||
// Bag contents
|
||||
if self.show.bag {
|
||||
match Bag::new(client, &self.imgs, &self.fonts).set(self.ids.bag, ui_widgets) {
|
||||
match Bag::new(
|
||||
client,
|
||||
&self.imgs,
|
||||
&self.fonts,
|
||||
&self.rot_imgs,
|
||||
tooltip_manager,
|
||||
)
|
||||
.set(self.ids.bag, ui_widgets)
|
||||
{
|
||||
Some(bag::Event::HudEvent(event)) => events.push(event),
|
||||
Some(bag::Event::Close) => {
|
||||
self.show.bag(false);
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
render::{
|
||||
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Model,
|
||||
PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
|
||||
PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
|
||||
},
|
||||
scene::{
|
||||
camera::{Camera, CameraMode},
|
||||
@ -36,6 +36,7 @@ struct PostProcess {
|
||||
pub struct Scene {
|
||||
globals: Consts<Globals>,
|
||||
lights: Consts<Light>,
|
||||
shadows: Consts<Shadow>,
|
||||
camera: Camera,
|
||||
|
||||
skybox: Skybox,
|
||||
@ -57,6 +58,7 @@ impl Scene {
|
||||
Self {
|
||||
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
|
||||
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
|
||||
shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(),
|
||||
camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson),
|
||||
|
||||
skybox: Skybox {
|
||||
@ -120,7 +122,8 @@ impl Scene {
|
||||
.set_orientation(Vec3::new(client.state().get_time() as f32 * 0.0, 0.0, 0.0));
|
||||
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||
const CHAR_SELECT_TIME_OF_DAY: f32 = 80000.0; // 12*3600 seconds
|
||||
const VD: f32 = 115.0; //View Distance
|
||||
const TIME: f64 = 36000.0; // hours*3600 seconds
|
||||
if let Err(err) = renderer.update_consts(
|
||||
&mut self.globals,
|
||||
&[Globals::new(
|
||||
@ -128,12 +131,14 @@ impl Scene {
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
self.camera.get_focus_pos(),
|
||||
CHAR_SELECT_TIME_OF_DAY,
|
||||
55800.0,
|
||||
VD,
|
||||
TIME,
|
||||
client.state().get_time(),
|
||||
renderer.get_resolution(),
|
||||
0,
|
||||
0,
|
||||
BlockKind::Air,
|
||||
None,
|
||||
)],
|
||||
) {
|
||||
error!("Renderer failed to update: {:?}", err);
|
||||
@ -192,6 +197,7 @@ impl Scene {
|
||||
self.figure_state.locals(),
|
||||
self.figure_state.bone_consts(),
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
);
|
||||
|
||||
renderer.render_figure(
|
||||
@ -200,6 +206,7 @@ impl Scene {
|
||||
self.backdrop_state.locals(),
|
||||
self.backdrop_state.bone_consts(),
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
);
|
||||
|
||||
renderer.render_post_process(
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
use common::{
|
||||
figure::Segment,
|
||||
util::{linear_to_srgb, srgb_to_linear},
|
||||
vol::{IntoFullVolIterator, Vox},
|
||||
vol::{IntoFullVolIterator, ReadVol, Vox},
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
@ -32,17 +32,35 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
|
||||
self,
|
||||
pos,
|
||||
offs + pos.map(|e| e as f32),
|
||||
col,
|
||||
&[[[Some(col); 3]; 3]; 3],
|
||||
|origin, norm, col, ao, light| {
|
||||
FigureVertex::new(
|
||||
origin,
|
||||
norm,
|
||||
linear_to_srgb(srgb_to_linear(col) * ao * light),
|
||||
linear_to_srgb(srgb_to_linear(col) * light.min(ao)),
|
||||
0,
|
||||
)
|
||||
},
|
||||
true,
|
||||
&[[[1.0; 3]; 3]; 3],
|
||||
&{
|
||||
let mut ls = [[[0.0; 3]; 3]; 3];
|
||||
for x in 0..3 {
|
||||
for y in 0..3 {
|
||||
for z in 0..3 {
|
||||
ls[z][y][x] = if self
|
||||
.get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1)
|
||||
.map(|v| v.is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
ls
|
||||
},
|
||||
|vox| vox.is_empty(),
|
||||
|vox| !vox.is_empty(),
|
||||
);
|
||||
@ -73,7 +91,7 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
|
||||
self,
|
||||
pos,
|
||||
offs + pos.map(|e| e as f32),
|
||||
col,
|
||||
&[[[Some(col); 3]; 3]; 3],
|
||||
|origin, norm, col, ao, light| {
|
||||
SpriteVertex::new(
|
||||
origin,
|
||||
|
@ -3,24 +3,124 @@ use crate::{
|
||||
render::{self, FluidPipeline, Mesh, TerrainPipeline},
|
||||
};
|
||||
use common::{
|
||||
terrain::{Block, BlockKind},
|
||||
vol::{ReadVol, RectRasterableVol},
|
||||
terrain::Block,
|
||||
vol::{ReadVol, RectRasterableVol, Vox},
|
||||
volumes::vol_grid_2d::VolGrid2d,
|
||||
};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
use vek::*;
|
||||
|
||||
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
|
||||
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
|
||||
|
||||
fn block_shadow_density(kind: BlockKind) -> (f32, f32) {
|
||||
// (density, cap)
|
||||
match kind {
|
||||
BlockKind::Normal => (0.085, 0.3),
|
||||
BlockKind::Dense => (0.3, 0.0),
|
||||
BlockKind::Water => (0.15, 0.0),
|
||||
kind if kind.is_air() => (0.0, 0.0),
|
||||
_ => (1.0, 0.0),
|
||||
const DIRS: [Vec2<i32>; 4] = [
|
||||
Vec2 { x: 1, y: 0 },
|
||||
Vec2 { x: 0, y: 1 },
|
||||
Vec2 { x: -1, y: 0 },
|
||||
Vec2 { x: 0, y: -1 },
|
||||
];
|
||||
|
||||
const DIRS_3D: [Vec3<i32>; 6] = [
|
||||
Vec3 { x: 1, y: 0, z: 0 },
|
||||
Vec3 { x: 0, y: 1, z: 0 },
|
||||
Vec3 { x: 0, y: 0, z: 1 },
|
||||
Vec3 { x: -1, y: 0, z: 0 },
|
||||
Vec3 { x: 0, y: -1, z: 0 },
|
||||
Vec3 { x: 0, y: 0, z: -1 },
|
||||
];
|
||||
|
||||
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
bounds: Aabb<i32>,
|
||||
vol: &VolGrid2d<V>,
|
||||
) -> impl Fn(Vec3<i32>) -> f32 {
|
||||
let sunlight = 24;
|
||||
|
||||
let outer = Aabb {
|
||||
min: bounds.min - sunlight,
|
||||
max: bounds.max + sunlight,
|
||||
};
|
||||
|
||||
let mut voids = HashMap::new();
|
||||
let mut rays = vec![outer.size().d; outer.size().product() as usize];
|
||||
for x in 0..outer.size().w {
|
||||
for y in 0..outer.size().h {
|
||||
let mut outside = true;
|
||||
for z in (0..outer.size().d).rev() {
|
||||
let block = vol
|
||||
.get(outer.min + Vec3::new(x, y, z))
|
||||
.ok()
|
||||
.copied()
|
||||
.unwrap_or(Block::empty());
|
||||
|
||||
if !block.is_air() && outside {
|
||||
rays[(outer.size().w * y + x) as usize] = z;
|
||||
outside = false;
|
||||
}
|
||||
|
||||
if (block.is_air() || block.is_fluid()) && !outside {
|
||||
voids.insert(Vec3::new(x, y, z), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut opens = HashSet::new();
|
||||
'voids: for (pos, l) in &mut voids {
|
||||
for dir in &DIRS {
|
||||
let col = Vec2::<i32>::from(*pos) + dir;
|
||||
if pos.z
|
||||
> *rays
|
||||
.get(((outer.size().w * col.y) + col.x) as usize)
|
||||
.unwrap_or(&0)
|
||||
{
|
||||
*l = Some(sunlight - 1);
|
||||
opens.insert(*pos);
|
||||
continue 'voids;
|
||||
}
|
||||
}
|
||||
|
||||
if pos.z
|
||||
>= *rays
|
||||
.get(((outer.size().w * pos.y) + pos.x) as usize)
|
||||
.unwrap_or(&0)
|
||||
{
|
||||
*l = Some(sunlight - 1);
|
||||
opens.insert(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
while opens.len() > 0 {
|
||||
let mut new_opens = HashSet::new();
|
||||
for open in &opens {
|
||||
let parent_l = voids[open].unwrap_or(0);
|
||||
for dir in &DIRS_3D {
|
||||
let other = *open + *dir;
|
||||
if !opens.contains(&other) {
|
||||
if let Some(l) = voids.get_mut(&other) {
|
||||
if l.unwrap_or(0) < parent_l - 1 {
|
||||
new_opens.insert(other);
|
||||
}
|
||||
*l = Some(parent_l - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
opens = new_opens;
|
||||
}
|
||||
|
||||
move |wpos| {
|
||||
let pos = wpos - outer.min;
|
||||
rays.get(((outer.size().w * pos.y) + pos.x) as usize)
|
||||
.and_then(|ray| if pos.z > *ray { Some(1.0) } else { None })
|
||||
.or_else(|| {
|
||||
if let Some(Some(l)) = voids.get(&pos) {
|
||||
Some(*l as f32 / sunlight as f32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,85 +138,98 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
let mut fluid_mesh = Mesh::new();
|
||||
|
||||
let light = calc_light(range, self);
|
||||
|
||||
for x in range.min.x + 1..range.max.x - 1 {
|
||||
for y in range.min.y + 1..range.max.y - 1 {
|
||||
let mut neighbour_light = [[[1.0f32; 3]; 3]; 3];
|
||||
let mut lights = [[[0.0; 3]; 3]; 3];
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
lights[k][j][i] = light(
|
||||
Vec3::new(x, y, range.min.z)
|
||||
+ Vec3::new(i as i32, j as i32, k as i32)
|
||||
- 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for z in (range.min.z..range.max.z).rev() {
|
||||
let get_color = |pos| {
|
||||
self.get(pos)
|
||||
.ok()
|
||||
.filter(|vox| vox.is_opaque())
|
||||
.and_then(|vox| vox.get_color())
|
||||
.map(|col| col.map(|e| e as f32 / 255.0))
|
||||
};
|
||||
|
||||
let mut colors = [[[None; 3]; 3]; 3];
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
colors[k][j][i] = get_color(
|
||||
Vec3::new(x, y, range.min.z)
|
||||
+ Vec3::new(i as i32, j as i32, k as i32)
|
||||
- 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for z in range.min.z..range.max.z {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
let offs = (pos - (range.min + 1) * Vec3::new(1, 1, 0)).map(|e| e as f32);
|
||||
|
||||
lights[0] = lights[1];
|
||||
lights[1] = lights[2];
|
||||
colors[0] = colors[1];
|
||||
colors[1] = colors[2];
|
||||
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
lights[2][j][i] = light(pos + Vec3::new(i as i32, j as i32, 2) - 1);
|
||||
}
|
||||
}
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
colors[2][j][i] = get_color(pos + Vec3::new(i as i32, j as i32, 2) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let block = self.get(pos).ok();
|
||||
|
||||
// Create mesh polygons
|
||||
if let Some(col) = block
|
||||
.filter(|vox| vox.is_opaque())
|
||||
.and_then(|vox| vox.get_color())
|
||||
{
|
||||
let col = col.map(|e| e as f32 / 255.0);
|
||||
|
||||
if block.map(|vox| vox.is_opaque()).unwrap_or(false) {
|
||||
vol::push_vox_verts(
|
||||
&mut opaque_mesh,
|
||||
self,
|
||||
pos,
|
||||
offs,
|
||||
col,
|
||||
&colors,
|
||||
|pos, norm, col, ao, light| {
|
||||
TerrainVertex::new(pos, norm, col, light * ao)
|
||||
TerrainVertex::new(pos, norm, col, light.min(ao))
|
||||
},
|
||||
false,
|
||||
&neighbour_light,
|
||||
&lights,
|
||||
|vox| !vox.is_opaque(),
|
||||
|vox| vox.is_opaque(),
|
||||
);
|
||||
} else if let Some(col) = block
|
||||
.filter(|vox| vox.is_fluid())
|
||||
.and_then(|vox| vox.get_color())
|
||||
{
|
||||
let col = col.map(|e| e as f32 / 255.0);
|
||||
|
||||
} else if block.map(|vox| vox.is_fluid()).unwrap_or(false) {
|
||||
vol::push_vox_verts(
|
||||
&mut fluid_mesh,
|
||||
self,
|
||||
pos,
|
||||
offs,
|
||||
col,
|
||||
&colors,
|
||||
|pos, norm, col, ao, light| {
|
||||
FluidVertex::new(pos, norm, col, light * ao, 0.3)
|
||||
FluidVertex::new(pos, norm, col, light.min(ao), 0.3)
|
||||
},
|
||||
false,
|
||||
&neighbour_light,
|
||||
&lights,
|
||||
|vox| vox.is_air(),
|
||||
|vox| vox.is_opaque(),
|
||||
);
|
||||
}
|
||||
|
||||
// Shift lighting
|
||||
neighbour_light[2] = neighbour_light[1];
|
||||
neighbour_light[1] = neighbour_light[0];
|
||||
|
||||
// Accumulate shade under opaque blocks
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
let (density, cap) = self
|
||||
.get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, -1))
|
||||
.ok()
|
||||
.map(|vox| block_shadow_density(vox.kind()))
|
||||
.unwrap_or((0.0, 0.0));
|
||||
|
||||
neighbour_light[0][i][j] = (neighbour_light[0][i][j] * (1.0 - density))
|
||||
.max(cap.min(neighbour_light[1][i][j]));
|
||||
}
|
||||
}
|
||||
|
||||
// Spread light
|
||||
neighbour_light[0] = [[neighbour_light[0]
|
||||
.iter()
|
||||
.map(|col| col.iter())
|
||||
.flatten()
|
||||
.copied()
|
||||
.fold(0.0, |a, x| a + x)
|
||||
/ 9.0; 3]; 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,15 @@ fn get_ao_quad<V: ReadVol>(
|
||||
.unwrap_or(false),
|
||||
);
|
||||
|
||||
let darkness = darknesses
|
||||
.iter()
|
||||
.map(|x| x.iter().map(|y| y.iter()))
|
||||
.flatten()
|
||||
.flatten()
|
||||
.fold(0.0, |a: f32, x| a.max(*x));
|
||||
let mut darkness = 0.0;
|
||||
for x in 0..2 {
|
||||
for y in 0..2 {
|
||||
let dark_pos = shift + offs[0] * x + offs[1] * y + 1;
|
||||
darkness += darknesses[dark_pos.z as usize][dark_pos.y as usize]
|
||||
[dark_pos.x as usize]
|
||||
/ 4.0;
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
darkness,
|
||||
@ -57,34 +60,85 @@ fn get_ao_quad<V: ReadVol>(
|
||||
.collect::<Vec4<(f32, f32)>>()
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn get_col_quad<V: ReadVol>(
|
||||
_vol: &V,
|
||||
_pos: Vec3<i32>,
|
||||
_shift: Vec3<i32>,
|
||||
dirs: &[Vec3<i32>],
|
||||
cols: &[[[Option<Rgb<f32>>; 3]; 3]; 3],
|
||||
_is_opaque: impl Fn(&V::Vox) -> bool,
|
||||
) -> Vec4<Rgb<f32>> {
|
||||
dirs.windows(2)
|
||||
.map(|offs| {
|
||||
let primary_col = cols[1][1][1].unwrap_or(Rgb::zero());
|
||||
let mut color = Rgb::zero();
|
||||
let mut total = 0.0;
|
||||
for x in 0..2 {
|
||||
for y in 0..2 {
|
||||
let col_pos = offs[0] * x + offs[1] * y + 1;
|
||||
if let Some(col) = unsafe {
|
||||
cols.get_unchecked(col_pos.z as usize)
|
||||
.get_unchecked(col_pos.y as usize)
|
||||
.get_unchecked(col_pos.x as usize)
|
||||
} {
|
||||
if Vec3::<f32>::from(primary_col).distance_squared(Vec3::from(*col))
|
||||
< 0.25 * 0.25
|
||||
{
|
||||
color += *col;
|
||||
total += 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color / total
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Utility function
|
||||
fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex>(
|
||||
origin: Vec3<f32>,
|
||||
unit_x: Vec3<f32>,
|
||||
unit_y: Vec3<f32>,
|
||||
norm: Vec3<f32>,
|
||||
col: Rgb<f32>,
|
||||
cols: Vec4<Rgb<f32>>,
|
||||
darkness_ao: Vec4<(f32, f32)>,
|
||||
vcons: &F,
|
||||
) -> Quad<P> {
|
||||
let darkness = darkness_ao.map(|e| e.0);
|
||||
let ao = darkness_ao.map(|e| e.1);
|
||||
|
||||
let ao_map = ao.map(|e| 0.05 + e.powf(1.6) * 0.95);
|
||||
let ao_map = ao.map(|e| e); //0.05 + e.powf(1.2) * 0.95);
|
||||
|
||||
if ao[0].min(ao[2]) < ao[1].min(ao[3]) {
|
||||
if ao[0].min(ao[2]).min(darkness[0]).min(darkness[2])
|
||||
< ao[1].min(ao[3]).min(darkness[1]).min(darkness[3])
|
||||
{
|
||||
Quad::new(
|
||||
vcons(origin + unit_y, norm, col, darkness[3], ao_map[3]),
|
||||
vcons(origin, norm, col, darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, col, darkness[1], ao_map[1]),
|
||||
vcons(origin + unit_x + unit_y, norm, col, darkness[2], ao_map[2]),
|
||||
vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3]),
|
||||
vcons(origin, norm, cols[0], darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]),
|
||||
vcons(
|
||||
origin + unit_x + unit_y,
|
||||
norm,
|
||||
cols[2],
|
||||
darkness[2],
|
||||
ao_map[2],
|
||||
),
|
||||
)
|
||||
} else {
|
||||
Quad::new(
|
||||
vcons(origin, norm, col, darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, col, darkness[1], ao_map[1]),
|
||||
vcons(origin + unit_x + unit_y, norm, col, darkness[2], ao_map[2]),
|
||||
vcons(origin + unit_y, norm, col, darkness[3], ao_map[3]),
|
||||
vcons(origin, norm, cols[0], darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]),
|
||||
vcons(
|
||||
origin + unit_x + unit_y,
|
||||
norm,
|
||||
cols[2],
|
||||
darkness[2],
|
||||
ao_map[2],
|
||||
),
|
||||
vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3]),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -94,7 +148,7 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
vol: &V,
|
||||
pos: Vec3<i32>,
|
||||
offs: Vec3<f32>,
|
||||
col: Rgb<f32>,
|
||||
cols: &[[[Option<Rgb<f32>>; 3]; 3]; 3],
|
||||
vcons: impl Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex,
|
||||
error_makes_face: bool,
|
||||
darknesses: &[[[f32; 3]; 3]; 3],
|
||||
@ -114,7 +168,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_z(),
|
||||
Vec3::unit_y(),
|
||||
-Vec3::unit_x(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_x(),
|
||||
&[-z, -y, z, y, -z],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
@ -137,7 +198,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_y(),
|
||||
Vec3::unit_z(),
|
||||
Vec3::unit_x(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_x(),
|
||||
&[-y, -z, y, z, -y],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
@ -160,7 +228,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_x(),
|
||||
Vec3::unit_z(),
|
||||
-Vec3::unit_y(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_y(),
|
||||
&[-x, -z, x, z, -x],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
@ -183,7 +258,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_z(),
|
||||
Vec3::unit_x(),
|
||||
Vec3::unit_y(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_y(),
|
||||
&[-z, -x, z, x, -z],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
@ -206,7 +288,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_y(),
|
||||
Vec3::unit_x(),
|
||||
-Vec3::unit_z(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_z(),
|
||||
&[-y, -x, y, x, -y],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
@ -229,7 +318,14 @@ pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
|
||||
Vec3::unit_x(),
|
||||
Vec3::unit_y(),
|
||||
Vec3::unit_z(),
|
||||
col,
|
||||
get_col_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_z(),
|
||||
&[-x, -y, x, y, -x],
|
||||
cols,
|
||||
&is_opaque,
|
||||
),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
|
@ -26,7 +26,7 @@ pub use self::{
|
||||
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,
|
||||
Mode as UiMode, UiPipeline,
|
||||
},
|
||||
Globals, Light,
|
||||
Globals, Light, Shadow,
|
||||
},
|
||||
renderer::{Renderer, TgtColorFmt, TgtDepthFmt, WinColorFmt, WinDepthFmt},
|
||||
texture::Texture,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
|
||||
Globals, Light,
|
||||
Globals, Light, Shadow,
|
||||
};
|
||||
use gfx::{
|
||||
self,
|
||||
@ -38,6 +38,7 @@ gfx_defines! {
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
bones: gfx::ConstantBuffer<BoneData> = "u_bones",
|
||||
lights: gfx::ConstantBuffer<Light> = "u_lights",
|
||||
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
|
||||
|
||||
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthFmt},
|
||||
Globals, Light,
|
||||
Globals, Light, Shadow,
|
||||
};
|
||||
use gfx::{
|
||||
self,
|
||||
@ -27,6 +27,7 @@ gfx_defines! {
|
||||
locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
lights: gfx::ConstantBuffer<Light> = "u_lights",
|
||||
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
|
||||
|
||||
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
|
||||
|
@ -28,14 +28,19 @@ gfx_defines! {
|
||||
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64.
|
||||
tick: [f32; 4] = "tick",
|
||||
screen_res: [f32; 4] = "screen_res",
|
||||
light_count: [u32; 4] = "light_count",
|
||||
light_shadow_count: [u32; 4] = "light_shadow_count",
|
||||
medium: [u32; 4] = "medium",
|
||||
select_pos: [i32; 4] = "select_pos",
|
||||
}
|
||||
|
||||
constant Light {
|
||||
pos: [f32; 4] = "light_pos",
|
||||
col: [f32; 4] = "light_col",
|
||||
}
|
||||
|
||||
constant Shadow {
|
||||
pos_radius: [f32; 4] = "shadow_pos_radius",
|
||||
}
|
||||
}
|
||||
|
||||
impl Globals {
|
||||
@ -50,7 +55,9 @@ impl Globals {
|
||||
tick: f64,
|
||||
screen_res: Vec2<u16>,
|
||||
light_count: usize,
|
||||
shadow_count: usize,
|
||||
medium: BlockKind,
|
||||
select_pos: Option<Vec3<i32>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
view_mat: arr_to_mat(view_mat.into_col_array()),
|
||||
@ -61,8 +68,12 @@ impl Globals {
|
||||
time_of_day: [time_of_day as f32; 4],
|
||||
tick: [tick as f32; 4],
|
||||
screen_res: Vec4::from(screen_res.map(|e| e as f32)).into_array(),
|
||||
light_count: [light_count as u32; 4],
|
||||
light_shadow_count: [light_count as u32, shadow_count as u32, 0, 0],
|
||||
medium: [if medium.is_fluid() { 1 } else { 0 }; 4],
|
||||
select_pos: select_pos
|
||||
.map(|sp| Vec4::from(sp) + Vec4::unit_w())
|
||||
.unwrap_or(Vec4::zero())
|
||||
.into_array(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +90,9 @@ impl Default for Globals {
|
||||
0.0,
|
||||
Vec2::new(800, 500),
|
||||
0,
|
||||
0,
|
||||
BlockKind::Air,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -91,6 +104,10 @@ impl Light {
|
||||
col: Rgba::new(col.r, col.g, col.b, strength).into_array(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> Vec3<f32> {
|
||||
Vec3::new(self.pos[0], self.pos[1], self.pos[2])
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Light {
|
||||
@ -98,3 +115,21 @@ impl Default for Light {
|
||||
Self::new(Vec3::zero(), Rgb::zero(), 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shadow {
|
||||
pub fn new(pos: Vec3<f32>, radius: f32) -> Self {
|
||||
Self {
|
||||
pos_radius: [pos.x, pos.y, pos.z, radius],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> Vec3<f32> {
|
||||
Vec3::new(self.pos_radius[0], self.pos_radius[1], self.pos_radius[2])
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Shadow {
|
||||
fn default() -> Self {
|
||||
Self::new(Vec3::zero(), 0.0)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
|
||||
Globals, Light,
|
||||
Globals, Light, Shadow,
|
||||
};
|
||||
use gfx::{
|
||||
self,
|
||||
@ -36,6 +36,7 @@ gfx_defines! {
|
||||
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
lights: gfx::ConstantBuffer<Light> = "u_lights",
|
||||
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
|
||||
|
||||
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
super::{Pipeline, TgtColorFmt, TgtDepthFmt},
|
||||
Globals, Light,
|
||||
Globals, Light, Shadow,
|
||||
};
|
||||
use gfx::{
|
||||
self,
|
||||
@ -23,6 +23,7 @@ gfx_defines! {
|
||||
|
||||
constant Locals {
|
||||
model_offs: [f32; 3] = "model_offs",
|
||||
load_time: f32 = "load_time",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
@ -31,6 +32,7 @@ gfx_defines! {
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
lights: gfx::ConstantBuffer<Light> = "u_lights",
|
||||
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
|
||||
|
||||
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
@ -66,6 +68,7 @@ impl Locals {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
model_offs: [0.0; 3],
|
||||
load_time: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use super::{
|
||||
instances::Instances,
|
||||
mesh::Mesh,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light},
|
||||
pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light, Shadow},
|
||||
texture::Texture,
|
||||
Pipeline, RenderError,
|
||||
};
|
||||
@ -392,6 +392,7 @@ impl Renderer {
|
||||
locals: &Consts<figure::Locals>,
|
||||
bones: &Consts<figure::BoneData>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
) {
|
||||
self.encoder.draw(
|
||||
&gfx::Slice {
|
||||
@ -408,6 +409,7 @@ impl Renderer {
|
||||
globals: globals.buf.clone(),
|
||||
bones: bones.buf.clone(),
|
||||
lights: lights.buf.clone(),
|
||||
shadows: shadows.buf.clone(),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth: self.tgt_depth_view.clone(),
|
||||
},
|
||||
@ -421,6 +423,7 @@ impl Renderer {
|
||||
globals: &Consts<Globals>,
|
||||
locals: &Consts<terrain::Locals>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
) {
|
||||
self.encoder.draw(
|
||||
&gfx::Slice {
|
||||
@ -436,6 +439,7 @@ impl Renderer {
|
||||
locals: locals.buf.clone(),
|
||||
globals: globals.buf.clone(),
|
||||
lights: lights.buf.clone(),
|
||||
shadows: shadows.buf.clone(),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth: self.tgt_depth_view.clone(),
|
||||
},
|
||||
@ -449,6 +453,7 @@ impl Renderer {
|
||||
globals: &Consts<Globals>,
|
||||
locals: &Consts<terrain::Locals>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
) {
|
||||
self.encoder.draw(
|
||||
&gfx::Slice {
|
||||
@ -464,6 +469,7 @@ impl Renderer {
|
||||
locals: locals.buf.clone(),
|
||||
globals: globals.buf.clone(),
|
||||
lights: lights.buf.clone(),
|
||||
shadows: shadows.buf.clone(),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth: self.tgt_depth_view.clone(),
|
||||
},
|
||||
@ -477,6 +483,7 @@ impl Renderer {
|
||||
globals: &Consts<Globals>,
|
||||
instances: &Instances<sprite::Instance>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
) {
|
||||
self.encoder.draw(
|
||||
&gfx::Slice {
|
||||
@ -492,6 +499,7 @@ impl Renderer {
|
||||
ibuf: instances.ibuf.clone(),
|
||||
globals: globals.buf.clone(),
|
||||
lights: lights.buf.clone(),
|
||||
shadows: shadows.buf.clone(),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth: self.tgt_depth_view.clone(),
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
self, character::CharacterSkeleton, object::ObjectSkeleton, quadruped::QuadrupedSkeleton,
|
||||
quadrupedmedium::QuadrupedMediumSkeleton, Animation, Skeleton,
|
||||
},
|
||||
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer},
|
||||
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
|
||||
scene::camera::{Camera, CameraMode},
|
||||
};
|
||||
use client::Client;
|
||||
@ -108,7 +108,7 @@ impl FigureMgr {
|
||||
.and_then(|stats| stats.health.last_change)
|
||||
.map(|(_, time, _)| {
|
||||
Rgba::broadcast(1.0)
|
||||
+ Rgba::new(0.0, -1.0, -1.0, 0.0)
|
||||
+ Rgba::new(2.0, 2.0, 2.0, 0.0)
|
||||
.map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32)
|
||||
})
|
||||
.unwrap_or(Rgba::broadcast(1.0));
|
||||
@ -408,6 +408,7 @@ impl FigureMgr {
|
||||
client: &mut Client,
|
||||
globals: &Consts<Globals>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
camera: &Camera,
|
||||
) {
|
||||
let tick = client.get_tick();
|
||||
@ -480,7 +481,7 @@ impl FigureMgr {
|
||||
)
|
||||
.0;
|
||||
|
||||
renderer.render_figure(model, globals, locals, bone_consts, lights);
|
||||
renderer.render_figure(model, globals, locals, bone_consts, lights, shadows);
|
||||
} else {
|
||||
debug!("Body has no saved figure");
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
audio::AudioFrontend,
|
||||
render::{
|
||||
create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals,
|
||||
PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
|
||||
PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
|
||||
},
|
||||
window::Event,
|
||||
};
|
||||
@ -30,7 +30,10 @@ use vek::*;
|
||||
const CURSOR_PAN_SCALE: f32 = 0.005;
|
||||
|
||||
const MAX_LIGHT_COUNT: usize = 32;
|
||||
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not be visible
|
||||
const MAX_SHADOW_COUNT: usize = 24;
|
||||
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin
|
||||
const SHADOW_DIST_RADIUS: f32 = 8.0;
|
||||
const SHADOW_MAX_DIST: f32 = 96.0; // The distance beyond which shadows may not be visible
|
||||
|
||||
struct Skybox {
|
||||
model: Model<SkyboxPipeline>,
|
||||
@ -45,12 +48,14 @@ struct PostProcess {
|
||||
pub struct Scene {
|
||||
globals: Consts<Globals>,
|
||||
lights: Consts<Light>,
|
||||
shadows: Consts<Shadow>,
|
||||
camera: Camera,
|
||||
|
||||
skybox: Skybox,
|
||||
postprocess: PostProcess,
|
||||
terrain: Terrain<TerrainChunk>,
|
||||
loaded_distance: f32,
|
||||
select_pos: Option<Vec3<i32>>,
|
||||
|
||||
figure_mgr: FigureMgr,
|
||||
sound_mgr: SoundMgr,
|
||||
@ -63,7 +68,12 @@ impl Scene {
|
||||
|
||||
Self {
|
||||
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
|
||||
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
|
||||
lights: renderer
|
||||
.create_consts(&[Light::default(); MAX_LIGHT_COUNT])
|
||||
.unwrap(),
|
||||
shadows: renderer
|
||||
.create_consts(&[Shadow::default(); MAX_SHADOW_COUNT])
|
||||
.unwrap(),
|
||||
camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson),
|
||||
|
||||
skybox: Skybox {
|
||||
@ -78,6 +88,8 @@ impl Scene {
|
||||
},
|
||||
terrain: Terrain::new(renderer),
|
||||
loaded_distance: 0.0,
|
||||
select_pos: None,
|
||||
|
||||
figure_mgr: FigureMgr::new(),
|
||||
sound_mgr: SoundMgr::new(),
|
||||
}
|
||||
@ -98,6 +110,11 @@ impl Scene {
|
||||
&mut self.camera
|
||||
}
|
||||
|
||||
/// Set the block position that the player is interacting with
|
||||
pub fn set_select_pos(&mut self, pos: Option<Vec3<i32>>) {
|
||||
self.select_pos = pos;
|
||||
}
|
||||
|
||||
/// Handle an incoming user input event (e.g.: cursor moved, key pressed, window closed).
|
||||
///
|
||||
/// If the event is handled, return true.
|
||||
@ -200,14 +217,33 @@ impl Scene {
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
lights.sort_by_key(|light| {
|
||||
Vec3::from(Vec4::from(light.pos)).distance_squared(player_pos) as i32
|
||||
});
|
||||
lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32);
|
||||
lights.truncate(MAX_LIGHT_COUNT);
|
||||
renderer
|
||||
.update_consts(&mut self.lights, &lights)
|
||||
.expect("Failed to update light constants");
|
||||
|
||||
// Update shadow constants
|
||||
let mut shadows = (
|
||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||
client.state().ecs().read_storage::<comp::Scale>().maybe(),
|
||||
&client.state().ecs().read_storage::<comp::Body>(),
|
||||
&client.state().ecs().read_storage::<comp::Stats>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, _, _, stats)| !stats.is_dead)
|
||||
.filter(|(pos, _, _, _)| {
|
||||
(pos.0.distance_squared(player_pos) as f32)
|
||||
< (self.loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0)
|
||||
})
|
||||
.map(|(pos, scale, _, _)| Shadow::new(pos.0, scale.map(|s| s.0).unwrap_or(1.0)))
|
||||
.collect::<Vec<_>>();
|
||||
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
|
||||
shadows.truncate(MAX_SHADOW_COUNT);
|
||||
renderer
|
||||
.update_consts(&mut self.shadows, &shadows)
|
||||
.expect("Failed to update light constants");
|
||||
|
||||
// Update global constants.
|
||||
renderer
|
||||
.update_consts(
|
||||
@ -222,12 +258,14 @@ impl Scene {
|
||||
client.state().get_time(),
|
||||
renderer.get_resolution(),
|
||||
lights.len(),
|
||||
shadows.len(),
|
||||
client
|
||||
.state()
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind())
|
||||
.unwrap_or(BlockKind::Air),
|
||||
self.select_pos,
|
||||
)],
|
||||
)
|
||||
.expect("Failed to update global constants");
|
||||
@ -258,12 +296,19 @@ impl Scene {
|
||||
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
|
||||
|
||||
// Render terrain and figures.
|
||||
self.figure_mgr
|
||||
.render(renderer, client, &self.globals, &self.lights, &self.camera);
|
||||
self.figure_mgr.render(
|
||||
renderer,
|
||||
client,
|
||||
&self.globals,
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
&self.camera,
|
||||
);
|
||||
self.terrain.render(
|
||||
renderer,
|
||||
&self.globals,
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
self.camera.get_focus_pos(),
|
||||
);
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
mesh::Meshable,
|
||||
render::{
|
||||
Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, SpriteInstance,
|
||||
SpritePipeline, TerrainLocals, TerrainPipeline,
|
||||
Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow,
|
||||
SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline,
|
||||
},
|
||||
};
|
||||
|
||||
@ -23,6 +23,7 @@ use vek::*;
|
||||
|
||||
struct TerrainChunk {
|
||||
// GPU data
|
||||
load_time: f32,
|
||||
opaque_model: Model<TerrainPipeline>,
|
||||
fluid_model: Model<FluidPipeline>,
|
||||
sprite_instances: HashMap<(BlockKind, usize), Instances<SpriteInstance>>,
|
||||
@ -130,6 +131,10 @@ fn sprite_config_for(kind: BlockKind) -> Option<SpriteConfig> {
|
||||
variations: 2,
|
||||
wind_sway: 0.05,
|
||||
}),
|
||||
BlockKind::Velorite => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -595,6 +600,13 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
Vec3::new(-1.0, -0.5, -55.0),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Velorite, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.velorite.velorite",
|
||||
Vec3::new(-5.0, -5.0, -0.0),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
@ -613,6 +625,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
proj_mat: Mat4<f32>,
|
||||
) {
|
||||
let current_tick = client.get_tick();
|
||||
let current_time = client.state().get_time();
|
||||
|
||||
// Add any recently created or changed chunks to the list of chunks to be meshed.
|
||||
for (modified, pos) in client
|
||||
@ -700,6 +713,17 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Remesh all neighbours because we have complex lighting now
|
||||
/*self.mesh_todo.insert(
|
||||
neighbour_chunk_pos,
|
||||
ChunkMeshState {
|
||||
pos: chunk_pos + Vec2::new(x, y),
|
||||
started_tick: current_tick,
|
||||
active_worker: None,
|
||||
},
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -785,9 +809,15 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
// It's the mesh we want, insert the newly finished model into the terrain model
|
||||
// data structure (convert the mesh to a model first of course).
|
||||
Some(todo) if response.started_tick <= todo.started_tick => {
|
||||
let load_time = self
|
||||
.chunks
|
||||
.get(&response.pos)
|
||||
.map(|chunk| chunk.load_time)
|
||||
.unwrap_or(current_time as f32);
|
||||
self.chunks.insert(
|
||||
response.pos,
|
||||
TerrainChunk {
|
||||
load_time,
|
||||
opaque_model: renderer
|
||||
.create_model(&response.opaque_mesh)
|
||||
.expect("Failed to upload chunk mesh to the GPU!"),
|
||||
@ -814,6 +844,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
}),
|
||||
)
|
||||
.into_array(),
|
||||
load_time,
|
||||
}])
|
||||
.expect("Failed to upload chunk locals to the GPU!"),
|
||||
visible: false,
|
||||
@ -874,12 +905,19 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
renderer: &mut Renderer,
|
||||
globals: &Consts<Globals>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
focus_pos: Vec3<f32>,
|
||||
) {
|
||||
// Opaque
|
||||
for (_, chunk) in &self.chunks {
|
||||
if chunk.visible {
|
||||
renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights);
|
||||
renderer.render_terrain_chunk(
|
||||
&chunk.opaque_model,
|
||||
globals,
|
||||
&chunk.locals,
|
||||
lights,
|
||||
shadows,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -899,6 +937,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
globals,
|
||||
&instances,
|
||||
lights,
|
||||
shadows,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -908,7 +947,13 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
// Translucent
|
||||
for (_, chunk) in &self.chunks {
|
||||
if chunk.visible {
|
||||
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
|
||||
renderer.render_fluid_chunk(
|
||||
&chunk.fluid_model,
|
||||
globals,
|
||||
&chunk.locals,
|
||||
lights,
|
||||
shadows,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,27 @@ impl PlayState for SessionState {
|
||||
.compute_dependents(&self.client.borrow());
|
||||
let cam_dir: Vec3<f32> = Vec3::from(view_mat.inverted() * -Vec4::unit_z());
|
||||
|
||||
// Check to see whether we're aiming at anything
|
||||
let (build_pos, select_pos) = {
|
||||
let client = self.client.borrow();
|
||||
let terrain = client.state().terrain();
|
||||
let ray = terrain
|
||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||
.until(|block| block.is_tangible())
|
||||
.cast();
|
||||
let dist = ray.0;
|
||||
if let Ok(Some(_)) = ray.1 {
|
||||
// Hit something!
|
||||
(
|
||||
Some((cam_pos + cam_dir * (dist - 0.01)).map(|e| e.floor() as i32)),
|
||||
Some((cam_pos + cam_dir * dist).map(|e| e.floor() as i32)),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
};
|
||||
self.scene.set_select_pos(select_pos);
|
||||
|
||||
// Reset controller events
|
||||
self.controller.clear_events();
|
||||
|
||||
@ -142,16 +163,8 @@ impl PlayState for SessionState {
|
||||
.get(client.entity())
|
||||
.is_some()
|
||||
{
|
||||
let (d, b) = {
|
||||
let terrain = client.state().terrain();
|
||||
let ray = terrain.ray(cam_pos, cam_pos + cam_dir * 100.0).cast();
|
||||
(ray.0, if let Ok(Some(_)) = ray.1 { true } else { false })
|
||||
};
|
||||
|
||||
if b {
|
||||
let pos =
|
||||
(cam_pos + cam_dir * (d - 0.01)).map(|e| e.floor() as i32);
|
||||
client.place_block(pos, self.selected_block);
|
||||
if let Some(build_pos) = build_pos {
|
||||
client.place_block(build_pos, self.selected_block);
|
||||
}
|
||||
} else {
|
||||
self.controller.primary = state
|
||||
@ -159,7 +172,10 @@ impl PlayState for SessionState {
|
||||
}
|
||||
|
||||
Event::InputUpdate(GameInput::Secondary, state) => {
|
||||
self.controller.secondary = false; // To be changed later on
|
||||
|
||||
let mut client = self.client.borrow_mut();
|
||||
|
||||
if state
|
||||
&& client
|
||||
.state()
|
||||
@ -167,18 +183,21 @@ impl PlayState for SessionState {
|
||||
.get(client.entity())
|
||||
.is_some()
|
||||
{
|
||||
let (d, b) = {
|
||||
let terrain = client.state().terrain();
|
||||
let ray = terrain.ray(cam_pos, cam_pos + cam_dir * 100.0).cast();
|
||||
(ray.0, if let Ok(Some(_)) = ray.1 { true } else { false })
|
||||
};
|
||||
|
||||
if b {
|
||||
let pos = (cam_pos + cam_dir * d).map(|e| e.floor() as i32);
|
||||
client.remove_block(pos);
|
||||
if let Some(select_pos) = select_pos {
|
||||
client.remove_block(select_pos);
|
||||
}
|
||||
} else {
|
||||
} else if client
|
||||
.state()
|
||||
.read_storage::<comp::CharacterState>()
|
||||
.get(client.entity())
|
||||
.map(|cs| cs.action.is_wield())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.controller.secondary = state;
|
||||
} else {
|
||||
if let Some(select_pos) = select_pos {
|
||||
client.collect_block(select_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::InputUpdate(GameInput::Roll, state) => {
|
||||
@ -190,14 +209,10 @@ impl PlayState for SessionState {
|
||||
.is_some()
|
||||
{
|
||||
if state {
|
||||
if let Ok(Some(block)) = client
|
||||
.state()
|
||||
.terrain()
|
||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||
.cast()
|
||||
.1
|
||||
if let Some(block) = select_pos
|
||||
.and_then(|sp| client.state().terrain().get(sp).ok().copied())
|
||||
{
|
||||
self.selected_block = *block;
|
||||
self.selected_block = block;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -64,7 +64,7 @@ impl Default for ControlSettings {
|
||||
jump: KeyMouse::Key(VirtualKeyCode::Space),
|
||||
sit: KeyMouse::Key(VirtualKeyCode::K),
|
||||
glide: KeyMouse::Key(VirtualKeyCode::LShift),
|
||||
climb: KeyMouse::Key(VirtualKeyCode::Space),
|
||||
climb: KeyMouse::Key(VirtualKeyCode::LControl),
|
||||
climb_down: KeyMouse::Key(VirtualKeyCode::LShift),
|
||||
wall_leap: KeyMouse::Mouse(MouseButton::Middle),
|
||||
mount: KeyMouse::Key(VirtualKeyCode::F),
|
||||
@ -193,7 +193,7 @@ impl Default for AudioSettings {
|
||||
music_volume: 0.4,
|
||||
sfx_volume: 0.6,
|
||||
audio_device: None,
|
||||
audio_on: false,
|
||||
audio_on: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub use widgets::{
|
||||
image_slider::ImageSlider,
|
||||
ingame::{Ingame, IngameAnchor, Ingameable},
|
||||
toggle_button::ToggleButton,
|
||||
tooltip::{Tooltip, Tooltipable},
|
||||
tooltip::{Tooltip, TooltipManager, Tooltipable},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -49,7 +49,6 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use vek::*;
|
||||
use widgets::tooltip::TooltipManager;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UiError {
|
||||
|
@ -54,19 +54,23 @@ impl<'a> BlockGen<'a> {
|
||||
cache,
|
||||
Vec2::from(*cliff_pos),
|
||||
) {
|
||||
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
|
||||
Some(cliff_sample)
|
||||
if cliff_sample.is_cliffs
|
||||
&& cliff_sample.spawn_rate > 0.5
|
||||
&& cliff_sample.spawn_rules.cliffs =>
|
||||
{
|
||||
let cliff_pos3d = Vec3::from(*cliff_pos);
|
||||
|
||||
let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48;
|
||||
let height = (RandomField::new(seed + 1).get(cliff_pos3d) % 64) as f32
|
||||
/ (1.0 + 3.0 * cliff_sample.chaos)
|
||||
+ 3.0;
|
||||
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
||||
|
||||
max_height.max(
|
||||
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
|
||||
< (radius as f32 + tolerance).powf(2.0)
|
||||
{
|
||||
cliff_sample.alt
|
||||
+ height as f32 * (1.0 - cliff_sample.chaos)
|
||||
+ cliff_hill
|
||||
cliff_sample.alt + height * (1.0 - cliff_sample.chaos) + cliff_hill
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
@ -178,12 +182,13 @@ impl<'a> BlockGen<'a> {
|
||||
(true, alt, CONFIG.sea_level /*water_level*/)
|
||||
} else {
|
||||
// Apply warping
|
||||
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(48.0)
|
||||
+ (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(24.0);
|
||||
let warp = world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get(wposf.div(24.0))
|
||||
.mul((chaos - 0.1).max(0.0).powf(2.0))
|
||||
.mul(48.0);
|
||||
|
||||
let height = if (wposf.z as f32) < alt + warp - 10.0 {
|
||||
// Shortcut cliffs
|
||||
@ -310,6 +315,14 @@ impl<'a> BlockGen<'a> {
|
||||
},
|
||||
Rgb::broadcast(0),
|
||||
))
|
||||
} else if (wposf.z as f32) < height + 0.9
|
||||
&& chaos > 0.6
|
||||
&& (wposf.z as f32 > water_height + 3.0)
|
||||
&& marble > 0.75
|
||||
&& marble_small > 0.3
|
||||
&& (marble * 7323.07).fract() < 0.75
|
||||
{
|
||||
Some(Block::new(BlockKind::Velorite, Rgb::broadcast(0)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -379,10 +392,9 @@ impl<'a> BlockGen<'a> {
|
||||
let (st, st_sample) = st.as_ref()?;
|
||||
st.get(wpos, st_sample)
|
||||
})
|
||||
.or(block)
|
||||
.unwrap_or(Block::empty());
|
||||
.or(block);
|
||||
|
||||
Some(block)
|
||||
Some(block.unwrap_or(Block::empty()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,7 +425,8 @@ impl<'a> ZCache<'a> {
|
||||
|
||||
let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 };
|
||||
|
||||
let warp = self.sample.chaos * 24.0;
|
||||
let warp = self.sample.chaos * 32.0;
|
||||
|
||||
let (structure_min, structure_max) = self
|
||||
.structures
|
||||
.iter()
|
||||
@ -432,7 +445,7 @@ impl<'a> ZCache<'a> {
|
||||
}
|
||||
});
|
||||
|
||||
let ground_max = (self.sample.alt + 2.0 + warp + rocks).max(cliff);
|
||||
let ground_max = (self.sample.alt + warp + rocks).max(cliff) + 2.0;
|
||||
|
||||
let min = min + structure_min;
|
||||
let max = (ground_max + structure_max)
|
||||
|
@ -168,18 +168,18 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
+ (sim
|
||||
.gen_ctx
|
||||
.small_nz
|
||||
.get((wposf_turb.div(150.0)).into_array()) as f32)
|
||||
.get((wposf_turb.div(200.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.mul(chaos.max(0.025))
|
||||
.mul(64.0)
|
||||
.mul(chaos.max(0.05))
|
||||
.mul(55.0)
|
||||
+ (sim
|
||||
.gen_ctx
|
||||
.small_nz
|
||||
.get((wposf_turb.div(450.0)).into_array()) as f32)
|
||||
.get((wposf_turb.div(400.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.mul(1.0 - chaos)
|
||||
.mul((1.0 - chaos).max(0.3))
|
||||
.mul(1.0 - humidity)
|
||||
.mul(96.0);
|
||||
.mul(65.0);
|
||||
|
||||
let is_cliffs = sim_chunk.is_cliffs;
|
||||
let near_cliffs = sim_chunk.near_cliffs;
|
||||
@ -207,6 +207,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64);
|
||||
|
||||
let marble_small = (sim.gen_ctx.hill_nz.get((wposf3d.div(3.0)).into_array()) as f32)
|
||||
.powf(3.0)
|
||||
.add(1.0)
|
||||
.mul(0.5);
|
||||
let marble = (sim.gen_ctx.hill_nz.get((wposf3d.div(48.0)).into_array()) as f32)
|
||||
@ -225,13 +226,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
let wet_grass = Rgb::new(0.1, 0.8, 0.2);
|
||||
let cold_stone = Rgb::new(0.57, 0.67, 0.8);
|
||||
let warm_stone = Rgb::new(0.77, 0.77, 0.64);
|
||||
let beach_sand = Rgb::new(0.89, 0.87, 0.64);
|
||||
let desert_sand = Rgb::new(0.93, 0.80, 0.54);
|
||||
let beach_sand = Rgb::new(0.9, 0.82, 0.6);
|
||||
let desert_sand = Rgb::new(0.95, 0.75, 0.5);
|
||||
let snow = Rgb::new(0.8, 0.85, 1.0);
|
||||
|
||||
let dirt = Lerp::lerp(
|
||||
Rgb::new(0.078, 0.078, 0.20),
|
||||
Rgb::new(0.61, 0.49, 0.0),
|
||||
Rgb::new(0.075, 0.07, 0.3),
|
||||
Rgb::new(0.75, 0.55, 0.1),
|
||||
marble,
|
||||
);
|
||||
let tundra = Lerp::lerp(snow, Rgb::new(0.01, 0.3, 0.0), 0.4 + marble * 0.6);
|
||||
|
@ -10,11 +10,15 @@ use vek::*;
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SpawnRules {
|
||||
pub trees: bool,
|
||||
pub cliffs: bool,
|
||||
}
|
||||
|
||||
impl Default for SpawnRules {
|
||||
fn default() -> Self {
|
||||
Self { trees: true }
|
||||
Self {
|
||||
trees: true,
|
||||
cliffs: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +26,7 @@ impl SpawnRules {
|
||||
pub fn and(self, other: Self) -> Self {
|
||||
Self {
|
||||
trees: self.trees && other.trees,
|
||||
cliffs: self.cliffs && other.cliffs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
block::block_from_structure,
|
||||
column::{ColumnGen, ColumnSample},
|
||||
util::Sampler,
|
||||
CONFIG,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
@ -70,6 +71,7 @@ impl<'a> Sampler<'a> for TownGen {
|
||||
CellKind::Park => None,
|
||||
CellKind::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))),
|
||||
CellKind::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))),
|
||||
CellKind::Well => Some(Block::new(BlockKind::Normal, Rgb::broadcast(0))),
|
||||
CellKind::Road => {
|
||||
if (wpos.z as f32) < height - 1.0 {
|
||||
Some(Block::new(
|
||||
@ -101,8 +103,11 @@ impl<'a> Generator<'a, TownState> for TownGen {
|
||||
(sample.alt - 32.0, sample.alt + 75.0)
|
||||
}
|
||||
|
||||
fn spawn_rules(&self, _town: &'a TownState, _wpos: Vec2<i32>) -> SpawnRules {
|
||||
SpawnRules { trees: false }
|
||||
fn spawn_rules(&self, town: &'a TownState, wpos: Vec2<i32>) -> SpawnRules {
|
||||
SpawnRules {
|
||||
trees: wpos.distance_squared(town.center.into()) > (town.radius + 32).pow(2),
|
||||
cliffs: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,6 +127,12 @@ impl TownState {
|
||||
let radius = rng.gen_range(18, 20) * 9;
|
||||
let size = Vec2::broadcast(radius * 2 / 9 - 2);
|
||||
|
||||
if gen.get(center).map(|sample| sample.chaos).unwrap_or(0.0) > 0.35
|
||||
|| gen.get(center).map(|sample| sample.alt).unwrap_or(0.0) < CONFIG.sea_level + 10.0
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let alt = gen.get(center).map(|sample| sample.alt).unwrap_or(0.0) as i32;
|
||||
|
||||
let mut vol = TownVol::generate_from(
|
||||
@ -154,6 +165,7 @@ impl TownState {
|
||||
vol.gen_parks(rng, 3);
|
||||
vol.emplace_columns();
|
||||
let houses = vol.gen_houses(rng, 50);
|
||||
vol.gen_wells(rng, 5);
|
||||
vol.gen_walls(rng);
|
||||
vol.resolve_modules(rng);
|
||||
vol.cull_unused();
|
||||
@ -301,10 +313,7 @@ impl TownVol {
|
||||
self.set_col_kind(cell, Some(ColumnKind::Internal));
|
||||
let col = self.col(cell).unwrap();
|
||||
let ground = col.ground;
|
||||
for z in 0..2 {
|
||||
let _ =
|
||||
self.set(Vec3::new(cell.x, cell.y, ground + z), CellKind::Park.into());
|
||||
}
|
||||
let _ = self.set(Vec3::new(cell.x, cell.y, ground), CellKind::Park.into());
|
||||
}
|
||||
|
||||
break;
|
||||
@ -394,6 +403,20 @@ impl TownVol {
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_wells(&mut self, rng: &mut impl Rng, n: usize) {
|
||||
for _ in 0..n {
|
||||
if let Some(cell) = self.choose_cell(rng, |_, cell| {
|
||||
if let CellKind::Park = cell.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
let _ = self.set(cell, CellKind::Well.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_houses(&mut self, rng: &mut impl Rng, n: usize) -> Vec<House> {
|
||||
const ATTEMPTS: usize = 10;
|
||||
|
||||
@ -401,7 +424,12 @@ impl TownVol {
|
||||
for _ in 0..n {
|
||||
for _ in 0..ATTEMPTS {
|
||||
let entrance = {
|
||||
let start = self.choose_cell(rng, |_, cell| cell.is_road()).unwrap();
|
||||
let start_col = self.choose_column(rng, |_, col| col.is_road()).unwrap();;
|
||||
let start = Vec3::new(
|
||||
start_col.x,
|
||||
start_col.y,
|
||||
self.col(start_col).unwrap().ground,
|
||||
);
|
||||
let dir = Vec3::from(util::gen_dir(rng));
|
||||
|
||||
if self
|
||||
@ -419,13 +447,13 @@ impl TownVol {
|
||||
}
|
||||
};
|
||||
|
||||
let mut cells: HashSet<_> = Some(entrance).into_iter().collect();
|
||||
let mut cells = HashSet::new();
|
||||
|
||||
let mut energy = 1000;
|
||||
let mut energy = 2300;
|
||||
while energy > 0 {
|
||||
energy -= 1;
|
||||
|
||||
let parent = *cells.iter().choose(rng).unwrap();
|
||||
let parent = *cells.iter().choose(rng).unwrap_or(&entrance);
|
||||
let dir = util::UNITS_3D
|
||||
.choose_weighted(rng, |pos| 1 + pos.z.max(0))
|
||||
.unwrap();
|
||||
@ -441,6 +469,8 @@ impl TownVol {
|
||||
|| cells.contains(&(parent + dir - Vec3::unit_z()))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
&& parent.z + dir.z <= entrance.z + 2
|
||||
// Maximum house height
|
||||
{
|
||||
cells.insert(parent + dir);
|
||||
energy -= 10;
|
||||
@ -602,6 +632,7 @@ fn modules_from_kind(kind: &CellKind) -> Option<&'static [(Arc<Structure>, [Modu
|
||||
match kind {
|
||||
CellKind::House(_) => Some(&HOUSE_MODULES),
|
||||
CellKind::Wall => Some(&WALL_MODULES),
|
||||
CellKind::Well => Some(&WELL_MODULES),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -613,6 +644,10 @@ lazy_static! {
|
||||
module("human.floor_ground", [This, This, This, This, This, That]),
|
||||
module("human.stair_ground", [This, This, This, This, This, That]),
|
||||
module("human.corner_ground", [This, This, That, That, This, That]),
|
||||
module(
|
||||
"human.window_corner_ground",
|
||||
[This, This, That, That, This, That],
|
||||
),
|
||||
module("human.wall_ground", [This, This, This, That, This, That]),
|
||||
module("human.door_ground", [This, This, This, That, This, That]),
|
||||
module("human.window_ground", [This, This, This, That, This, That]),
|
||||
@ -629,6 +664,10 @@ lazy_static! {
|
||||
"human.corner_upstairs",
|
||||
[This, This, That, That, This, This],
|
||||
),
|
||||
module(
|
||||
"human.window_corner_upstairs",
|
||||
[This, This, That, That, This, This],
|
||||
),
|
||||
module("human.wall_upstairs", [This, This, This, That, This, This]),
|
||||
module(
|
||||
"human.window_upstairs",
|
||||
@ -649,4 +688,29 @@ lazy_static! {
|
||||
module("wall.single_top", [That, That, That, That, That, This]),
|
||||
]
|
||||
};
|
||||
pub static ref WELL_MODULES: Vec<(Arc<Structure>, [ModuleKind; 6])> = {
|
||||
use ModuleKind::*;
|
||||
vec![module("misc.well", [That; 6])]
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO
|
||||
struct ModuleModel {
|
||||
near: u64,
|
||||
mask: u64,
|
||||
vol: Arc<Structure>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum NearKind {
|
||||
This,
|
||||
That,
|
||||
}
|
||||
|
||||
impl ModuleModel {
|
||||
pub fn generate_list(_details: &[(&str, &[([i32; 3], NearKind)])]) -> Vec<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -48,6 +48,7 @@ pub enum CellKind {
|
||||
Road,
|
||||
Wall,
|
||||
House(usize),
|
||||
Well,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
@ -57,13 +58,6 @@ pub struct TownCell {
|
||||
}
|
||||
|
||||
impl TownCell {
|
||||
pub fn is_road(&self) -> bool {
|
||||
match self.kind {
|
||||
CellKind::Road => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_space(&self) -> bool {
|
||||
match self.kind {
|
||||
CellKind::Empty => true,
|
||||
|
@ -128,7 +128,7 @@ impl WorldSim {
|
||||
cave_0_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||
cave_1_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||
|
||||
structure_gen: StructureGen2d::new(rng.gen(), 32, 24),
|
||||
structure_gen: StructureGen2d::new(rng.gen(), 32, 16),
|
||||
region_gen: StructureGen2d::new(rng.gen(), 400, 96),
|
||||
cliff_gen: StructureGen2d::new(rng.gen(), 80, 56),
|
||||
humid_nz: Billow::new()
|
||||
@ -150,9 +150,9 @@ impl WorldSim {
|
||||
// but value here is from -0.275 to 0.225).
|
||||
let alt_base = uniform_noise(|_, wposf| {
|
||||
Some(
|
||||
(gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32)
|
||||
.sub(0.1)
|
||||
.mul(0.25),
|
||||
(gen_ctx.alt_nz.get((wposf.div(10_000.0)).into_array()) as f32)
|
||||
.sub(0.05)
|
||||
.mul(0.35),
|
||||
)
|
||||
});
|
||||
|
||||
@ -174,14 +174,14 @@ impl WorldSim {
|
||||
.max(0.0);
|
||||
|
||||
Some(
|
||||
(gen_ctx.chaos_nz.get((wposf.div(3_000.0)).into_array()) as f32)
|
||||
(gen_ctx.chaos_nz.get((wposf.div(3_500.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
// [0, 1] * [0.25, 1] = [0, 1] (but probably towards the lower end)
|
||||
.mul(
|
||||
(gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.max(0.25)
|
||||
.max(0.4)
|
||||
.min(1.0),
|
||||
)
|
||||
// Chaos is always increased by a little when we're on a hill (but remember that
|
||||
@ -189,7 +189,7 @@ impl WorldSim {
|
||||
// [0, 1] + 0.15 * [0, 1.6] = [0, 1.24]
|
||||
.add(0.2 * hill)
|
||||
// We can't have *no* chaos!
|
||||
.max(0.1),
|
||||
.max(0.12),
|
||||
)
|
||||
});
|
||||
|
||||
@ -214,12 +214,17 @@ impl WorldSim {
|
||||
.abs()
|
||||
.powf(1.35);
|
||||
|
||||
fn spring(x: f32, pow: f32) -> f32 {
|
||||
x.abs().powf(pow) * x.signum()
|
||||
}
|
||||
|
||||
(0.0 + alt_main
|
||||
+ (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32)
|
||||
.mul(alt_main.max(0.25))
|
||||
.mul(alt_main.powf(0.8).max(0.15))
|
||||
.mul(0.3)
|
||||
.add(1.0)
|
||||
.mul(0.5))
|
||||
.mul(0.4)
|
||||
+ spring(alt_main.abs().powf(0.5).min(0.75).mul(60.0).sin(), 4.0).mul(0.045))
|
||||
};
|
||||
|
||||
// Now we can compute the final altitude using chaos.
|
||||
@ -227,7 +232,10 @@ impl WorldSim {
|
||||
// alt_pre, then multiply by CONFIG.mountain_scale and add to the base and sea level to
|
||||
// get an adjusted value, then multiply the whole thing by map_edge_factor
|
||||
// (TODO: compute final bounds).
|
||||
Some((alt_base[posi].1 + alt_main.mul(chaos[posi].1)).mul(map_edge_factor(posi)))
|
||||
Some(
|
||||
(alt_base[posi].1 + alt_main.mul(chaos[posi].1.powf(1.2)))
|
||||
.mul(map_edge_factor(posi)),
|
||||
)
|
||||
});
|
||||
|
||||
// Check whether any tiles around this tile are not water (since Lerp will ensure that they
|
||||
@ -704,7 +712,7 @@ impl SimChunk {
|
||||
} else {
|
||||
// For now we don't take humidity into account for cold climates (but we really
|
||||
// should!) except that we make sure we only have snow pines when there is snow.
|
||||
if temp <= CONFIG.snow_temp && humidity > CONFIG.forest_hum {
|
||||
if temp <= CONFIG.snow_temp {
|
||||
ForestKind::SnowPine
|
||||
} else if humidity > CONFIG.desert_hum {
|
||||
ForestKind::Pine
|
||||
|
Loading…
Reference in New Issue
Block a user