mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/weapon-trails' into 'master'
Weapon trails See merge request veloren/veloren!3143
This commit is contained in:
commit
31062c3761
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Waypoints saved between sessions and shared with group members.
|
||||
- New rocks
|
||||
- Weapon trails
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -517,7 +517,7 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Attack(BasicMelee, Dagger): (
|
||||
Attack(BasicMelee(Action), Dagger): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.swing_sword",
|
||||
],
|
||||
@ -551,7 +551,7 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Attack(BasicMelee, Shield): (
|
||||
Attack(BasicMelee(Action), Shield): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.swing",
|
||||
],
|
||||
|
@ -89,6 +89,7 @@
|
||||
"hud.settings.gpu_profiler": "Enable GPU timing (not supported everywhere)",
|
||||
"hud.settings.particles": "Particles",
|
||||
"hud.settings.lossy_terrain_compression": "Lossy terrain compression",
|
||||
"hud.settings.weapon_trails": "Weapon trails",
|
||||
"hud.settings.resolution": "Resolution",
|
||||
"hud.settings.bit_depth": "Bit Depth",
|
||||
"hud.settings.refresh_rate": "Refresh Rate",
|
||||
|
44
assets/voxygen/shaders/trail-frag.glsl
Normal file
44
assets/voxygen/shaders/trail-frag.glsl
Normal file
@ -0,0 +1,44 @@
|
||||
#version 420 core
|
||||
|
||||
#include <constants.glsl>
|
||||
|
||||
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
|
||||
|
||||
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
|
||||
|
||||
#if (FLUID_MODE == FLUID_MODE_CHEAP)
|
||||
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
|
||||
#elif (FLUID_MODE == FLUID_MODE_SHINY)
|
||||
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
|
||||
#endif
|
||||
|
||||
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
|
||||
|
||||
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
|
||||
|
||||
#define HAS_SHADOW_MAPS
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
layout(location = 0) in vec3 f_pos;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
#include <sky.glsl>
|
||||
#include <light.glsl>
|
||||
#include <lod.glsl>
|
||||
|
||||
const float FADE_DIST = 32.0;
|
||||
|
||||
void main() {
|
||||
vec3 trail_color = vec3(.55, .92, 1.0);
|
||||
float trail_alpha = 0.05;
|
||||
// Controls how much light affects alpha variation. TODO: Maybe a better name?
|
||||
float light_variable = 0.075;
|
||||
|
||||
// Make less faint at day (relative to night) by adding light to alpha. Probably hacky but looks fine.
|
||||
// TODO: Trails should also eventually account for shadows, nearby lights, attenuation of sunlight in water, and block based lighting. Note: many of these will require alternative methods that don't require a normal.
|
||||
trail_alpha += get_sun_brightness() * light_variable;
|
||||
|
||||
tgt_color = vec4(trail_color, trail_alpha);
|
||||
}
|
13
assets/voxygen/shaders/trail-vert.glsl
Normal file
13
assets/voxygen/shaders/trail-vert.glsl
Normal file
@ -0,0 +1,13 @@
|
||||
#version 420 core
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
layout(location = 0) in vec3 v_pos;
|
||||
|
||||
layout(location = 0) out vec3 f_pos;
|
||||
|
||||
void main() {
|
||||
f_pos = v_pos;
|
||||
|
||||
gl_Position = all_mat * vec4(f_pos - focus_off.xyz, 1);
|
||||
}
|
@ -344,9 +344,13 @@ impl From<AuxiliaryAbility> for Ability {
|
||||
}
|
||||
}
|
||||
|
||||
/// A lighter form of character state to pass around as needed for frontend
|
||||
/// purposes
|
||||
// Only add to this enum as needed for frontends, not necessary to immediately
|
||||
// add a variant here when adding a new character state
|
||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum CharacterAbilityType {
|
||||
BasicMelee,
|
||||
BasicMelee(StageSection),
|
||||
BasicRanged,
|
||||
Boost,
|
||||
ChargedMelee(StageSection),
|
||||
@ -361,12 +365,13 @@ pub enum CharacterAbilityType {
|
||||
RepeaterRanged,
|
||||
BasicAura,
|
||||
SelfBuff,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<&CharacterState> for CharacterAbilityType {
|
||||
fn from(state: &CharacterState) -> Self {
|
||||
match state {
|
||||
CharacterState::BasicMelee(_) => Self::BasicMelee,
|
||||
CharacterState::BasicMelee(data) => Self::BasicMelee(data.stage_section),
|
||||
CharacterState::BasicRanged(_) => Self::BasicRanged,
|
||||
CharacterState::Boost(_) => Self::Boost,
|
||||
CharacterState::DashMelee(data) => Self::DashMelee(data.stage_section),
|
||||
@ -381,7 +386,23 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
CharacterState::RepeaterRanged(_) => Self::RepeaterRanged,
|
||||
CharacterState::BasicAura(_) => Self::BasicAura,
|
||||
CharacterState::SelfBuff(_) => Self::SelfBuff,
|
||||
_ => Self::BasicMelee,
|
||||
CharacterState::Idle(_)
|
||||
| CharacterState::Climb(_)
|
||||
| CharacterState::Sit
|
||||
| CharacterState::Dance
|
||||
| CharacterState::Talk
|
||||
| CharacterState::Glide(_)
|
||||
| CharacterState::GlideWield(_)
|
||||
| CharacterState::Stunned(_)
|
||||
| CharacterState::Equipping(_)
|
||||
| CharacterState::Wielding(_)
|
||||
| CharacterState::Roll(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
| CharacterState::UseItem(_)
|
||||
| CharacterState::SpriteInteract(_)
|
||||
| CharacterState::Wallrun(_) => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,8 @@ impl Skeleton for ArthropodSkeleton {
|
||||
orientation: mount_orientation,
|
||||
scale: Vec3::one(),
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,8 @@ impl Skeleton for BipedLargeSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ impl Skeleton for BipedSmallSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,8 @@ impl Skeleton for BirdLargeSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ impl Skeleton for BirdMediumSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,33 +47,36 @@ impl Animation for AlphaAnimation {
|
||||
next.torso.position = Vec3::new(0.0, 0.0, 1.1);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
match ability_info.and_then(|a| a.tool) {
|
||||
Some(ToolKind::Sword | ToolKind::Dagger) => {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.chest.orientation =
|
||||
Quaternion::rotation_z(move1 * 1.1 + move2 * -2.0 + move3 * 0.2);
|
||||
next.chest.orientation = Quaternion::rotation_z(move1 * 1.1 + move2 * -2.0);
|
||||
|
||||
next.head.position = Vec3::new(0.0 + move2 * 2.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_z(move1 * -0.9 + move2 * 1.8 + move3 * -0.2);
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * -0.9 + move2 * 1.8);
|
||||
},
|
||||
|
||||
Some(ToolKind::Axe) => {
|
||||
let (move1, move2, move3) = match stage_section {
|
||||
let (move1, move2, _move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
|
||||
Some(StageSection::Action) => (1.0, anim_time, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
next.head.position = Vec3::new(move2 * 2.0, s_a.head.0 + move2 * 2.0, s_a.head.1);
|
||||
next.chest.orientation =
|
||||
Quaternion::rotation_x(0.0 + move1 * 0.6 + move2 * -0.6 + move3 * 0.4)
|
||||
* Quaternion::rotation_y(0.0 + move1 * 0.0 + move2 * 0.0 + move3 * 0.0)
|
||||
* Quaternion::rotation_z(0.0 + move1 * 1.5 + move2 * -2.5 + move3 * 1.5);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_z(0.0 + move1 * -1.5 + move2 * 2.5 + move3 * -1.0);
|
||||
next.chest.orientation = Quaternion::rotation_x(0.0 + move1 * 0.6 + move2 * -0.6)
|
||||
* Quaternion::rotation_y(0.0 + move1 * 0.0 + move2 * 0.0)
|
||||
* Quaternion::rotation_z(0.0 + move1 * 1.5 + move2 * -2.5);
|
||||
next.head.orientation = Quaternion::rotation_z(0.0 + move1 * -1.5 + move2 * 2.5);
|
||||
},
|
||||
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::Pick) => {
|
||||
|
@ -41,6 +41,13 @@ impl Animation for BetaAnimation {
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_z(0.0);
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
@ -55,6 +55,13 @@ impl Animation for ChargeswingAnimation {
|
||||
_ => (0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Charge | StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
let pullback = 1.0 - movement3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
|
@ -42,6 +42,13 @@ impl Animation for DashAnimation {
|
||||
Some(StageSection::Recover) => (1.1, 1.0, 1.0, anim_time.powi(4)),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
let pullback = 1.0 - move4;
|
||||
let move1 = movement1 * pullback;
|
||||
let move2 = movement2 * pullback;
|
||||
|
@ -26,6 +26,8 @@ impl Animation for GlidingAnimation {
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
next.glider_trails = true;
|
||||
|
||||
let speednorm = velocity.magnitude().min(50.0) / 50.0;
|
||||
let slow = (acc_vel * 0.5).sin();
|
||||
|
||||
|
@ -42,6 +42,13 @@ impl Animation for LeapAnimation {
|
||||
Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time.powf(0.75)),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Movement | StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
let pullback = 1.0 - move4;
|
||||
let move1 = movement1 * pullback;
|
||||
let move2 = movement2 * pullback;
|
||||
|
@ -49,7 +49,7 @@ pub use self::{
|
||||
stunned::StunnedAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation,
|
||||
talk::TalkAnimation, wallrun::WallrunAnimation, wield::WieldAnimation,
|
||||
};
|
||||
use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton};
|
||||
use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton, TrailSource};
|
||||
use common::comp;
|
||||
use core::{convert::TryFrom, f32::consts::PI};
|
||||
|
||||
@ -78,6 +78,10 @@ skeleton_impls!(struct CharacterSkeleton {
|
||||
control_r,
|
||||
:: // Begin non-bone fields
|
||||
holding_lantern: bool,
|
||||
main_weapon_trail: bool,
|
||||
off_weapon_trail: bool,
|
||||
// Cannot exist at same time as weapon trails. Since gliding and attacking are mutually exclusive, should never be a concern.
|
||||
glider_trails: bool,
|
||||
});
|
||||
|
||||
impl CharacterSkeleton {
|
||||
@ -125,6 +129,9 @@ impl Skeleton for CharacterSkeleton {
|
||||
} else {
|
||||
shorts_mat
|
||||
} * Mat4::<f32>::from(self.lantern);
|
||||
let main_mat = control_l_mat * Mat4::<f32>::from(self.main);
|
||||
let second_mat = control_r_mat * Mat4::<f32>::from(self.second);
|
||||
let glider_mat = chest_mat * Mat4::<f32>::from(self.glider);
|
||||
|
||||
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
|
||||
make_bone(head_mat),
|
||||
@ -138,13 +145,14 @@ impl Skeleton for CharacterSkeleton {
|
||||
make_bone(torso_mat * Mat4::<f32>::from(self.foot_r)),
|
||||
make_bone(chest_mat * Mat4::<f32>::from(self.shoulder_l)),
|
||||
make_bone(chest_mat * Mat4::<f32>::from(self.shoulder_r)),
|
||||
make_bone(chest_mat * Mat4::<f32>::from(self.glider)),
|
||||
make_bone(control_l_mat * Mat4::<f32>::from(self.main)),
|
||||
make_bone(control_r_mat * Mat4::<f32>::from(self.second)),
|
||||
make_bone(glider_mat),
|
||||
make_bone(main_mat),
|
||||
make_bone(second_mat),
|
||||
make_bone(lantern_mat),
|
||||
// FIXME: Should this be control_l_mat?
|
||||
make_bone(control_mat * hand_l_mat * Mat4::<f32>::from(self.hold)),
|
||||
];
|
||||
let weapon_trails = self.main_weapon_trail || self.off_weapon_trail;
|
||||
Offsets {
|
||||
lantern: Some((lantern_mat * Vec4::new(0.0, 0.5, -6.0, 1.0)).xyz()),
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
@ -155,6 +163,20 @@ impl Skeleton for CharacterSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: if weapon_trails {
|
||||
self.main_weapon_trail
|
||||
.then_some((main_mat, TrailSource::Weapon))
|
||||
} else {
|
||||
self.glider_trails
|
||||
.then_some((glider_mat, TrailSource::GliderLeft))
|
||||
},
|
||||
secondary_trail_mat: if weapon_trails {
|
||||
self.off_weapon_trail
|
||||
.then_some((second_mat, TrailSource::Weapon))
|
||||
} else {
|
||||
self.glider_trails
|
||||
.then_some((glider_mat, TrailSource::GliderRight))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,13 @@ impl Animation for ShockwaveAnimation {
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2);
|
||||
|
@ -50,6 +50,13 @@ impl Animation for SpinAnimation {
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
match ability_info.and_then(|a| a.tool) {
|
||||
Some(ToolKind::Sword) => {
|
||||
next.head.position = Vec3::new(
|
||||
|
@ -39,10 +39,18 @@ impl Animation for SpinMeleeAnimation {
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(2.0)),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
let move1 = movement1 * pullback;
|
||||
let move2 = movement2 * pullback;
|
||||
let mut next = (*skeleton).clone();
|
||||
if matches!(
|
||||
stage_section,
|
||||
Some(StageSection::Action | StageSection::Recover)
|
||||
) {
|
||||
next.main_weapon_trail = true;
|
||||
next.off_weapon_trail = true;
|
||||
}
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_z(0.0);
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
@ -81,6 +81,8 @@ impl Skeleton for DragonSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ impl Skeleton for FishMediumSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ impl Skeleton for FishSmallSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ impl Skeleton for FixtureSkeleton {
|
||||
Offsets {
|
||||
lantern: None,
|
||||
mount_bone: Transform::default(),
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ impl Skeleton for GolemSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ impl Skeleton for ItemDropSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(generic_associated_types, bool_to_option)]
|
||||
#![allow(incomplete_features)]
|
||||
#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))]
|
||||
compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once");
|
||||
@ -69,6 +69,7 @@ pub mod vek;
|
||||
|
||||
use self::vek::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use common::comp::tool::ToolKind;
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
use {
|
||||
lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc, std::sync::Mutex,
|
||||
@ -103,6 +104,50 @@ pub fn init() { lazy_static::initialize(&LIB); }
|
||||
pub struct Offsets {
|
||||
pub lantern: Option<Vec3<f32>>,
|
||||
pub mount_bone: Transform<f32, f32, f32>,
|
||||
pub primary_trail_mat: Option<(Mat4<f32>, TrailSource)>,
|
||||
pub secondary_trail_mat: Option<(Mat4<f32>, TrailSource)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TrailSource {
|
||||
Weapon,
|
||||
GliderLeft,
|
||||
GliderRight,
|
||||
}
|
||||
|
||||
impl TrailSource {
|
||||
pub fn relative_offsets(&self, tool: Option<ToolKind>) -> (Vec4<f32>, Vec4<f32>) {
|
||||
// Offsets
|
||||
const GLIDER_VERT: f32 = 5.0;
|
||||
const GLIDER_HORIZ: f32 = 15.0;
|
||||
// Trail width
|
||||
const GLIDER_WIDTH: f32 = 1.0;
|
||||
|
||||
match self {
|
||||
Self::Weapon => {
|
||||
let lengths = match tool {
|
||||
Some(ToolKind::Sword) => (0.0, 29.25),
|
||||
Some(ToolKind::Axe) => (10.0, 19.25),
|
||||
Some(ToolKind::Hammer) => (10.0, 19.25),
|
||||
Some(ToolKind::Staff) => (10.0, 19.25),
|
||||
Some(ToolKind::Sceptre) => (10.0, 19.25),
|
||||
_ => (0.0, 0.0),
|
||||
};
|
||||
(
|
||||
Vec4::new(0.0, 0.0, lengths.0, 1.0),
|
||||
Vec4::new(0.0, 0.0, lengths.1, 1.0),
|
||||
)
|
||||
},
|
||||
Self::GliderLeft => (
|
||||
Vec4::new(GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0),
|
||||
Vec4::new(GLIDER_HORIZ + GLIDER_WIDTH, 0.0, GLIDER_VERT, 1.0),
|
||||
),
|
||||
Self::GliderRight => (
|
||||
Vec4::new(-GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0),
|
||||
Vec4::new(-(GLIDER_HORIZ + GLIDER_WIDTH), 0.0, GLIDER_VERT, 1.0),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Skeleton: Send + Sync + 'static {
|
||||
|
@ -49,6 +49,8 @@ impl Skeleton for ObjectSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,8 @@ impl Skeleton for QuadrupedLowSkeleton {
|
||||
orientation: mount_orientation,
|
||||
scale: Vec3::one(),
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,8 @@ impl Skeleton for QuadrupedMediumSkeleton {
|
||||
orientation: mount_orientation,
|
||||
scale: Vec3::one(),
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,8 @@ impl Skeleton for QuadrupedSmallSkeleton {
|
||||
orientation: mount_orientation,
|
||||
scale: Vec3::one(),
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ impl Skeleton for ShipSkeleton {
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ impl Skeleton for TheropodSkeleton {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ fn maps_basic_melee() {
|
||||
ability_info: empty_ability_info(),
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: states::utils::StageSection::Buildup,
|
||||
stage_section: states::utils::StageSection::Action,
|
||||
exhausted: false,
|
||||
}),
|
||||
&PreviousEntityState {
|
||||
@ -104,7 +104,10 @@ fn maps_basic_melee() {
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
SfxEvent::Attack(CharacterAbilityType::BasicMelee, ToolKind::Axe)
|
||||
SfxEvent::Attack(
|
||||
CharacterAbilityType::BasicMelee(states::utils::StageSection::Action),
|
||||
ToolKind::Axe
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,8 @@ widget_ids! {
|
||||
particles_label,
|
||||
lossy_terrain_compression_button,
|
||||
lossy_terrain_compression_label,
|
||||
weapon_trails_button,
|
||||
weapon_trails_label,
|
||||
//
|
||||
fullscreen_button,
|
||||
fullscreen_label,
|
||||
@ -1164,6 +1166,31 @@ impl<'a> Widget for Video<'a> {
|
||||
));
|
||||
}
|
||||
|
||||
// Weapon trails
|
||||
Text::new(self.localized_strings.get("hud.settings.weapon_trails"))
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.right_from(state.ids.lossy_terrain_compression_label, 64.0)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.weapon_trails_label, ui);
|
||||
|
||||
let weapon_trails_enabled = ToggleButton::new(
|
||||
self.global_state.settings.graphics.weapon_trails_enabled,
|
||||
self.imgs.checkbox,
|
||||
self.imgs.checkbox_checked,
|
||||
)
|
||||
.w_h(18.0, 18.0)
|
||||
.right_from(state.ids.weapon_trails_label, 10.0)
|
||||
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||
.set(state.ids.weapon_trails_button, ui);
|
||||
|
||||
if self.global_state.settings.graphics.weapon_trails_enabled != weapon_trails_enabled {
|
||||
events.push(GraphicsChange::ToggleWeaponTrailsEnabled(
|
||||
weapon_trails_enabled,
|
||||
));
|
||||
}
|
||||
|
||||
// Resolution
|
||||
let resolutions: Vec<[u16; 2]> = state
|
||||
.video_modes
|
||||
|
@ -9,7 +9,8 @@
|
||||
drain_filter,
|
||||
once_cell,
|
||||
trait_alias,
|
||||
option_get_or_insert_default
|
||||
option_get_or_insert_default,
|
||||
map_try_insert
|
||||
)]
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
|
@ -109,6 +109,10 @@ impl<V: Vertex> Mesh<V> {
|
||||
pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<V> {
|
||||
self.verts[vertex_range].iter_mut()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { self.verts.len() }
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
}
|
||||
|
||||
impl<V: Vertex> IntoIterator for Mesh<V> {
|
||||
|
@ -36,6 +36,7 @@ pub use self::{
|
||||
Vertex as SpriteVertex, VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE,
|
||||
},
|
||||
terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
|
||||
trail::Vertex as TrailVertex,
|
||||
ui::{
|
||||
create_quad as create_ui_quad,
|
||||
create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri,
|
||||
@ -48,7 +49,7 @@ pub use self::{
|
||||
drawer::{
|
||||
DebugDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer,
|
||||
PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer,
|
||||
TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
|
||||
TerrainShadowDrawer, ThirdPassDrawer, TrailDrawer, UiDrawer,
|
||||
},
|
||||
ColLightInfo, Renderer,
|
||||
},
|
||||
|
@ -180,7 +180,22 @@ impl CloudsPipeline {
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilState {
|
||||
front: wgpu::StencilFaceState::IGNORE,
|
||||
back: wgpu::StencilFaceState::IGNORE,
|
||||
read_mask: !0,
|
||||
write_mask: 0,
|
||||
},
|
||||
bias: wgpu::DepthBiasState {
|
||||
constant: 0,
|
||||
slope_scale: 0.0,
|
||||
clamp: 0.0,
|
||||
},
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: samples,
|
||||
mask: !0,
|
||||
|
@ -11,6 +11,7 @@ pub mod shadow;
|
||||
pub mod skybox;
|
||||
pub mod sprite;
|
||||
pub mod terrain;
|
||||
pub mod trail;
|
||||
pub mod ui;
|
||||
|
||||
use super::{Consts, Texture};
|
||||
|
144
voxygen/src/render/pipelines/trail.rs
Normal file
144
voxygen/src/render/pipelines/trail.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, Vertex as VertexTrait};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use std::{
|
||||
mem,
|
||||
ops::{Add, Mul},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod, PartialEq)]
|
||||
pub struct Vertex {
|
||||
pub pos: [f32; 3],
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Float32x3];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: Self::STRIDE,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &ATTRIBUTES,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self { Self { pos: [0.0; 3] } }
|
||||
}
|
||||
|
||||
impl Mul<f32> for Vertex {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, val: f32) -> Self::Output {
|
||||
Self {
|
||||
pos: self.pos.map(|a| a * val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Vertex> for Vertex {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
Self {
|
||||
pos: self.pos.zip(other.pos).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VertexTrait for Vertex {
|
||||
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint16);
|
||||
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||
}
|
||||
|
||||
pub struct TrailPipeline {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl TrailPipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
vs_module: &wgpu::ShaderModule,
|
||||
fs_module: &wgpu::ShaderModule,
|
||||
global_layout: &GlobalsLayouts,
|
||||
aa_mode: AaMode,
|
||||
) -> Self {
|
||||
common_base::span!(_guard, "TrailPipeline::new");
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Trail pipeline layout"),
|
||||
push_constant_ranges: &[],
|
||||
bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures],
|
||||
});
|
||||
|
||||
let samples = match aa_mode {
|
||||
AaMode::None | AaMode::Fxaa => 1,
|
||||
AaMode::MsaaX4 => 4,
|
||||
AaMode::MsaaX8 => 8,
|
||||
AaMode::MsaaX16 => 16,
|
||||
};
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Trail pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[Vertex::desc()],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
clamp_depth: false,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::GreaterEqual,
|
||||
stencil: wgpu::StencilState {
|
||||
front: wgpu::StencilFaceState::IGNORE,
|
||||
back: wgpu::StencilFaceState::IGNORE,
|
||||
read_mask: !0,
|
||||
write_mask: 0,
|
||||
},
|
||||
bias: wgpu::DepthBiasState {
|
||||
constant: 0,
|
||||
slope_scale: 0.0,
|
||||
clamp: 0.0,
|
||||
},
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: samples,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
// TODO: use a constant and/or pass in this format on pipeline construction
|
||||
format: wgpu::TextureFormat::Rgba16Float,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::One,
|
||||
dst_factor: wgpu::BlendFactor::One,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
}),
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
}),
|
||||
});
|
||||
|
||||
Self {
|
||||
pipeline: render_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
@ -1232,6 +1232,7 @@ impl Renderer {
|
||||
|
||||
/// Create a new dynamic model with the specified size.
|
||||
pub fn create_dynamic_model<V: Vertex>(&mut self, size: usize) -> DynamicModel<V> {
|
||||
self.ensure_sufficient_index_length::<V>(size);
|
||||
DynamicModel::new(&self.device, size)
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use super::{
|
||||
model::{DynamicModel, Model, SubModel},
|
||||
pipelines::{
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox,
|
||||
sprite, terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
sprite, terrain, trail, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
},
|
||||
},
|
||||
Renderer, ShadowMap, ShadowMapRenderer,
|
||||
@ -214,8 +214,7 @@ impl<'frame> Drawer<'frame> {
|
||||
|
||||
/// Returns None if the clouds pipeline is not available
|
||||
pub fn second_pass(&mut self) -> Option<SecondPassDrawer> {
|
||||
let pipeline = &self.borrow.pipelines.all()?.clouds;
|
||||
|
||||
let pipelines = &self.borrow.pipelines.all()?;
|
||||
let encoder = self.encoder.as_mut().unwrap();
|
||||
let device = self.borrow.device;
|
||||
let mut render_pass =
|
||||
@ -229,7 +228,11 @@ impl<'frame> Drawer<'frame> {
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.borrow.views.tgt_depth,
|
||||
depth_ops: None,
|
||||
stencil_ops: None,
|
||||
}),
|
||||
});
|
||||
|
||||
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
|
||||
@ -237,7 +240,8 @@ impl<'frame> Drawer<'frame> {
|
||||
Some(SecondPassDrawer {
|
||||
render_pass,
|
||||
borrow: &self.borrow,
|
||||
pipeline,
|
||||
clouds_pipeline: &pipelines.clouds,
|
||||
trail_pipeline: &pipelines.trail,
|
||||
})
|
||||
}
|
||||
|
||||
@ -603,6 +607,7 @@ impl<'frame> Drop for Drawer<'frame> {
|
||||
}
|
||||
|
||||
// Shadow pass
|
||||
#[must_use]
|
||||
pub struct ShadowPassDrawer<'pass> {
|
||||
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
|
||||
borrow: &'pass RendererBorrow<'pass>,
|
||||
@ -633,6 +638,7 @@ impl<'pass> ShadowPassDrawer<'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
@ -650,6 +656,7 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct TerrainShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
@ -668,6 +675,7 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
|
||||
// First pass
|
||||
#[must_use]
|
||||
pub struct FirstPassDrawer<'pass> {
|
||||
pub(super) render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
|
||||
borrow: &'pass RendererBorrow<'pass>,
|
||||
@ -766,6 +774,7 @@ impl<'pass> FirstPassDrawer<'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
shadows: &'pass ShadowTexturesBindGroup,
|
||||
@ -791,6 +800,8 @@ impl<'pass_ref, 'pass: 'pass_ref> Drop for DebugDrawer<'pass_ref, 'pass> {
|
||||
.set_bind_group(1, &self.shadows.bind_group, &[]);
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
@ -812,6 +823,7 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
col_lights: Option<&'pass_ref Arc<ColLights<terrain::Locals>>>,
|
||||
@ -842,6 +854,7 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct ParticleDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
@ -863,6 +876,7 @@ impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
globals: &'pass GlobalsBindGroup,
|
||||
@ -895,6 +909,7 @@ impl<'pass_ref, 'pass: 'pass_ref> Drop for SpriteDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct FluidDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
@ -913,22 +928,52 @@ impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> {
|
||||
}
|
||||
|
||||
// Second pass: clouds
|
||||
#[must_use]
|
||||
pub struct SecondPassDrawer<'pass> {
|
||||
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
|
||||
borrow: &'pass RendererBorrow<'pass>,
|
||||
pipeline: &'pass clouds::CloudsPipeline,
|
||||
clouds_pipeline: &'pass clouds::CloudsPipeline,
|
||||
trail_pipeline: &'pass trail::TrailPipeline,
|
||||
}
|
||||
|
||||
impl<'pass> SecondPassDrawer<'pass> {
|
||||
pub fn draw_clouds(&mut self) {
|
||||
self.render_pass.set_pipeline(&self.pipeline.pipeline);
|
||||
self.render_pass
|
||||
.set_pipeline(&self.clouds_pipeline.pipeline);
|
||||
self.render_pass
|
||||
.set_bind_group(1, &self.borrow.locals.clouds_bind.bind_group, &[]);
|
||||
self.render_pass.draw(0..3, 0..1);
|
||||
}
|
||||
|
||||
pub fn draw_trails(&mut self) -> Option<TrailDrawer<'_, 'pass>> {
|
||||
let shadow = &self.borrow.shadow?;
|
||||
|
||||
let mut render_pass = self.render_pass.scope("trails", self.borrow.device);
|
||||
|
||||
render_pass.set_pipeline(&self.trail_pipeline.pipeline);
|
||||
set_quad_index_buffer::<trail::Vertex>(&mut render_pass, self.borrow);
|
||||
|
||||
render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
|
||||
|
||||
Some(TrailDrawer { render_pass })
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct TrailDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
|
||||
impl<'pass_ref, 'pass: 'pass_ref> TrailDrawer<'pass_ref, 'pass> {
|
||||
pub fn draw(&mut self, submodel: SubModel<'pass, trail::Vertex>) {
|
||||
self.render_pass.set_vertex_buffer(0, submodel.buf());
|
||||
self.render_pass
|
||||
.draw_indexed(0..submodel.len() / 4 * 6, 0, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Third pass: postprocess + ui
|
||||
#[must_use]
|
||||
pub struct ThirdPassDrawer<'pass> {
|
||||
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
|
||||
borrow: &'pass RendererBorrow<'pass>,
|
||||
@ -961,10 +1006,12 @@ impl<'pass> ThirdPassDrawer<'pass> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct UiDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct PreparedUiDrawer<'pass_ref, 'pass: 'pass_ref> {
|
||||
render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
super::{
|
||||
pipelines::{
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow,
|
||||
skybox, sprite, terrain, ui,
|
||||
skybox, sprite, terrain, trail, ui,
|
||||
},
|
||||
AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError,
|
||||
ShadowMode,
|
||||
@ -20,6 +20,7 @@ pub struct Pipelines {
|
||||
pub fluid: fluid::FluidPipeline,
|
||||
pub lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
pub particle: particle::ParticlePipeline,
|
||||
pub trail: trail::TrailPipeline,
|
||||
pub clouds: clouds::CloudsPipeline,
|
||||
pub bloom: Option<bloom::BloomPipelines>,
|
||||
pub postprocess: postprocess::PostProcessPipeline,
|
||||
@ -40,6 +41,7 @@ pub struct IngamePipelines {
|
||||
fluid: fluid::FluidPipeline,
|
||||
lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
particle: particle::ParticlePipeline,
|
||||
trail: trail::TrailPipeline,
|
||||
clouds: clouds::CloudsPipeline,
|
||||
pub bloom: Option<bloom::BloomPipelines>,
|
||||
postprocess: postprocess::PostProcessPipeline,
|
||||
@ -76,6 +78,7 @@ impl Pipelines {
|
||||
fluid: ingame.fluid,
|
||||
lod_terrain: ingame.lod_terrain,
|
||||
particle: ingame.particle,
|
||||
trail: ingame.trail,
|
||||
clouds: ingame.clouds,
|
||||
bloom: ingame.bloom,
|
||||
postprocess: ingame.postprocess,
|
||||
@ -105,6 +108,8 @@ struct ShaderModules {
|
||||
sprite_frag: wgpu::ShaderModule,
|
||||
particle_vert: wgpu::ShaderModule,
|
||||
particle_frag: wgpu::ShaderModule,
|
||||
trail_vert: wgpu::ShaderModule,
|
||||
trail_frag: wgpu::ShaderModule,
|
||||
ui_vert: wgpu::ShaderModule,
|
||||
ui_frag: wgpu::ShaderModule,
|
||||
lod_terrain_vert: wgpu::ShaderModule,
|
||||
@ -290,6 +295,8 @@ impl ShaderModules {
|
||||
sprite_frag: create_shader("sprite-frag", ShaderKind::Fragment)?,
|
||||
particle_vert: create_shader("particle-vert", ShaderKind::Vertex)?,
|
||||
particle_frag: create_shader("particle-frag", ShaderKind::Fragment)?,
|
||||
trail_vert: create_shader("trail-vert", ShaderKind::Vertex)?,
|
||||
trail_frag: create_shader("trail-frag", ShaderKind::Fragment)?,
|
||||
ui_vert: create_shader("ui-vert", ShaderKind::Vertex)?,
|
||||
ui_frag: create_shader("ui-frag", ShaderKind::Fragment)?,
|
||||
lod_terrain_vert: create_shader("lod-terrain-vert", ShaderKind::Vertex)?,
|
||||
@ -407,7 +414,8 @@ fn create_interface_pipelines(
|
||||
fn create_ingame_and_shadow_pipelines(
|
||||
needs: PipelineNeeds,
|
||||
pool: &rayon::ThreadPool,
|
||||
tasks: [Task; 14],
|
||||
// TODO: Reduce the boilerplate in this file
|
||||
tasks: [Task; 15],
|
||||
) -> IngameAndShadowPipelines {
|
||||
prof_span!(_guard, "create_ingame_and_shadow_pipelines");
|
||||
|
||||
@ -427,6 +435,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
fluid_task,
|
||||
sprite_task,
|
||||
particle_task,
|
||||
trail_task,
|
||||
lod_terrain_task,
|
||||
clouds_task,
|
||||
bloom_task,
|
||||
@ -552,6 +561,21 @@ fn create_ingame_and_shadow_pipelines(
|
||||
"particle pipeline creation",
|
||||
)
|
||||
};
|
||||
// Pipeline for rendering weapon trails
|
||||
let create_trail = || {
|
||||
trail_task.run(
|
||||
|| {
|
||||
trail::TrailPipeline::new(
|
||||
device,
|
||||
&shaders.trail_vert,
|
||||
&shaders.trail_frag,
|
||||
&layouts.global,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"trail pipeline creation",
|
||||
)
|
||||
};
|
||||
// Pipeline for rendering terrain
|
||||
let create_lod_terrain = || {
|
||||
lod_terrain_task.run(
|
||||
@ -696,7 +720,11 @@ fn create_ingame_and_shadow_pipelines(
|
||||
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
|
||||
let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
|
||||
let j3 = || pool.join(create_sprite, create_particle);
|
||||
let j4 = || pool.join(create_lod_terrain, create_clouds);
|
||||
let j4 = || {
|
||||
pool.join(create_lod_terrain, || {
|
||||
pool.join(create_clouds, create_trail)
|
||||
})
|
||||
};
|
||||
let j5 = || pool.join(create_postprocess, create_point_shadow);
|
||||
let j6 = || {
|
||||
pool.join(
|
||||
@ -709,7 +737,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
let (
|
||||
(
|
||||
((debug, (skybox, figure)), (terrain, (fluid, bloom))),
|
||||
((sprite, particle), (lod_terrain, clouds)),
|
||||
((sprite, particle), (lod_terrain, (clouds, trail))),
|
||||
),
|
||||
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
|
||||
) = pool.join(
|
||||
@ -724,6 +752,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
fluid,
|
||||
lod_terrain,
|
||||
particle,
|
||||
trail,
|
||||
clouds,
|
||||
bloom,
|
||||
postprocess,
|
||||
|
@ -63,6 +63,8 @@ impl assets::Compound for Shaders {
|
||||
"sprite-frag",
|
||||
"particle-vert",
|
||||
"particle-frag",
|
||||
"trail-vert",
|
||||
"trail-frag",
|
||||
"ui-vert",
|
||||
"ui-frag",
|
||||
"lod-terrain-vert",
|
||||
|
@ -9,15 +9,15 @@ pub use volume::VolumeKey;
|
||||
use crate::{
|
||||
ecs::comp::Interpolated,
|
||||
render::{
|
||||
pipelines::{self, ColLights},
|
||||
pipelines::{self, trail, ColLights},
|
||||
ColLightInfo, FigureBoneData, FigureDrawer, FigureLocals, FigureModel, FigureShadowDrawer,
|
||||
Mesh, RenderError, Renderer, SubModel, TerrainVertex,
|
||||
Mesh, Quad, RenderError, Renderer, SubModel, TerrainVertex,
|
||||
},
|
||||
scene::{
|
||||
camera::{Camera, CameraMode, Dependents},
|
||||
math,
|
||||
terrain::Terrain,
|
||||
SceneData,
|
||||
SceneData, TrailMgr,
|
||||
},
|
||||
};
|
||||
use anim::{
|
||||
@ -571,6 +571,7 @@ impl FigureMgr {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
trail_mgr: &mut TrailMgr,
|
||||
scene_data: &SceneData,
|
||||
// Visible chunk data.
|
||||
visible_psr_bounds: math::Aabr<f32>,
|
||||
@ -814,7 +815,7 @@ impl FigureMgr {
|
||||
.unwrap_or_else(|| vek::Rgba::broadcast(1.0))
|
||||
// Highlight targeted collectible entities
|
||||
* if item.is_some() && scene_data.target_entity.map_or(false, |e| e == entity) {
|
||||
vek::Rgba::new(5.0, 5.0, 5.0, 1.0)
|
||||
vek::Rgba::new(1.5, 1.5, 1.5, 1.0)
|
||||
} else {
|
||||
vek::Rgba::one()
|
||||
};
|
||||
@ -854,12 +855,17 @@ impl FigureMgr {
|
||||
|
||||
let body = *body;
|
||||
|
||||
// Only use trail manager when trails are enabled
|
||||
let trail_mgr = scene_data.weapon_trails_enabled.then(|| &mut *trail_mgr);
|
||||
|
||||
let common_params = FigureUpdateCommonParameters {
|
||||
entity: Some(entity),
|
||||
pos: pos.0,
|
||||
ori,
|
||||
scale,
|
||||
mount_transform_pos,
|
||||
body: Some(body),
|
||||
tools: (active_tool_kind, second_tool_kind),
|
||||
col,
|
||||
dt,
|
||||
_lpindex: lpindex,
|
||||
@ -1759,6 +1765,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -1958,6 +1965,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -2274,6 +2282,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -2631,6 +2640,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -2734,6 +2744,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -2816,6 +2827,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -3342,6 +3354,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -3428,6 +3441,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -3606,6 +3620,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -3897,6 +3912,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -4219,6 +4235,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -4301,6 +4318,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -4922,6 +4940,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -5162,6 +5181,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -5289,6 +5309,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -5351,6 +5372,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -5385,6 +5407,7 @@ impl FigureMgr {
|
||||
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -5478,6 +5501,7 @@ impl FigureMgr {
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
trail_mgr,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
@ -6170,6 +6194,8 @@ impl FigureColLights {
|
||||
|
||||
pub struct FigureStateMeta {
|
||||
lantern_offset: Option<anim::vek::Vec3<f32>>,
|
||||
main_abs_trail_points: Option<(anim::vek::Vec3<f32>, anim::vek::Vec3<f32>)>,
|
||||
off_abs_trail_points: Option<(anim::vek::Vec3<f32>, anim::vek::Vec3<f32>)>,
|
||||
// Animation to be applied to rider of this entity
|
||||
mount_transform: anim::vek::Transform<f32, f32, f32>,
|
||||
// Contains the position of this figure or if it is a rider it will contain the mount's
|
||||
@ -6218,11 +6244,13 @@ impl<S> DerefMut for FigureState<S> {
|
||||
/// Parameters that don't depend on the body variant or animation results and
|
||||
/// are also not mutable
|
||||
pub struct FigureUpdateCommonParameters<'a> {
|
||||
pub entity: Option<EcsEntity>,
|
||||
pub pos: anim::vek::Vec3<f32>,
|
||||
pub ori: anim::vek::Quaternion<f32>,
|
||||
pub scale: f32,
|
||||
pub mount_transform_pos: Option<(anim::vek::Transform<f32, f32, f32>, anim::vek::Vec3<f32>)>,
|
||||
pub body: Option<Body>,
|
||||
pub tools: (Option<ToolKind>, Option<ToolKind>),
|
||||
pub col: vek::Rgba<f32>,
|
||||
pub dt: f32,
|
||||
// TODO: evaluate unused variable
|
||||
@ -6245,6 +6273,8 @@ impl<S: Skeleton> FigureState<S> {
|
||||
Self {
|
||||
meta: FigureStateMeta {
|
||||
lantern_offset: offsets.lantern,
|
||||
main_abs_trail_points: None,
|
||||
off_abs_trail_points: None,
|
||||
mount_transform: offsets.mount_bone,
|
||||
mount_world_pos: anim::vek::Vec3::zero(),
|
||||
state_time: 0.0,
|
||||
@ -6266,13 +6296,16 @@ impl<S: Skeleton> FigureState<S> {
|
||||
pub fn update<const N: usize>(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
trail_mgr: Option<&mut TrailMgr>,
|
||||
buf: &mut [anim::FigureBoneData; anim::MAX_BONE_COUNT],
|
||||
FigureUpdateCommonParameters {
|
||||
entity,
|
||||
pos,
|
||||
ori,
|
||||
scale,
|
||||
mount_transform_pos,
|
||||
body,
|
||||
tools,
|
||||
col,
|
||||
dt,
|
||||
_lpindex,
|
||||
@ -6410,6 +6443,55 @@ impl<S: Skeleton> FigureState<S> {
|
||||
|
||||
renderer.update_consts(&mut self.meta.bound.1, &new_bone_consts[0..S::BONE_COUNT]);
|
||||
self.lantern_offset = offsets.lantern;
|
||||
// Handle weapon trails
|
||||
fn handle_weapon_trails(
|
||||
trail_mgr: &mut TrailMgr,
|
||||
new_weapon_trail_mat: Option<(anim::vek::Mat4<f32>, anim::TrailSource)>,
|
||||
old_abs_trail_points: &mut Option<(anim::vek::Vec3<f32>, anim::vek::Vec3<f32>)>,
|
||||
entity: EcsEntity,
|
||||
is_main_weapon: bool,
|
||||
pos: anim::vek::Vec3<f32>,
|
||||
tool: Option<ToolKind>,
|
||||
) {
|
||||
let weapon_offsets = new_weapon_trail_mat.map(|(mat, trail)| {
|
||||
let (trail_start, trail_end) = trail.relative_offsets(tool);
|
||||
((mat * trail_start).xyz(), (mat * trail_end).xyz())
|
||||
});
|
||||
let new_abs_trail_points = weapon_offsets.map(|(a, b)| (a + pos, b + pos));
|
||||
if let (Some((p1, p2)), Some((p4, p3))) = (&old_abs_trail_points, new_abs_trail_points)
|
||||
{
|
||||
let trail_mgr_offset = trail_mgr.offset();
|
||||
let quad_mesh = trail_mgr.entity_mesh_or_insert(entity, is_main_weapon);
|
||||
let vertex = |p: anim::vek::Vec3<f32>| trail::Vertex {
|
||||
pos: p.into_array(),
|
||||
};
|
||||
let quad = Quad::new(vertex(*p1), vertex(*p2), vertex(p3), vertex(p4));
|
||||
quad_mesh.replace_quad(trail_mgr_offset * 4, quad);
|
||||
}
|
||||
*old_abs_trail_points = new_abs_trail_points;
|
||||
}
|
||||
|
||||
if let (Some(trail_mgr), Some(entity)) = (trail_mgr, entity) {
|
||||
handle_weapon_trails(
|
||||
trail_mgr,
|
||||
offsets.primary_trail_mat,
|
||||
&mut self.main_abs_trail_points,
|
||||
*entity,
|
||||
true,
|
||||
*pos,
|
||||
tools.0,
|
||||
);
|
||||
handle_weapon_trails(
|
||||
trail_mgr,
|
||||
offsets.secondary_trail_mat,
|
||||
&mut self.off_abs_trail_points,
|
||||
*entity,
|
||||
false,
|
||||
*pos,
|
||||
tools.1,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: compute the mount bone only when it is needed
|
||||
self.mount_transform = offsets.mount_bone;
|
||||
self.mount_world_pos = mount_transform_pos.map_or(*pos, |(_, pos)| pos);
|
||||
|
@ -49,6 +49,8 @@ impl anim::Skeleton for VolumeKey {
|
||||
anim::Offsets {
|
||||
lantern: None,
|
||||
mount_bone: anim::vek::Transform::default(),
|
||||
primary_trail_mat: None,
|
||||
secondary_trail_mat: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ pub mod math;
|
||||
pub mod particle;
|
||||
pub mod simple;
|
||||
pub mod terrain;
|
||||
pub mod trail;
|
||||
|
||||
pub use self::{
|
||||
camera::{Camera, CameraMode},
|
||||
@ -14,6 +15,7 @@ pub use self::{
|
||||
lod::Lod,
|
||||
particle::ParticleMgr,
|
||||
terrain::{SpriteRenderContextLazy, Terrain},
|
||||
trail::TrailMgr,
|
||||
};
|
||||
use crate::{
|
||||
audio::{ambient::AmbientMgr, music::MusicMgr, sfx::SfxMgr, AudioFrontend},
|
||||
@ -95,6 +97,7 @@ pub struct Scene {
|
||||
light_data: Vec<Light>,
|
||||
|
||||
particle_mgr: ParticleMgr,
|
||||
trail_mgr: TrailMgr,
|
||||
figure_mgr: FigureMgr,
|
||||
pub sfx_mgr: SfxMgr,
|
||||
music_mgr: MusicMgr,
|
||||
@ -115,6 +118,7 @@ pub struct SceneData<'a> {
|
||||
pub mouse_smoothing: bool,
|
||||
pub sprite_render_distance: f32,
|
||||
pub particles_enabled: bool,
|
||||
pub weapon_trails_enabled: bool,
|
||||
pub figure_lod_render_distance: f32,
|
||||
pub is_aiming: bool,
|
||||
}
|
||||
@ -305,6 +309,7 @@ impl Scene {
|
||||
select_pos: None,
|
||||
light_data: Vec::new(),
|
||||
particle_mgr: ParticleMgr::new(renderer),
|
||||
trail_mgr: TrailMgr::default(),
|
||||
figure_mgr: FigureMgr::new(renderer),
|
||||
sfx_mgr: SfxMgr::default(),
|
||||
music_mgr: MusicMgr::default(),
|
||||
@ -327,6 +332,9 @@ impl Scene {
|
||||
/// Get a reference to the scene's particle manager.
|
||||
pub fn particle_mgr(&self) -> &ParticleMgr { &self.particle_mgr }
|
||||
|
||||
/// Get a reference to the scene's trail manager.
|
||||
pub fn trail_mgr(&self) -> &TrailMgr { &self.trail_mgr }
|
||||
|
||||
/// Get a reference to the scene's figure manager.
|
||||
pub fn figure_mgr(&self) -> &FigureMgr { &self.figure_mgr }
|
||||
|
||||
@ -547,6 +555,9 @@ impl Scene {
|
||||
self.particle_mgr
|
||||
.maintain(renderer, scene_data, &self.terrain, lights);
|
||||
|
||||
// Maintain the trails.
|
||||
self.trail_mgr.maintain(renderer, scene_data);
|
||||
|
||||
// Update light constants
|
||||
lights.extend(
|
||||
(
|
||||
@ -687,6 +698,7 @@ impl Scene {
|
||||
// Maintain the figures.
|
||||
let _figure_bounds = self.figure_mgr.maintain(
|
||||
renderer,
|
||||
&mut self.trail_mgr,
|
||||
scene_data,
|
||||
visible_psr_bounds,
|
||||
&self.camera,
|
||||
|
@ -325,12 +325,6 @@ impl ParticleMgr {
|
||||
Body::Object(object::Body::CampfireLit) => {
|
||||
self.maintain_campfirelit_particles(scene_data, pos, vel)
|
||||
},
|
||||
Body::Object(
|
||||
object::Body::Arrow
|
||||
| object::Body::MultiArrow
|
||||
| object::Body::ArrowSnake
|
||||
| object::Body::ArrowTurret,
|
||||
) => self.maintain_arrow_particles(scene_data, pos, vel),
|
||||
Body::Object(object::Body::BoltFire) => {
|
||||
self.maintain_boltfire_particles(scene_data, pos, vel)
|
||||
},
|
||||
@ -390,33 +384,6 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_arrow_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) {
|
||||
const MIN_SPEED: f32 = 15.0;
|
||||
// Don't emit particles for immobile arrows
|
||||
if vel.map_or(true, |v| v.0.magnitude_squared() < MIN_SPEED.powi(2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
span!(
|
||||
_guard,
|
||||
"arrow_particles",
|
||||
"ParticleMgr::maintain_arrow_particles"
|
||||
);
|
||||
let time = scene_data.state.get_time();
|
||||
let dt = scene_data.state.get_delta_time();
|
||||
|
||||
let count = self.scheduler.heartbeats(Duration::from_millis(2));
|
||||
for i in 0..count {
|
||||
let proportion = i as f32 / count as f32;
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_millis(200),
|
||||
time,
|
||||
ParticleMode::StaticSmoke,
|
||||
pos.0 + vel.map_or(Vec3::zero(), |v| -v.0 * dt * proportion),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_boltfire_particles(
|
||||
&mut self,
|
||||
scene_data: &SceneData,
|
||||
|
@ -148,6 +148,7 @@ impl Scene {
|
||||
.create_figure(renderer, greedy.finalize(), (opaque_mesh, bounds), [range]);
|
||||
let mut buf = [Default::default(); anim::MAX_BONE_COUNT];
|
||||
let common_params = FigureUpdateCommonParameters {
|
||||
entity: None,
|
||||
pos: anim::vek::Vec3::zero(),
|
||||
ori: anim::vek::Quaternion::rotation_from_to_3d(
|
||||
anim::vek::Vec3::unit_y(),
|
||||
@ -156,6 +157,7 @@ impl Scene {
|
||||
scale: 1.0,
|
||||
mount_transform_pos: None,
|
||||
body: None,
|
||||
tools: (None, None),
|
||||
col: Rgba::broadcast(1.0),
|
||||
dt: 15.0, // Want to get there immediately.
|
||||
_lpindex: 0,
|
||||
@ -165,7 +167,15 @@ impl Scene {
|
||||
terrain: None,
|
||||
ground_vel: Vec3::zero(),
|
||||
};
|
||||
state.update(renderer, &mut buf, &common_params, 1.0, Some(&model), ());
|
||||
state.update(
|
||||
renderer,
|
||||
None,
|
||||
&mut buf,
|
||||
&common_params,
|
||||
1.0,
|
||||
Some(&model),
|
||||
(),
|
||||
);
|
||||
(model, state)
|
||||
}),
|
||||
col_lights,
|
||||
@ -330,6 +340,7 @@ impl Scene {
|
||||
.0;
|
||||
let mut buf = [Default::default(); anim::MAX_BONE_COUNT];
|
||||
let common_params = FigureUpdateCommonParameters {
|
||||
entity: None,
|
||||
pos: anim::vek::Vec3::zero(),
|
||||
ori: anim::vek::Quaternion::rotation_from_to_3d(
|
||||
anim::vek::Vec3::unit_y(),
|
||||
@ -338,6 +349,7 @@ impl Scene {
|
||||
scale: 1.0,
|
||||
mount_transform_pos: None,
|
||||
body: None,
|
||||
tools: (None, None),
|
||||
col: Rgba::broadcast(1.0),
|
||||
dt: scene_data.delta_time,
|
||||
_lpindex: 0,
|
||||
@ -348,7 +360,7 @@ impl Scene {
|
||||
ground_vel: Vec3::zero(),
|
||||
};
|
||||
|
||||
figure_state.update(renderer, &mut buf, &common_params, 1.0, model, body);
|
||||
figure_state.update(renderer, None, &mut buf, &common_params, 1.0, model, body);
|
||||
}
|
||||
}
|
||||
|
||||
|
186
voxygen/src/scene/trail.rs
Normal file
186
voxygen/src/scene/trail.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use super::SceneData;
|
||||
use crate::render::{DynamicModel, Mesh, Quad, Renderer, TrailDrawer, TrailVertex};
|
||||
use common::comp::{object, Body, Pos, Vel};
|
||||
use common_base::span;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
use std::collections::HashMap;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||
struct MeshKey {
|
||||
entity: EcsEntity,
|
||||
is_main_weapon: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TrailMgr {
|
||||
/// Meshes for each entity, usize is the last offset tick it was updated
|
||||
entity_meshes: HashMap<MeshKey, (Mesh<TrailVertex>, usize)>,
|
||||
|
||||
/// Position cache for things like projectiles
|
||||
pos_cache: HashMap<EcsEntity, Pos>,
|
||||
|
||||
/// Offset
|
||||
offset: usize,
|
||||
|
||||
/// Dynamic model to upload to GPU
|
||||
dynamic_model: Option<DynamicModel<TrailVertex>>,
|
||||
|
||||
/// Used to create sub model from dynamic model
|
||||
model_len: u32,
|
||||
}
|
||||
|
||||
const TRAIL_DYNAMIC_MODEL_SIZE: usize = 15;
|
||||
const TRAIL_SHRINKAGE: f32 = 0.8;
|
||||
|
||||
impl TrailMgr {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
||||
span!(_guard, "maintain", "TrailMgr::maintain");
|
||||
|
||||
if scene_data.weapon_trails_enabled {
|
||||
// Hack to shove trails in for projectiles
|
||||
let ecs = scene_data.state.ecs();
|
||||
for (entity, body, vel, pos) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<Body>(),
|
||||
&ecs.read_storage::<Vel>(),
|
||||
&ecs.read_storage::<Pos>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
const MIN_SPEED: f32 = 15.0;
|
||||
if vel.0.magnitude_squared() > MIN_SPEED.powi(2)
|
||||
&& matches!(
|
||||
body,
|
||||
Body::Object(
|
||||
object::Body::Arrow
|
||||
| object::Body::MultiArrow
|
||||
| object::Body::ArrowSnake
|
||||
| object::Body::ArrowTurret,
|
||||
)
|
||||
)
|
||||
{
|
||||
let last_pos = *self.pos_cache.entry(entity).or_insert(*pos);
|
||||
let offset = self.offset;
|
||||
let quad_mesh = self.entity_mesh_or_insert(entity, true);
|
||||
const THICKNESS: f32 = 0.05;
|
||||
let p1 = pos.0;
|
||||
let p2 = p1 + Vec3::unit_z() * THICKNESS;
|
||||
let p4 = last_pos.0;
|
||||
let p3 = p4 + Vec3::unit_z() * THICKNESS;
|
||||
let vertex = |p: Vec3<f32>| TrailVertex {
|
||||
pos: p.into_array(),
|
||||
};
|
||||
let quad = Quad::new(vertex(p1), vertex(p2), vertex(p3), vertex(p4));
|
||||
quad_mesh.replace_quad(offset * 4, quad);
|
||||
self.pos_cache.insert(entity, *pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Update offset
|
||||
self.offset = (self.offset + 1) % TRAIL_DYNAMIC_MODEL_SIZE;
|
||||
|
||||
self.entity_meshes.values_mut().for_each(|(mesh, _)| {
|
||||
// TODO: Figure out how to do this in shader files instead
|
||||
// Shrink size of each quad over time
|
||||
let vertices = mesh.vertices_mut_vec();
|
||||
let last_offset =
|
||||
(self.offset + TRAIL_DYNAMIC_MODEL_SIZE - 1) % TRAIL_DYNAMIC_MODEL_SIZE;
|
||||
let next_offset = (self.offset + 1) % TRAIL_DYNAMIC_MODEL_SIZE;
|
||||
for i in 0..TRAIL_DYNAMIC_MODEL_SIZE {
|
||||
// Verts per quad are in b, c, a, d order
|
||||
let [b, c, a, d] = [0, 1, 2, 3].map(|offset| i * 4 + offset);
|
||||
vertices[a] = if i == next_offset {
|
||||
vertices[b]
|
||||
} else {
|
||||
vertices[a] * TRAIL_SHRINKAGE + vertices[b] * (1.0 - TRAIL_SHRINKAGE)
|
||||
};
|
||||
if i != last_offset {
|
||||
// Avoid shrinking edge of most recent quad so that edges of quads align
|
||||
vertices[d] =
|
||||
vertices[d] * TRAIL_SHRINKAGE + vertices[c] * (1.0 - TRAIL_SHRINKAGE);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset quad for each entity mesh at new offset
|
||||
let zero = TrailVertex::zero();
|
||||
mesh.replace_quad(self.offset * 4, Quad::new(zero, zero, zero, zero));
|
||||
});
|
||||
|
||||
// Clear meshes for entities that only have zero quads in mesh
|
||||
self.entity_meshes
|
||||
.retain(|_, (_mesh, last_updated)| *last_updated != self.offset);
|
||||
|
||||
// TODO: as an optimization we can keep this big mesh around between frames and
|
||||
// write directly to it for each entity.
|
||||
// Create big mesh from currently existing meshes that is used to update dynamic
|
||||
// model
|
||||
let mut big_mesh = Mesh::new();
|
||||
self.entity_meshes
|
||||
.values()
|
||||
// If any of the vertices in a mesh are non-zero, upload the entire mesh for the entity
|
||||
.filter(|(mesh, _)| mesh.iter().any(|vert| *vert != TrailVertex::zero()))
|
||||
.for_each(|(mesh, _)| big_mesh.push_mesh(mesh));
|
||||
|
||||
// To avoid empty mesh
|
||||
if big_mesh.is_empty() {
|
||||
let zero = TrailVertex::zero();
|
||||
big_mesh.push_quad(Quad::new(zero, zero, zero, zero));
|
||||
}
|
||||
|
||||
// If dynamic model too small, resize, with room for 10 additional entities to
|
||||
// avoid needing to resize frequently
|
||||
if self.dynamic_model.as_ref().map_or(0, |model| model.len()) < big_mesh.len() {
|
||||
self.dynamic_model = Some(
|
||||
renderer
|
||||
.create_dynamic_model(big_mesh.len() + TRAIL_DYNAMIC_MODEL_SIZE * 4 * 10),
|
||||
);
|
||||
}
|
||||
if let Some(dynamic_model) = &self.dynamic_model {
|
||||
renderer.update_model(dynamic_model, &big_mesh, 0);
|
||||
}
|
||||
self.model_len = big_mesh.len() as u32;
|
||||
} else {
|
||||
self.entity_meshes.clear();
|
||||
// Clear dynamic model to free memory
|
||||
self.dynamic_model = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render<'a>(&'a self, drawer: &mut TrailDrawer<'_, 'a>, scene_data: &SceneData) {
|
||||
span!(_guard, "render", "TrailMgr::render");
|
||||
if scene_data.weapon_trails_enabled {
|
||||
if let Some(dynamic_model) = &self.dynamic_model {
|
||||
drawer.draw(dynamic_model.submodel(0..self.model_len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entity_mesh_or_insert(
|
||||
&mut self,
|
||||
entity: EcsEntity,
|
||||
is_main_weapon: bool,
|
||||
) -> &mut Mesh<TrailVertex> {
|
||||
let key = MeshKey {
|
||||
entity,
|
||||
is_main_weapon,
|
||||
};
|
||||
&mut self
|
||||
.entity_meshes
|
||||
.entry(key)
|
||||
.and_modify(|(_mesh, offset)| *offset = self.offset)
|
||||
.or_insert((Self::default_trail_mesh(), self.offset))
|
||||
.0
|
||||
}
|
||||
|
||||
fn default_trail_mesh() -> Mesh<TrailVertex> {
|
||||
let mut mesh = Mesh::new();
|
||||
let zero = TrailVertex::zero();
|
||||
for _ in 0..TRAIL_DYNAMIC_MODEL_SIZE {
|
||||
mesh.push_quad(Quad::new(zero, zero, zero, zero));
|
||||
}
|
||||
mesh
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize { self.offset }
|
||||
}
|
@ -1474,6 +1474,7 @@ impl PlayState for SessionState {
|
||||
sprite_render_distance: global_state.settings.graphics.sprite_render_distance
|
||||
as f32,
|
||||
particles_enabled: global_state.settings.graphics.particles_enabled,
|
||||
weapon_trails_enabled: global_state.settings.graphics.weapon_trails_enabled,
|
||||
figure_lod_render_distance: global_state
|
||||
.settings
|
||||
.graphics
|
||||
@ -1527,44 +1528,52 @@ impl PlayState for SessionState {
|
||||
fn render<'a>(&'a self, drawer: &mut Drawer<'a>, settings: &Settings) {
|
||||
span!(_guard, "render", "<Session as PlayState>::render");
|
||||
|
||||
let client = self.client.borrow();
|
||||
|
||||
let scene_data = SceneData {
|
||||
client: &client,
|
||||
state: client.state(),
|
||||
player_entity: client.entity(),
|
||||
// Only highlight if interactable
|
||||
target_entity: self.interactable.and_then(Interactable::entity),
|
||||
loaded_distance: client.loaded_distance(),
|
||||
view_distance: client.view_distance().unwrap_or(1),
|
||||
tick: client.get_tick(),
|
||||
gamma: settings.graphics.gamma,
|
||||
exposure: settings.graphics.exposure,
|
||||
ambiance: settings.graphics.ambiance,
|
||||
mouse_smoothing: settings.gameplay.smooth_pan_enable,
|
||||
sprite_render_distance: settings.graphics.sprite_render_distance as f32,
|
||||
figure_lod_render_distance: settings.graphics.figure_lod_render_distance as f32,
|
||||
particles_enabled: settings.graphics.particles_enabled,
|
||||
weapon_trails_enabled: settings.graphics.weapon_trails_enabled,
|
||||
is_aiming: self.is_aiming,
|
||||
};
|
||||
|
||||
// Render world
|
||||
{
|
||||
let client = self.client.borrow();
|
||||
self.scene.render(
|
||||
drawer,
|
||||
client.state(),
|
||||
client.entity(),
|
||||
client.get_tick(),
|
||||
&scene_data,
|
||||
);
|
||||
|
||||
let scene_data = SceneData {
|
||||
client: &client,
|
||||
state: client.state(),
|
||||
player_entity: client.entity(),
|
||||
// Only highlight if interactable
|
||||
target_entity: self.interactable.and_then(Interactable::entity),
|
||||
loaded_distance: client.loaded_distance(),
|
||||
view_distance: client.view_distance().unwrap_or(1),
|
||||
tick: client.get_tick(),
|
||||
gamma: settings.graphics.gamma,
|
||||
exposure: settings.graphics.exposure,
|
||||
ambiance: settings.graphics.ambiance,
|
||||
mouse_smoothing: settings.gameplay.smooth_pan_enable,
|
||||
sprite_render_distance: settings.graphics.sprite_render_distance as f32,
|
||||
figure_lod_render_distance: settings.graphics.figure_lod_render_distance as f32,
|
||||
particles_enabled: settings.graphics.particles_enabled,
|
||||
is_aiming: self.is_aiming,
|
||||
};
|
||||
|
||||
self.scene.render(
|
||||
drawer,
|
||||
client.state(),
|
||||
client.entity(),
|
||||
client.get_tick(),
|
||||
&scene_data,
|
||||
);
|
||||
}
|
||||
|
||||
// Clouds
|
||||
{
|
||||
prof_span!("clouds");
|
||||
if let Some(mut second_pass) = drawer.second_pass() {
|
||||
if let Some(mut second_pass) = drawer.second_pass() {
|
||||
// Clouds
|
||||
{
|
||||
prof_span!("clouds");
|
||||
second_pass.draw_clouds();
|
||||
}
|
||||
// Trails
|
||||
{
|
||||
prof_span!("trails");
|
||||
if let Some(mut trail_drawer) = second_pass.draw_trails() {
|
||||
self.scene
|
||||
.trail_mgr()
|
||||
.render(&mut trail_drawer, &scene_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Bloom (call does nothing if bloom is off)
|
||||
{
|
||||
|
@ -86,6 +86,7 @@ pub enum Graphics {
|
||||
ChangeFullscreenMode(FullScreenSettings),
|
||||
ToggleParticlesEnabled(bool),
|
||||
ToggleLossyTerrainCompression(bool),
|
||||
ToggleWeaponTrailsEnabled(bool),
|
||||
AdjustWindowSize([u16; 2]),
|
||||
|
||||
ResetGraphicsSettings,
|
||||
@ -402,6 +403,9 @@ impl SettingsChange {
|
||||
.borrow_mut()
|
||||
.request_lossy_terrain_compression(lossy_terrain_compression);
|
||||
},
|
||||
Graphics::ToggleWeaponTrailsEnabled(weapon_trails_enabled) => {
|
||||
settings.graphics.weapon_trails_enabled = weapon_trails_enabled;
|
||||
},
|
||||
Graphics::AdjustWindowSize(new_size) => {
|
||||
global_state.window.set_size(new_size.into());
|
||||
settings.graphics.window_size = new_size;
|
||||
|
@ -33,6 +33,7 @@ pub struct GraphicsSettings {
|
||||
pub sprite_render_distance: u32,
|
||||
pub particles_enabled: bool,
|
||||
pub lossy_terrain_compression: bool,
|
||||
pub weapon_trails_enabled: bool,
|
||||
pub figure_lod_render_distance: u32,
|
||||
pub max_fps: Fps,
|
||||
pub max_background_fps: Fps,
|
||||
@ -53,6 +54,7 @@ impl Default for GraphicsSettings {
|
||||
sprite_render_distance: 100,
|
||||
particles_enabled: true,
|
||||
lossy_terrain_compression: false,
|
||||
weapon_trails_enabled: true,
|
||||
figure_lod_render_distance: 300,
|
||||
max_fps: Fps::Max(60),
|
||||
max_background_fps: Fps::Max(30),
|
||||
|
Loading…
Reference in New Issue
Block a user