mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'laundmo/glide_weather_wind' into 'master'
Add Lift to gliders (thermals, ridge lift) See merge request veloren/veloren!3977
This commit is contained in:
commit
2a9e03caf2
@ -31,4 +31,4 @@ dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo"
|
||||
# misc
|
||||
swarm = "run --bin swarm --features client/bin_bot,client/tick_network --"
|
||||
ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak,bin,stat"
|
||||
ci-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish"
|
||||
ci-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish"
|
@ -95,6 +95,7 @@ const int ENERGY_PHOENIX = 55;
|
||||
const int PHOENIX_BEAM = 56;
|
||||
const int PHOENIX_BUILD_UP_AIM = 57;
|
||||
const int CLAY_SHRAPNEL = 58;
|
||||
const int AIRFLOW = 59;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -163,10 +164,24 @@ mat4 spin_in_axis(vec3 axis, float angle)
|
||||
float c = cos(angle);
|
||||
float oc = 1.0 - c;
|
||||
|
||||
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0,
|
||||
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0,
|
||||
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0,
|
||||
0, 0, 0, 1);
|
||||
return mat4(
|
||||
oc * axis.x * axis.x + c,
|
||||
oc * axis.x * axis.y - axis.z * s,
|
||||
oc * axis.z * axis.x + axis.y * s,
|
||||
0,
|
||||
|
||||
oc * axis.x * axis.y + axis.z * s,
|
||||
oc * axis.y * axis.y + c,
|
||||
oc * axis.y * axis.z - axis.x * s,
|
||||
0,
|
||||
|
||||
oc * axis.z * axis.x - axis.y * s,
|
||||
oc * axis.y * axis.z + axis.x * s,
|
||||
oc * axis.z * axis.z + c,
|
||||
0,
|
||||
|
||||
0, 0, 0, 1
|
||||
);
|
||||
}
|
||||
|
||||
mat4 identity() {
|
||||
@ -984,6 +999,23 @@ void main() {
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
break;
|
||||
case AIRFLOW:
|
||||
perp_axis = normalize(cross(inst_dir, vec3(1.0, 0.0, 0.0)));
|
||||
attr = Attr(
|
||||
// offsets
|
||||
inst_dir * 0.2 * length(inst_dir) * percent() + inst_dir * percent() * 0.08,
|
||||
// scale
|
||||
vec3(
|
||||
0.3 * length(inst_dir),
|
||||
0.3 * length(inst_dir),
|
||||
3.0 * length(inst_dir) * percent() * (1 - percent())
|
||||
),
|
||||
// color
|
||||
vec4(1.1, 1.1, 1.1, 0.3),
|
||||
// rotation
|
||||
spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
@ -1008,7 +1040,14 @@ void main() {
|
||||
|
||||
// First 3 normals are negative, next 3 are positive
|
||||
// TODO: Make particle normals match orientation
|
||||
vec4 normals[6] = vec4[](vec4(-1,0,0,0), vec4(1,0,0,0), vec4(0,-1,0,0), vec4(0,1,0,0), vec4(0,0,-1,0), vec4(0,0,1,0));
|
||||
vec4 normals[6] = vec4[](
|
||||
vec4(-1,0,0,0),
|
||||
vec4(1,0,0,0),
|
||||
vec4(0,-1,0,0),
|
||||
vec4(0,1,0,0),
|
||||
vec4(0,0,-1,0),
|
||||
vec4(0,0,1,0)
|
||||
);
|
||||
f_norm =
|
||||
// inst_pos *
|
||||
normalize(((normals[(v_norm_ao >> 0) & 0x7u]) * attr.rot).xyz);
|
||||
|
@ -2,10 +2,31 @@ use crate::comp::Pos;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::Entity;
|
||||
use std::ops::{Mul, MulAssign};
|
||||
use vek::Vec3;
|
||||
|
||||
/// A resource that stores the time of day.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub struct TimeOfDay(pub f64);
|
||||
impl TimeOfDay {
|
||||
pub fn new(t: f64) -> Self { TimeOfDay(t) }
|
||||
|
||||
fn get_angle_rad(self) -> f32 {
|
||||
const TIME_FACTOR: f64 = (std::f64::consts::PI * 2.0) / (3600.0 * 24.0);
|
||||
((self.0 * TIME_FACTOR) % (std::f64::consts::PI * 2.0)) as f32
|
||||
}
|
||||
|
||||
/// Computes the direction of light from the sun based on the time of day.
|
||||
pub fn get_sun_dir(self) -> Vec3<f32> {
|
||||
let angle_rad = self.get_angle_rad();
|
||||
Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos())
|
||||
}
|
||||
|
||||
/// Computes the direction of light from the moon based on the time of day.
|
||||
pub fn get_moon_dir(self) -> Vec3<f32> {
|
||||
let angle_rad = self.get_angle_rad();
|
||||
-Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos() - 0.5).normalized()
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeOfDay {
|
||||
pub fn day(&self) -> f64 { self.0.rem_euclid(24.0 * 3600.0) }
|
||||
|
@ -26,10 +26,30 @@ impl From<&JoinData<'_>> for Data {
|
||||
Self {
|
||||
// Aspect ratio is what really matters for lift/drag ratio
|
||||
// and the aerodynamics model works for ARs up to 25.
|
||||
//
|
||||
// The inflated dimensions are hopefully only a temporary
|
||||
// bandaid for the poor glide ratio experienced under 2.5G.
|
||||
//
|
||||
// The formula is:
|
||||
// s: span_length_modifier
|
||||
// c: chord_length_modifier
|
||||
// h: height (this is a hack to balance different races)
|
||||
//
|
||||
// p_a = Pi/4 * c * h * s * h
|
||||
// AR
|
||||
// = (s * h)^2 / p_a
|
||||
// = (s * h)^2 / (Pi / 4 * (c * h) * (s * h))
|
||||
// = (s * h) / (c * h) / (Pi / 4)
|
||||
// = s / c / Pi/4
|
||||
//
|
||||
// or if c is 1,
|
||||
// = s / Pi/4
|
||||
//
|
||||
// In other words, the bigger `span_length` the better.
|
||||
//
|
||||
// A span/chord ratio of 4.5 gives an AR of ~5.73.
|
||||
span_length: scale * 4.5,
|
||||
// A span/chord ratio of 3.0 gives an ARI of ~3.82.
|
||||
span_length: scale * 3.0,
|
||||
chord_length: scale,
|
||||
ori: *data.ori,
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ pub mod site;
|
||||
pub mod sprite;
|
||||
pub mod structure;
|
||||
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
biome::BiomeKind,
|
||||
@ -140,7 +142,11 @@ pub struct TerrainChunkMeta {
|
||||
tree_density: f32,
|
||||
contains_cave: bool,
|
||||
contains_river: bool,
|
||||
near_water: bool,
|
||||
river_velocity: Vec3<f32>,
|
||||
approx_chunk_terrain_normal: Option<Vec3<f32>>,
|
||||
rockiness: f32,
|
||||
cliff_height: f32,
|
||||
temp: f32,
|
||||
humidity: f32,
|
||||
site: Option<SiteKindMeta>,
|
||||
@ -158,10 +164,14 @@ impl TerrainChunkMeta {
|
||||
tree_density: f32,
|
||||
contains_cave: bool,
|
||||
contains_river: bool,
|
||||
near_water: bool,
|
||||
river_velocity: Vec3<f32>,
|
||||
temp: f32,
|
||||
humidity: f32,
|
||||
site: Option<SiteKindMeta>,
|
||||
approx_chunk_terrain_normal: Option<Vec3<f32>>,
|
||||
rockiness: f32,
|
||||
cliff_height: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
@ -170,6 +180,7 @@ impl TerrainChunkMeta {
|
||||
tree_density,
|
||||
contains_cave,
|
||||
contains_river,
|
||||
near_water,
|
||||
river_velocity,
|
||||
temp,
|
||||
humidity,
|
||||
@ -178,6 +189,9 @@ impl TerrainChunkMeta {
|
||||
debug_points: Vec::new(),
|
||||
debug_lines: Vec::new(),
|
||||
sprite_cfgs: HashMap::default(),
|
||||
approx_chunk_terrain_normal,
|
||||
rockiness,
|
||||
cliff_height,
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +203,7 @@ impl TerrainChunkMeta {
|
||||
tree_density: 0.0,
|
||||
contains_cave: false,
|
||||
contains_river: false,
|
||||
near_water: false,
|
||||
river_velocity: Vec3::zero(),
|
||||
temp: 0.0,
|
||||
humidity: 0.0,
|
||||
@ -197,6 +212,9 @@ impl TerrainChunkMeta {
|
||||
debug_points: Vec::new(),
|
||||
debug_lines: Vec::new(),
|
||||
sprite_cfgs: HashMap::default(),
|
||||
approx_chunk_terrain_normal: None,
|
||||
rockiness: 0.0,
|
||||
cliff_height: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +222,7 @@ impl TerrainChunkMeta {
|
||||
|
||||
pub fn biome(&self) -> BiomeKind { self.biome }
|
||||
|
||||
/// Altitude in blocks
|
||||
pub fn alt(&self) -> f32 { self.alt }
|
||||
|
||||
pub fn tree_density(&self) -> f32 { self.tree_density }
|
||||
@ -212,10 +231,13 @@ impl TerrainChunkMeta {
|
||||
|
||||
pub fn contains_river(&self) -> bool { self.contains_river }
|
||||
|
||||
pub fn near_water(&self) -> bool { self.near_water }
|
||||
|
||||
pub fn river_velocity(&self) -> Vec3<f32> { self.river_velocity }
|
||||
|
||||
pub fn site(&self) -> Option<SiteKindMeta> { self.site }
|
||||
|
||||
/// Temperature from 0 to 1 (possibly -1 to 1)
|
||||
pub fn temp(&self) -> f32 { self.temp }
|
||||
|
||||
pub fn humidity(&self) -> f32 { self.humidity }
|
||||
@ -239,6 +261,14 @@ impl TerrainChunkMeta {
|
||||
pub fn set_sprite_cfg_at(&mut self, rpos: Vec3<i32>, sprite_cfg: SpriteCfg) {
|
||||
self.sprite_cfgs.insert(rpos, sprite_cfg);
|
||||
}
|
||||
|
||||
pub fn approx_chunk_terrain_normal(&self) -> Option<Vec3<f32>> {
|
||||
self.approx_chunk_terrain_normal
|
||||
}
|
||||
|
||||
pub fn rockiness(&self) -> f32 { self.rockiness }
|
||||
|
||||
pub fn cliff_height(&self) -> f32 { self.cliff_height }
|
||||
}
|
||||
|
||||
// Terrain type aliases
|
||||
@ -279,6 +309,39 @@ impl TerrainGrid {
|
||||
&& self.is_space(*pos)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_interpolated<T, F>(&self, pos: Vec2<i32>, mut f: F) -> Option<T>
|
||||
where
|
||||
T: Copy + Default + Add<Output = T> + Mul<f32, Output = T>,
|
||||
F: FnMut(&TerrainChunk) -> T,
|
||||
{
|
||||
let pos = pos.as_::<f64>().wpos_to_cpos();
|
||||
|
||||
let cubic = |a: T, b: T, c: T, d: T, x: f32| -> T {
|
||||
let x2 = x * x;
|
||||
|
||||
// Catmull-Rom splines
|
||||
let co0 = a * -0.5 + b * 1.5 + c * -1.5 + d * 0.5;
|
||||
let co1 = a + b * -2.5 + c * 2.0 + d * -0.5;
|
||||
let co2 = a * -0.5 + c * 0.5;
|
||||
let co3 = b;
|
||||
|
||||
co0 * x2 * x + co1 * x2 + co2 * x + co3
|
||||
};
|
||||
|
||||
let mut x = [T::default(); 4];
|
||||
|
||||
for (x_idx, j) in (-1..3).enumerate() {
|
||||
let y0 = f(self.get_key(pos.map2(Vec2::new(j, -1), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y1 = f(self.get_key(pos.map2(Vec2::new(j, 0), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y2 = f(self.get_key(pos.map2(Vec2::new(j, 1), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y3 = f(self.get_key(pos.map2(Vec2::new(j, 2), |e, q| e.max(0.0) as i32 + q))?);
|
||||
|
||||
x[x_idx] = cubic(y0, y1, y2, y3, pos.y.fract() as f32);
|
||||
}
|
||||
|
||||
Some(cubic(x[0], x[1], x[2], x[3], pos.x.fract() as f32))
|
||||
}
|
||||
}
|
||||
|
||||
impl TerrainChunk {
|
||||
|
@ -12,15 +12,17 @@ use common::{
|
||||
link::Is,
|
||||
mounting::{Rider, VolumeRider},
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, GameMode},
|
||||
resources::{DeltaTime, GameMode, TimeOfDay},
|
||||
states,
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
terrain::{Block, BlockKind, CoordinateConversions, SiteKindMeta, TerrainGrid, NEIGHBOR_DELTA},
|
||||
uid::Uid,
|
||||
util::{Projection, SpatialGrid},
|
||||
vol::{BaseVol, ReadVol},
|
||||
weather::WeatherGrid,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
||||
use itertools::Itertools;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{
|
||||
shred, Entities, Entity, Join, LendJoin, ParJoin, Read, ReadExpect, ReadStorage, SystemData,
|
||||
@ -115,6 +117,151 @@ fn integrate_forces(
|
||||
vel
|
||||
}
|
||||
|
||||
/// Simulates winds based on weather and terrain data for specific position
|
||||
// TODO: Consider exporting it if one wants to build nice visuals
|
||||
fn simulated_wind_vel(
|
||||
pos: &Pos,
|
||||
weather: &WeatherGrid,
|
||||
terrain: &TerrainGrid,
|
||||
time_of_day: &TimeOfDay,
|
||||
) -> Result<Vec3<f32>, ()> {
|
||||
prof_span!(guard, "Apply Weather INIT");
|
||||
|
||||
let pos_2d = pos.0.as_().xy();
|
||||
let chunk_pos: Vec2<i32> = pos_2d.wpos_to_cpos();
|
||||
let Some(current_chunk) = terrain.get_key(chunk_pos) else {
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let meta = current_chunk.meta();
|
||||
|
||||
let interp_weather = weather.get_interpolated(pos.0.xy());
|
||||
// Weather sim wind
|
||||
let interp_alt = terrain
|
||||
.get_interpolated(pos_2d, |c| c.meta().alt())
|
||||
.unwrap_or(0.);
|
||||
let interp_tree_density = terrain
|
||||
.get_interpolated(pos_2d, |c| c.meta().tree_density())
|
||||
.unwrap_or(0.);
|
||||
let interp_town = terrain
|
||||
.get_interpolated(pos_2d, |c| match c.meta().site() {
|
||||
Some(SiteKindMeta::Settlement(_)) => 3.5,
|
||||
_ => 1.0,
|
||||
})
|
||||
.unwrap_or(0.);
|
||||
let normal = terrain
|
||||
.get_interpolated(pos_2d, |c| {
|
||||
c.meta()
|
||||
.approx_chunk_terrain_normal()
|
||||
.unwrap_or(Vec3::unit_z())
|
||||
})
|
||||
.unwrap_or(Vec3::unit_z());
|
||||
let above_ground = pos.0.z - interp_alt;
|
||||
let wind_velocity = interp_weather.wind_vel();
|
||||
|
||||
let surrounding_chunks_metas = NEIGHBOR_DELTA
|
||||
.iter()
|
||||
.map(move |&(x, y)| chunk_pos + Vec2::new(x, y))
|
||||
.filter_map(|cpos| terrain.get_key(cpos).map(|c| c.meta()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
drop(guard);
|
||||
|
||||
prof_span!(guard, "thermals");
|
||||
|
||||
// === THERMALS ===
|
||||
|
||||
// Sun angle of incidence.
|
||||
//
|
||||
// 0.0..1.0, 0.25 morning, 0.45 midday, 0.66 evening, 0.79 night, 0.0/1.0
|
||||
// midnight
|
||||
let sun_dir = time_of_day.get_sun_dir().normalized();
|
||||
let mut lift = ((sun_dir - normal.normalized()).magnitude() - 0.5).max(0.2) * 3.;
|
||||
|
||||
// TODO: potential source of harsh edges in wind speed.
|
||||
let temperatures = surrounding_chunks_metas.iter().map(|m| m.temp()).minmax();
|
||||
|
||||
// More thermals if hot chunks border cold chunks
|
||||
lift *= match temperatures {
|
||||
itertools::MinMaxResult::NoElements | itertools::MinMaxResult::OneElement(_) => 1.0,
|
||||
itertools::MinMaxResult::MinMax(a, b) => 0.8 + ((a - b).abs() * 1.1),
|
||||
}
|
||||
.min(2.0);
|
||||
|
||||
// TODO: potential source of harsh edges in wind speed.
|
||||
//
|
||||
// Way more thermals in strong rain as its often caused by strong thermals.
|
||||
// Less in weak rain or cloudy ..
|
||||
lift *= if interp_weather.rain.is_between(0.5, 1.0) && interp_weather.cloud.is_between(0.6, 1.0)
|
||||
{
|
||||
1.5
|
||||
} else if interp_weather.rain.is_between(0.2, 0.5) && interp_weather.cloud.is_between(0.3, 0.6)
|
||||
{
|
||||
0.8
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
// The first 15 blocks are weaker. Starting from the ground should be difficult.
|
||||
lift *= (above_ground / 15.).min(1.);
|
||||
lift *= (220. - above_ground / 20.).clamp(0.0, 1.0);
|
||||
|
||||
// TODO: Smooth this, and increase height some more (500 isnt that much higher
|
||||
// than the spires)
|
||||
if interp_alt > 500.0 {
|
||||
lift *= 0.8;
|
||||
}
|
||||
|
||||
// More thermals above towns, the materials tend to heat up more.
|
||||
lift *= interp_town;
|
||||
|
||||
// Bodies of water cool the air, causing less thermals.
|
||||
lift *= terrain
|
||||
.get_interpolated(pos_2d, |c| 1. - c.meta().near_water() as i32 as f32)
|
||||
.unwrap_or(1.);
|
||||
|
||||
drop(guard);
|
||||
|
||||
// === Ridge/Wave lift ===
|
||||
|
||||
let mut ridge_lift = {
|
||||
let steepness = normal.angle_between(normal.with_z(0.)).max(0.5);
|
||||
|
||||
// angle between normal and wind
|
||||
let mut angle = wind_velocity.angle_between(normal.xy()); // 1.4 radians of zero
|
||||
|
||||
// a deadzone of +-1.5 radians if wind is blowing away from
|
||||
// the mountainside.
|
||||
angle = (angle - 1.3).max(0.0);
|
||||
|
||||
// the ridge lift is based on the angle and the velocity of the wind
|
||||
angle * steepness * wind_velocity.magnitude() * 2.5
|
||||
};
|
||||
|
||||
// Cliffs mean more lift
|
||||
// 44 seems to be max, according to a lerp in WorldSim::generate_cliffs
|
||||
ridge_lift *= 0.9 + (meta.cliff_height() / 44.0) * 1.2;
|
||||
|
||||
// Height based fall-off (https://www.desmos.com/calculator/jijqfunchg)
|
||||
ridge_lift *= 1. / (1. + (1.3f32.powf(0.1 * above_ground - 15.)));
|
||||
|
||||
// More flat wind above ground (https://www.desmos.com/calculator/jryiyqsdnx)
|
||||
let wind_factor = 1. / (0.25 + (0.96f32.powf(0.1 * above_ground - 15.)));
|
||||
|
||||
let mut wind_vel = (wind_velocity * wind_factor).with_z(lift + ridge_lift);
|
||||
|
||||
// probably 0. to 1. src: SiteKind::is_suitable_loc comparisons
|
||||
wind_vel *= (1.0 - interp_tree_density).max(0.7);
|
||||
|
||||
// Clamp magnitude, we never want to throw players around way too fast.
|
||||
let magn = wind_vel.magnitude_squared().max(0.0001);
|
||||
|
||||
// 600 here is compared to squared ~ 25. this limits the magnitude of the wind.
|
||||
wind_vel *= magn.min(600.) / magn;
|
||||
|
||||
Ok(wind_vel)
|
||||
}
|
||||
|
||||
fn calc_z_limit(char_state_maybe: Option<&CharacterState>, collider: &Collider) -> (f32, f32) {
|
||||
let modifier = if char_state_maybe.map_or(false, |c_s| c_s.is_dodge() || c_s.is_glide()) {
|
||||
0.5
|
||||
@ -150,11 +297,12 @@ pub struct PhysicsRead<'a> {
|
||||
is_ridings: ReadStorage<'a, Is<Rider>>,
|
||||
is_volume_ridings: ReadStorage<'a, Is<VolumeRider>>,
|
||||
projectiles: ReadStorage<'a, Projectile>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
densities: ReadStorage<'a, Density>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
weather: Option<Read<'a, WeatherGrid>>,
|
||||
time_of_day: Read<'a, TimeOfDay>,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -236,7 +384,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&mut self.write.previous_phys_cache,
|
||||
&self.read.colliders,
|
||||
self.read.scales.maybe(),
|
||||
self.read.char_states.maybe(),
|
||||
self.read.character_states.maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -369,7 +517,7 @@ impl<'a> PhysicsData<'a> {
|
||||
// moving whether it should interact into the collider component
|
||||
// or into a separate component.
|
||||
read.projectiles.maybe(),
|
||||
read.char_states.maybe(),
|
||||
read.character_states.maybe(),
|
||||
)
|
||||
.par_join()
|
||||
.map_init(
|
||||
@ -438,7 +586,7 @@ impl<'a> PhysicsData<'a> {
|
||||
previous_cache,
|
||||
mass,
|
||||
collider,
|
||||
read.char_states.get(entity),
|
||||
read.character_states.get(entity),
|
||||
read.is_ridings.get(entity),
|
||||
))
|
||||
})
|
||||
@ -601,6 +749,54 @@ impl<'a> PhysicsData<'a> {
|
||||
ref mut write,
|
||||
} = self;
|
||||
|
||||
prof_span!(guard, "Apply Weather");
|
||||
if let Some(weather) = &read.weather {
|
||||
for (_, state, pos, phys) in (
|
||||
&read.entities,
|
||||
&read.character_states,
|
||||
&write.positions,
|
||||
&mut write.physics_states,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Don't simulate for non-gliding, for now
|
||||
if !state.is_glide() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos_2d = pos.0.as_().xy();
|
||||
let chunk_pos: Vec2<i32> = pos_2d.wpos_to_cpos();
|
||||
let Some(current_chunk) = &read.terrain.get_key(chunk_pos) else {
|
||||
// oopsie
|
||||
continue;
|
||||
};
|
||||
|
||||
let meta = current_chunk.meta();
|
||||
|
||||
// Skip simulating for entites deeply under the ground
|
||||
if pos.0.z < meta.alt() - 25.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If couldn't simulate wind for some reason, skip
|
||||
let Ok(wind_vel) =
|
||||
simulated_wind_vel(pos, weather, &read.terrain, &read.time_of_day)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
phys.in_fluid = phys.in_fluid.map(|f| match f {
|
||||
Fluid::Air { elevation, .. } => Fluid::Air {
|
||||
vel: Vel(wind_vel),
|
||||
elevation,
|
||||
},
|
||||
fluid => fluid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
drop(guard);
|
||||
|
||||
prof_span!(guard, "insert PosVelOriDefer");
|
||||
// NOTE: keep in sync with join below
|
||||
(
|
||||
|
@ -96,6 +96,7 @@ pub struct EguiInnerState {
|
||||
selected_entity_cylinder_height: f32,
|
||||
frame_times: Vec<f32>,
|
||||
windows: EguiWindows,
|
||||
debug_vectors_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -118,6 +119,7 @@ impl Default for EguiInnerState {
|
||||
selected_entity_cylinder_height: 10.0,
|
||||
frame_times: Vec::new(),
|
||||
windows: EguiWindows::default(),
|
||||
debug_vectors_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,6 +144,7 @@ pub enum EguiAction {
|
||||
},
|
||||
DebugShape(EguiDebugShapeAction),
|
||||
SetExperimentalShader(String, bool),
|
||||
SetShowDebugVector(bool),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -225,6 +228,7 @@ pub fn maintain_egui_inner(
|
||||
let mut max_entity_distance = egui_state.max_entity_distance;
|
||||
let mut selected_entity_cylinder_height = egui_state.selected_entity_cylinder_height;
|
||||
let mut windows = egui_state.windows.clone();
|
||||
let mut debug_vectors_enabled_mut = egui_state.debug_vectors_enabled;
|
||||
|
||||
// If a debug cylinder was added in the last frame, store it against the
|
||||
// selected entity
|
||||
@ -261,6 +265,7 @@ pub fn maintain_egui_inner(
|
||||
ui.checkbox(&mut windows.ecs_entities, "ECS Entities");
|
||||
ui.checkbox(&mut windows.frame_time, "Frame Time");
|
||||
ui.checkbox(&mut windows.experimental_shaders, "Experimental Shaders");
|
||||
ui.checkbox(&mut debug_vectors_enabled_mut, "Show Debug Vectors");
|
||||
});
|
||||
});
|
||||
|
||||
@ -510,6 +515,12 @@ pub fn maintain_egui_inner(
|
||||
}
|
||||
}
|
||||
};
|
||||
if debug_vectors_enabled_mut != egui_state.debug_vectors_enabled {
|
||||
egui_actions
|
||||
.actions
|
||||
.push(EguiAction::SetShowDebugVector(debug_vectors_enabled_mut));
|
||||
egui_state.debug_vectors_enabled = debug_vectors_enabled_mut;
|
||||
}
|
||||
|
||||
egui_state.max_entity_distance = max_entity_distance;
|
||||
egui_state.selected_entity_cylinder_height = selected_entity_cylinder_height;
|
||||
@ -754,8 +765,7 @@ fn selected_entity_window(
|
||||
two_col_row(ui, "On Wall", physics_state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z )));
|
||||
two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string());
|
||||
two_col_row(ui, "In Fluid", match physics_state.in_fluid {
|
||||
|
||||
Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation),
|
||||
Some(Fluid::Air { elevation, vel, .. }) => format!("Air (Elevation: {:.1}), vel: ({:.1},{:.1},{:.1}) ({:.1} u/s)", elevation, vel.0.x, vel.0.y, vel.0.z, vel.0.magnitude()),
|
||||
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
|
||||
_ => "None".to_owned() });
|
||||
});
|
||||
|
@ -270,6 +270,7 @@ widget_ids! {
|
||||
velocity,
|
||||
glide_ratio,
|
||||
glide_aoe,
|
||||
air_vel,
|
||||
orientation,
|
||||
look_direction,
|
||||
loaded_distance,
|
||||
@ -2735,6 +2736,7 @@ impl Hud {
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.glide_ratio, ui_widgets);
|
||||
// Glide Angle of Attack
|
||||
let glide_angle_text = angle_of_attack_text(
|
||||
debug_info.in_fluid,
|
||||
debug_info.velocity,
|
||||
@ -2746,6 +2748,14 @@ impl Hud {
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.glide_aoe, ui_widgets);
|
||||
// Air velocity
|
||||
let air_vel_text = air_velocity(debug_info.in_fluid);
|
||||
Text::new(&air_vel_text)
|
||||
.color(TEXT_COLOR)
|
||||
.down_from(self.ids.glide_aoe, V_PAD)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.air_vel, ui_widgets);
|
||||
// Player's orientation vector
|
||||
let orientation_text = match debug_info.ori {
|
||||
Some(ori) => {
|
||||
@ -2759,7 +2769,7 @@ impl Hud {
|
||||
};
|
||||
Text::new(&orientation_text)
|
||||
.color(TEXT_COLOR)
|
||||
.down_from(self.ids.glide_aoe, V_PAD)
|
||||
.down_from(self.ids.air_vel, V_PAD)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.orientation, ui_widgets);
|
||||
@ -5387,6 +5397,17 @@ pub fn angle_of_attack_text(
|
||||
}
|
||||
}
|
||||
|
||||
fn air_velocity(fluid: Option<comp::Fluid>) -> String {
|
||||
if let Some(comp::Fluid::Air { vel: air_vel, .. }) = fluid {
|
||||
format!(
|
||||
"Air Velocity: ({:.1}, {:.1}, {:.1})",
|
||||
air_vel.0.x, air_vel.0.y, air_vel.0.z
|
||||
)
|
||||
} else {
|
||||
"Air Velocity: Not in Air".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts multiplier to percentage.
|
||||
/// NOTE: floats are not the most precise type.
|
||||
///
|
||||
|
@ -20,7 +20,7 @@ pub mod ui;
|
||||
use super::{Consts, Renderer, Texture};
|
||||
use crate::scene::camera::CameraMode;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use common::{terrain::BlockKind, util::srgb_to_linear};
|
||||
use common::{resources::TimeOfDay, terrain::BlockKind, util::srgb_to_linear};
|
||||
use std::marker::PhantomData;
|
||||
use vek::*;
|
||||
|
||||
@ -144,8 +144,8 @@ impl Globals {
|
||||
0.0,
|
||||
0.0,
|
||||
],
|
||||
sun_dir: Vec4::from_direction(Self::get_sun_dir(time_of_day)).into_array(),
|
||||
moon_dir: Vec4::from_direction(Self::get_moon_dir(time_of_day)).into_array(),
|
||||
sun_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_sun_dir()).into_array(),
|
||||
moon_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_moon_dir()).into_array(),
|
||||
tick: [
|
||||
(tick % TIME_OVERFLOW) as f32,
|
||||
(tick / TIME_OVERFLOW).floor() as f32,
|
||||
@ -195,23 +195,6 @@ impl Globals {
|
||||
globals_dummy: [0.0; 3],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_angle_rad(time_of_day: f64) -> f32 {
|
||||
const TIME_FACTOR: f64 = (std::f64::consts::PI * 2.0) / (3600.0 * 24.0);
|
||||
((time_of_day * TIME_FACTOR) % (std::f64::consts::PI * 2.0)) as f32
|
||||
}
|
||||
|
||||
/// Computes the direction of light from the sun based on the time of day.
|
||||
pub fn get_sun_dir(time_of_day: f64) -> Vec3<f32> {
|
||||
let angle_rad = Self::get_angle_rad(time_of_day);
|
||||
Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos())
|
||||
}
|
||||
|
||||
/// Computes the direction of light from the moon based on the time of day.
|
||||
pub fn get_moon_dir(time_of_day: f64) -> Vec3<f32> {
|
||||
let angle_rad = Self::get_angle_rad(time_of_day);
|
||||
-Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos() - 0.5).normalized()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Globals {
|
||||
|
@ -110,6 +110,7 @@ pub enum ParticleMode {
|
||||
PhoenixBeam = 56,
|
||||
PhoenixBuildUpAim = 57,
|
||||
ClayShrapnel = 58,
|
||||
Airflow = 59,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -9,7 +9,8 @@ use vek::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DebugShape {
|
||||
Line([Vec3<f32>; 2]),
|
||||
/// [Start, End], width
|
||||
Line([Vec3<f32>; 2], f32),
|
||||
Cylinder {
|
||||
radius: f32,
|
||||
height: f32,
|
||||
@ -96,13 +97,13 @@ impl DebugShape {
|
||||
};
|
||||
|
||||
match self {
|
||||
DebugShape::Line([a, b]) => {
|
||||
DebugShape::Line([a, b], width) => {
|
||||
//let h = Vec3::new(0.0, 1.0, 0.0);
|
||||
//mesh.push_quad(quad(*a, a + h, b + h, *b));
|
||||
box_along_line(
|
||||
LineSegment3 { start: *a, end: *b },
|
||||
0.1,
|
||||
0.1,
|
||||
*width,
|
||||
*width,
|
||||
[1.0; 4],
|
||||
&mut mesh,
|
||||
);
|
||||
|
@ -27,6 +27,7 @@ use crate::{
|
||||
GlobalsBindGroup, Light, Model, PointLightMatrix, PostProcessLocals, RainOcclusionLocals,
|
||||
Renderer, Shadow, ShadowLocals, SkyboxVertex,
|
||||
},
|
||||
session::PlayerDebugLines,
|
||||
settings::Settings,
|
||||
window::{AnalogGameInput, Event},
|
||||
};
|
||||
@ -38,9 +39,10 @@ use common::{
|
||||
tool::ToolKind,
|
||||
},
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, TimeScale},
|
||||
resources::{DeltaTime, TimeOfDay, TimeScale},
|
||||
terrain::{BlockKind, TerrainChunk, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
weather::WeatherGrid,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_state::State;
|
||||
@ -121,6 +123,8 @@ pub struct Scene {
|
||||
pub interpolated_time_of_day: Option<f64>,
|
||||
last_lightning: Option<(Vec3<f32>, f64)>,
|
||||
local_time: f64,
|
||||
|
||||
pub debug_vectors_enabled: bool,
|
||||
}
|
||||
|
||||
pub struct SceneData<'a> {
|
||||
@ -148,11 +152,11 @@ pub struct SceneData<'a> {
|
||||
|
||||
impl<'a> SceneData<'a> {
|
||||
pub fn get_sun_dir(&self) -> Vec3<f32> {
|
||||
Globals::get_sun_dir(self.interpolated_time_of_day.unwrap_or(0.0))
|
||||
TimeOfDay::new(self.interpolated_time_of_day.unwrap_or(0.0)).get_sun_dir()
|
||||
}
|
||||
|
||||
pub fn get_moon_dir(&self) -> Vec3<f32> {
|
||||
Globals::get_moon_dir(self.interpolated_time_of_day.unwrap_or(0.0))
|
||||
TimeOfDay::new(self.interpolated_time_of_day.unwrap_or(0.0)).get_moon_dir()
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,6 +364,7 @@ impl Scene {
|
||||
interpolated_time_of_day: None,
|
||||
last_lightning: None,
|
||||
local_time: 0.0,
|
||||
debug_vectors_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1541,7 +1546,7 @@ impl Scene {
|
||||
for line in chunk.meta().debug_lines().iter() {
|
||||
let shape_id = self
|
||||
.debug
|
||||
.add_shape(DebugShape::Line([line.start, line.end]));
|
||||
.add_shape(DebugShape::Line([line.start, line.end], 0.1));
|
||||
ret.push(shape_id);
|
||||
self.debug
|
||||
.set_context(shape_id, [0.0; 4], [1.0; 4], [0.0, 0.0, 0.0, 1.0]);
|
||||
@ -1638,4 +1643,87 @@ impl Scene {
|
||||
keep
|
||||
});
|
||||
}
|
||||
|
||||
pub fn maintain_debug_vectors(&mut self, client: &Client, lines: &mut PlayerDebugLines) {
|
||||
lines
|
||||
.chunk_normal
|
||||
.take()
|
||||
.map(|id| self.debug.remove_shape(id));
|
||||
lines.fluid_vel.take().map(|id| self.debug.remove_shape(id));
|
||||
lines.wind.take().map(|id| self.debug.remove_shape(id));
|
||||
lines.vel.take().map(|id| self.debug.remove_shape(id));
|
||||
if self.debug_vectors_enabled {
|
||||
let ecs = client.state().ecs();
|
||||
|
||||
let vels = &ecs.read_component::<comp::Vel>();
|
||||
let Some(vel) = vels.get(client.entity()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let phys_states = &ecs.read_component::<comp::PhysicsState>();
|
||||
let Some(phys) = phys_states.get(client.entity()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let positions = &ecs.read_component::<comp::Pos>();
|
||||
let Some(pos) = positions.get(client.entity()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let weather = ecs.read_resource::<WeatherGrid>();
|
||||
// take id and remove to delete the previous lines.
|
||||
|
||||
const LINE_WIDTH: f32 = 0.05;
|
||||
// Fluid Velocity
|
||||
{
|
||||
let Some(fluid) = phys.in_fluid else {
|
||||
return;
|
||||
};
|
||||
let shape = DebugShape::Line([pos.0, pos.0 + fluid.flow_vel().0 / 2.], LINE_WIDTH);
|
||||
let id = self.debug.add_shape(shape);
|
||||
lines.fluid_vel = Some(id);
|
||||
self.debug
|
||||
.set_context(id, [0.0; 4], [0.18, 0.72, 0.87, 0.8], [0.0, 0.0, 0.0, 1.0]);
|
||||
}
|
||||
// Chunk Terrain Normal Vector
|
||||
{
|
||||
let Some(chunk) = client.current_chunk() else {
|
||||
return;
|
||||
};
|
||||
let shape = DebugShape::Line(
|
||||
[
|
||||
pos.0,
|
||||
pos.0
|
||||
+ chunk
|
||||
.meta()
|
||||
.approx_chunk_terrain_normal()
|
||||
.unwrap_or(Vec3::unit_z())
|
||||
* 2.5,
|
||||
],
|
||||
LINE_WIDTH,
|
||||
);
|
||||
let id = self.debug.add_shape(shape);
|
||||
lines.chunk_normal = Some(id);
|
||||
self.debug
|
||||
.set_context(id, [0.0; 4], [0.22, 0.63, 0.1, 0.8], [0.0, 0.0, 0.0, 1.0]);
|
||||
}
|
||||
// Wind
|
||||
{
|
||||
let wind = weather.get_interpolated(pos.0.xy()).wind_vel();
|
||||
let shape = DebugShape::Line([pos.0, pos.0 + wind * 5.0], LINE_WIDTH);
|
||||
let id = self.debug.add_shape(shape);
|
||||
lines.wind = Some(id);
|
||||
self.debug
|
||||
.set_context(id, [0.0; 4], [0.76, 0.76, 0.76, 0.8], [0.0, 0.0, 0.0, 1.0]);
|
||||
}
|
||||
// Player Vel
|
||||
{
|
||||
let shape = DebugShape::Line([pos.0, pos.0 + vel.0 / 2.0], LINE_WIDTH);
|
||||
let id = self.debug.add_shape(shape);
|
||||
lines.vel = Some(id);
|
||||
self.debug
|
||||
.set_context(id, [0.0; 4], [0.98, 0.76, 0.01, 0.8], [0.0, 0.0, 0.0, 1.0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ use common::{
|
||||
item::Reagent,
|
||||
object,
|
||||
shockwave::{self, ShockwaveDodgeable},
|
||||
Beam, Body, CharacterActivity, CharacterState, Ori, Pos, Scale, Shockwave, Vel,
|
||||
Beam, Body, CharacterActivity, CharacterState, Fluid, Ori, PhysicsState, Pos, Scale,
|
||||
Shockwave, Vel,
|
||||
},
|
||||
figure::Segment,
|
||||
outcome::Outcome,
|
||||
@ -916,16 +917,18 @@ impl ParticleMgr {
|
||||
let dt = scene_data.state.get_delta_time();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for (entity, interpolated, vel, character_state, body, ori, character_activity) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<Interpolated>(),
|
||||
ecs.read_storage::<Vel>().maybe(),
|
||||
&ecs.read_storage::<CharacterState>(),
|
||||
&ecs.read_storage::<Body>(),
|
||||
&ecs.read_storage::<Ori>(),
|
||||
&ecs.read_storage::<CharacterActivity>(),
|
||||
)
|
||||
.join()
|
||||
for (entity, interpolated, vel, character_state, body, ori, character_activity, physics) in
|
||||
(
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<Interpolated>(),
|
||||
ecs.read_storage::<Vel>().maybe(),
|
||||
&ecs.read_storage::<CharacterState>(),
|
||||
&ecs.read_storage::<Body>(),
|
||||
&ecs.read_storage::<Ori>(),
|
||||
&ecs.read_storage::<CharacterActivity>(),
|
||||
&ecs.read_storage::<PhysicsState>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
match character_state {
|
||||
CharacterState::Boost(_) => {
|
||||
@ -1317,6 +1320,61 @@ impl ParticleMgr {
|
||||
);
|
||||
}
|
||||
},
|
||||
CharacterState::Glide(_) => {
|
||||
if let Some(Fluid::Air {
|
||||
vel: air_vel,
|
||||
elevation: _,
|
||||
}) = physics.in_fluid
|
||||
{
|
||||
// Empirical observation is that air_vel is somewhere
|
||||
// between 0.0 and 13.0, but we are extending to be sure
|
||||
const MAX_AIR_VEL: f32 = 15.0;
|
||||
const MIN_AIR_VEL: f32 = -2.0;
|
||||
|
||||
let minmax_norm = |val, min, max| (val - min) / (max - min);
|
||||
|
||||
let wind_speed = air_vel.0.magnitude();
|
||||
|
||||
// Less means more frequent particles
|
||||
let heartbeat = 200
|
||||
- Lerp::lerp(
|
||||
50u64,
|
||||
150,
|
||||
minmax_norm(wind_speed, MIN_AIR_VEL, MAX_AIR_VEL),
|
||||
);
|
||||
|
||||
let new_count = self.particles.len()
|
||||
+ usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(heartbeat)),
|
||||
);
|
||||
|
||||
// More number, longer particles
|
||||
let duration = Lerp::lerp(
|
||||
0u64,
|
||||
1000,
|
||||
minmax_norm(wind_speed, MIN_AIR_VEL, MAX_AIR_VEL),
|
||||
);
|
||||
let duration = Duration::from_millis(duration);
|
||||
|
||||
self.particles.resize_with(new_count, || {
|
||||
let start_pos = interpolated.pos
|
||||
+ Vec3::new(
|
||||
body.max_radius(),
|
||||
body.max_radius(),
|
||||
body.height() / 2.0,
|
||||
)
|
||||
.map(|d| d * rng.gen_range(-10.0..10.0));
|
||||
|
||||
Particle::new_directed(
|
||||
duration,
|
||||
time,
|
||||
ParticleMode::Airflow,
|
||||
start_pos,
|
||||
start_pos + air_vel.0,
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +89,14 @@ enum TickAction {
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PlayerDebugLines {
|
||||
pub chunk_normal: Option<DebugShapeId>,
|
||||
pub wind: Option<DebugShapeId>,
|
||||
pub fluid_vel: Option<DebugShapeId>,
|
||||
pub vel: Option<DebugShapeId>,
|
||||
}
|
||||
|
||||
pub struct SessionState {
|
||||
scene: Scene,
|
||||
pub(crate) client: Rc<RefCell<Client>>,
|
||||
@ -113,6 +121,7 @@ pub struct SessionState {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mumble_link: SharedLink,
|
||||
hitboxes: HashMap<specs::Entity, DebugShapeId>,
|
||||
lines: PlayerDebugLines,
|
||||
tracks: HashMap<Vec2<i32>, Vec<DebugShapeId>>,
|
||||
}
|
||||
|
||||
@ -186,6 +195,7 @@ impl SessionState {
|
||||
hitboxes: HashMap::new(),
|
||||
metadata,
|
||||
tracks: HashMap::new(),
|
||||
lines: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,6 +247,7 @@ impl SessionState {
|
||||
&mut self.hitboxes,
|
||||
&mut self.tracks,
|
||||
);
|
||||
self.scene.maintain_debug_vectors(&client, &mut self.lines);
|
||||
|
||||
// All this camera code is just to determine if it's underwater for the sfx
|
||||
// filter
|
||||
|
@ -102,6 +102,9 @@ impl EguiState {
|
||||
}
|
||||
}
|
||||
},
|
||||
EguiAction::SetShowDebugVector(enabled) => {
|
||||
scene.debug_vectors_enabled = enabled;
|
||||
},
|
||||
});
|
||||
|
||||
new_render_mode.map(|rm| SettingsChange::Graphics(Graphics::ChangeRenderMode(Box::new(rm))))
|
||||
|
@ -36,6 +36,7 @@ pub use block::BlockGen;
|
||||
use civ::WorldCivStage;
|
||||
pub use column::ColumnSample;
|
||||
pub use common::terrain::site::{DungeonKindMeta, SettlementKindMeta};
|
||||
use common::terrain::CoordinateConversions;
|
||||
pub use index::{IndexOwned, IndexRef};
|
||||
use sim::WorldSimStage;
|
||||
|
||||
@ -54,8 +55,7 @@ use common::{
|
||||
resources::TimeOfDay,
|
||||
rtsim::ChunkResource,
|
||||
terrain::{
|
||||
Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunk, TerrainChunkMeta,
|
||||
TerrainChunkSize, TerrainGrid,
|
||||
Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid,
|
||||
},
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
};
|
||||
@ -373,6 +373,7 @@ impl World {
|
||||
sim_chunk.tree_density,
|
||||
sim_chunk.cave.1.alt != 0.0,
|
||||
sim_chunk.river.is_river(),
|
||||
sim_chunk.river.near_water(),
|
||||
sim_chunk.river.velocity,
|
||||
sim_chunk.temp,
|
||||
sim_chunk.humidity,
|
||||
@ -391,6 +392,9 @@ impl World {
|
||||
.distance_squared(chunk_center_wpos2d)
|
||||
})
|
||||
.map(|id| index.sites[*id].kind.convert_to_meta().unwrap_or_default()),
|
||||
self.sim.approx_chunk_terrain_normal(chunk_pos),
|
||||
sim_chunk.rockiness,
|
||||
sim_chunk.cliff_height,
|
||||
);
|
||||
|
||||
let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
|
||||
|
@ -1636,6 +1636,26 @@ impl WorldSim {
|
||||
TerrainChunk::water(CONFIG.sea_level as i32)
|
||||
}
|
||||
|
||||
pub fn approx_chunk_terrain_normal(&self, chunk_pos: Vec2<i32>) -> Option<Vec3<f32>> {
|
||||
let curr_chunk = self.get(chunk_pos)?;
|
||||
let downhill_chunk_pos = curr_chunk.downhill?.wpos_to_cpos();
|
||||
let downhill_chunk = self.get(downhill_chunk_pos)?;
|
||||
// special case if chunks are flat
|
||||
if (curr_chunk.alt - downhill_chunk.alt) == 0. {
|
||||
return Some(Vec3::unit_z());
|
||||
}
|
||||
let curr = chunk_pos.cpos_to_wpos_center().as_().with_z(curr_chunk.alt);
|
||||
let down = downhill_chunk_pos
|
||||
.cpos_to_wpos_center()
|
||||
.as_()
|
||||
.with_z(downhill_chunk.alt);
|
||||
let downwards = curr - down;
|
||||
let flat = downwards.with_z(down.z);
|
||||
let mut res = downwards.cross(flat).cross(downwards);
|
||||
res.normalize();
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Draw a map of the world based on chunk information. Returns a buffer of
|
||||
/// u32s.
|
||||
pub fn get_map(&self, index: IndexRef, calendar: Option<&Calendar>) -> WorldMapMsg {
|
||||
|
Loading…
Reference in New Issue
Block a user