Use temporal coherence for figure frustum culling, don't process figures if they are not in view frustum

This commit is contained in:
Yusuf Bera Ertan 2019-11-20 02:29:34 +03:00
parent c19c222a90
commit 31d18b3381
4 changed files with 143 additions and 36 deletions

2
Cargo.lock generated
View File

@ -1064,7 +1064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "frustum_culling"
version = "0.1.0"
source = "git+https://gitlab.com/yusdacra/frustum_culling#735aef658c7e5eb0c4d543cacbea2f3e06216438"
source = "git+https://gitlab.com/yusdacra/frustum_culling#7145dbee5eadf4df0cbe5aeda856a0967f47ace4"
[[package]]
name = "fsevent"

View File

@ -169,6 +169,8 @@ impl Scene {
1.0 / 60.0, // TODO: Use actual deltatime here?
1.0,
1.0,
0,
true,
);
}

View File

@ -43,8 +43,6 @@ pub struct FigureMgr {
fish_small_states: HashMap<EcsEntity, FigureState<FishSmallSkeleton>>,
biped_large_states: HashMap<EcsEntity, FigureState<BipedLargeSkeleton>>,
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
figure_count: usize,
visible_figure_count: usize,
}
impl FigureMgr {
@ -61,8 +59,6 @@ impl FigureMgr {
fish_small_states: HashMap::new(),
biped_large_states: HashMap::new(),
object_states: HashMap::new(),
figure_count: 0,
visible_figure_count: 0,
}
}
@ -70,7 +66,7 @@ impl FigureMgr {
self.model_cache.clean(tick);
}
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, camera: &Camera) {
let time = client.state().get_time();
let tick = client.get_tick();
let ecs = client.state().ecs();
@ -82,8 +78,6 @@ impl FigureMgr {
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
self.figure_count = 0;
for (entity, pos, ori, scale, body, character, last_character, stats) in (
&ecs.entities(),
&ecs.read_storage::<Pos>(),
@ -96,7 +90,79 @@ impl FigureMgr {
)
.join()
{
self.figure_count += 1;
// Don't process figures outside the frustum spectrum
let frustum = camera.frustum(client);
let (in_frustum, lpindex) = frustum.test_sphere_coherence(
pos.0.into_array(),
scale.unwrap_or(&Scale(1.0)).0 * 2.0,
match body {
Body::Humanoid(_) => {
self.character_states.get(&entity).map(|state| state.lpindex)
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get(&entity).map(|state| state.lpindex)
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::BirdMedium(_) => {
self.bird_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::FishMedium(_) => {
self.fish_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::Dragon(_) => {
self.dragon_states.get(&entity).map(|state| state.lpindex)
}
Body::BirdSmall(_) => {
self.bird_small_states.get(&entity).map(|state| state.lpindex)
}
Body::FishSmall(_) => {
self.fish_small_states.get(&entity).map(|state| state.lpindex)
}
Body::BipedLarge(_) => {
self.biped_large_states.get(&entity).map(|state| state.lpindex)
}
Body::Object(_) => {
self.object_states.get(&entity).map(|state| state.lpindex)
}
}.unwrap_or(0),
);
match body {
Body::Humanoid(_) => {
self.character_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BirdMedium(_) => {
self.bird_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::FishMedium(_) => {
self.fish_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::Dragon(_) => {
self.dragon_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BirdSmall(_) => {
self.bird_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::FishSmall(_) => {
self.fish_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BipedLarge(_) => {
self.biped_large_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::Object(_) => {
self.object_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
}
// Don't process figures outside the vd
let vd_frac = Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
@ -139,7 +205,7 @@ impl FigureMgr {
}
}
continue;
} else if vd_frac > 1.0 {
} else if vd_frac > 1.0 || !in_frustum {
continue;
}
@ -320,6 +386,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::QuadrupedSmall(_) => {
@ -377,6 +445,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::QuadrupedMedium(_) => {
@ -434,6 +504,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::BirdMedium(_) => {
@ -489,6 +561,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::FishMedium(_) => {
@ -544,6 +618,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::Dragon(_) => {
@ -599,6 +675,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::BirdSmall(_) => {
@ -654,6 +732,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::FishSmall(_) => {
@ -709,6 +789,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::BipedLarge(_) => {
@ -764,6 +846,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::Object(_) => {
@ -783,6 +867,8 @@ impl FigureMgr {
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
}
@ -823,15 +909,11 @@ impl FigureMgr {
let tick = client.get_tick();
let ecs = client.state().ecs();
let frustum = camera.frustum(client);
let character_state_storage = client
.state()
.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(client.entity());
self.visible_figure_count = 0;
for (entity, _, _, body, stats, _) in (
&ecs.entities(),
&ecs.read_storage::<Pos>(),
@ -841,59 +923,55 @@ impl FigureMgr {
ecs.read_storage::<Scale>().maybe(),
)
.join()
// Don't render figures outside of frustum (camera viewport, max draw distance is farplane)
.filter(|(_, pos, _, _, _, scale)| {
frustum.test_sphere(
pos.0.into_array(),
scale.unwrap_or(&Scale(1.0)).0 * 2.0,
)
})
// Don't render dead entities
.filter(|(_, _, _, _, stats, _)| stats.map_or(true, |s| !s.is_dead))
{
self.visible_figure_count += 1;
if let Some((locals, bone_consts)) = match body {
if let Some((locals, bone_consts, visible)) = match body {
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::QuadrupedSmall(_) => self
.quadruped_small_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::QuadrupedMedium(_) => self
.quadruped_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BirdMedium(_) => self
.bird_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::FishMedium(_) => self
.fish_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::Dragon(_) => self
.dragon_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BirdSmall(_) => self
.bird_small_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::FishSmall(_) => self
.fish_small_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BipedLarge(_) => self
.biped_large_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::Object(_) => self
.object_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
} {
if !visible {
continue;
}
let is_player = entity == client.entity();
let player_camera_mode = if is_player {
@ -922,11 +1000,30 @@ impl FigureMgr {
}
pub fn figure_count(&self) -> usize {
self.figure_count
self.character_states.len()
+ self.quadruped_small_states.len()
+ self.character_states.len()
+ self.quadruped_medium_states.len()
+ self.bird_medium_states.len()
+ self.fish_medium_states.len()
+ self.dragon_states.len()
+ self.bird_small_states.len()
+ self.fish_small_states.len()
+ self.biped_large_states.len()
+ self.object_states.len()
}
pub fn figure_count_visible(&self) -> usize {
self.visible_figure_count
self.character_states.iter().filter(|(_, c)| c.visible).count()
+ self.quadruped_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.quadruped_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.bird_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.fish_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.dragon_states.iter().filter(|(_, c)| c.visible).count()
+ self.bird_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.fish_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.biped_large_states.iter().filter(|(_, c)| c.visible).count()
+ self.object_states.iter().filter(|(_, c)| c.visible).count()
}
}
@ -939,6 +1036,8 @@ pub struct FigureState<S: Skeleton> {
pos: Vec3<f32>,
ori: Vec3<f32>,
last_ori: Vec3<f32>,
lpindex: u8,
visible: bool,
}
impl<S: Skeleton> FigureState<S> {
@ -954,6 +1053,8 @@ impl<S: Skeleton> FigureState<S> {
pos: Vec3::zero(),
ori: Vec3::zero(),
last_ori: Vec3::zero(),
lpindex: 0,
visible: false,
}
}
@ -968,7 +1069,11 @@ impl<S: Skeleton> FigureState<S> {
dt: f32,
movement_rate: f32,
action_rate: f32,
lpindex: u8,
visible: bool,
) {
self.visible = visible;
self.lpindex = lpindex;
self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt);
// Update interpolation values

View File

@ -301,7 +301,7 @@ impl Scene {
);
// Maintain the figures.
self.figure_mgr.maintain(renderer, client);
self.figure_mgr.maintain(renderer, client, &self.camera);
// Remove unused figures.
self.figure_mgr.clean(client.get_tick());