Attempt to interpolate transforms in the renderer
ci/woodpecker/push/build Pipeline was successful Details

This commit is contained in:
SeanOMik 2023-10-31 14:28:22 -04:00
parent 31799cae05
commit 1b723cc30b
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
4 changed files with 91 additions and 6 deletions

View File

@ -120,7 +120,7 @@ async fn main() {
game.world().insert_resource(TpsAccumulator(0.0));
let mut sys = BatchedSystem::new();
sys.with_criteria(FixedTimestep::new(30));
sys.with_criteria(FixedTimestep::new(10));
sys.with_system(spin_system);
sys.with_system(fps_system);

View File

@ -85,4 +85,18 @@ impl Transform {
pub fn rotate_z(&mut self, angle: Angle) {
self.rotate(Quat::from_rotation_z(angle.to_radians()))
}
/// Performs a linear interpolation between `self` and `rhs` based on the value `alpha`.
///
/// When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`, the result
/// will be equal to `rhs`. When `alpha` is outside of range `[0, 1]`, the result is linearly
/// extrapolated.
pub fn lerp(&self, rhs: Transform, alpha: f32) -> Self {
let mut res = self.clone();
res.translation = self.translation.lerp(rhs.translation, alpha);
res.rotation = self.rotation.lerp(rhs.rotation, alpha);
res.scale = self.scale.lerp(rhs.scale, alpha);
res
}
}

View File

@ -2,17 +2,19 @@ use edict::EntityId;
use crate::math::Transform;
use super::renderer::CachedTransform;
pub struct RenderJob {
pub entity: EntityId,
pub shader_id: u64,
pub mesh_buffer_id: uuid::Uuid,
pub transform: Transform,
pub last_transform: Option<Transform>, // TODO: render interpolation
pub last_transform: Option<CachedTransform>, // TODO: render interpolation
}
impl RenderJob {
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option<Transform>) -> Self {
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option<CachedTransform>) -> Self {
Self {
entity,
shader_id,

View File

@ -7,6 +7,7 @@ use std::borrow::Cow;
use edict::query::EpochOf;
use edict::{EntityId, Entities};
use glam::Vec3;
use instant::Instant;
use tracing::{debug, warn};
use wgpu::{BindGroup, BindGroupLayout, Limits};
use wgpu::util::DeviceExt;
@ -48,6 +49,13 @@ struct MeshBufferStorage {
transform_index: TransformBufferIndices,
}
#[derive(Clone, Debug)]
pub struct CachedTransform {
last_updated_at: Option<Instant>,
cached_at: Instant,
transform: Transform,
}
pub struct BasicRenderer {
pub surface: wgpu::Surface,
pub device: wgpu::Device,
@ -63,6 +71,7 @@ pub struct BasicRenderer {
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
entity_meshes: HashMap<EntityId, uuid::Uuid>,
entity_last_transforms: HashMap<EntityId, CachedTransform>,
transform_buffers: TransformBuffers,
transform_bind_group_layout: BindGroupLayout,
@ -312,6 +321,7 @@ impl BasicRenderer {
texture_bind_group_layout,
default_texture_bind_group: default_tex_bindgroup,
depth_buffer_texture: depth_texture,
entity_last_transforms: HashMap::new(),
};
// create the default pipelines
@ -510,18 +520,77 @@ impl Renderer for BasicRenderer {
let mut alive_entities = HashSet::new();
for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &ModelComponent, EpochOf<ModelComponent>, &TransformComponent)>().iter() {
let now_inst = Instant::now();
for (entity, model, model_epoch, transform, transform_epoch) in main_world.query::<(Entities, &ModelComponent, EpochOf<ModelComponent>, &TransformComponent, EpochOf<TransformComponent>)>().iter() {
alive_entities.insert(entity);
let model = model.data.as_ref().unwrap().as_ref();
for mesh in model.meshes.iter() {
let last_transform = self.entity_last_transforms.get(&entity).cloned();
if last_transform.is_none() {
let cached = CachedTransform {
last_updated_at: None,
cached_at: now_inst,
transform: transform.transform,
};
self.entity_last_transforms.insert(entity, cached);
} else if transform_epoch == last_epoch {
let last = self.entity_last_transforms.get_mut(&entity).unwrap();
last.transform = transform.transform;
last.last_updated_at = Some(last.cached_at);
last.cached_at = now_inst;
debug!("Updated transform");
// to get the fixed delta time, you'd just subtract last_updated_at and cached_at.
// to get the tps_accumulator, you'd get the elapsed ms of cached_at.
}
let transform_val = match last_transform {
Some(last) => {
debug!("now: {:?}, cached_at: {:?}, last_updated_at: {:?}", now_inst, last.cached_at, last.last_updated_at);
let fixed_time = match last.last_updated_at {
Some(last_updated_at) => last.cached_at - last_updated_at,
None => now_inst - last.cached_at
}.as_secs_f32();
let accumulator = last.cached_at.elapsed().as_secs_f32();
let alpha = accumulator / fixed_time;
debug!("fixed time: {fixed_time}, acc: {accumulator}, alpha: {alpha}");
last.transform.lerp(transform.transform, alpha)
} ,
None => {
transform.transform
}
};
/*{
match self.entity_last_transforms.get_mut(&entity) {
Some(last) => {
last.transform = transform.transform.clone();
last.last_updated_at = Some(last.cached_at);
last.cached_at = Instant::now();
},
None => {
self.entity_last_transforms.insert(entity, CachedTransform {
last_updated_at: None,
cached_at: Instant::now(),
transform: transform.transform.clone(),
});
}
}
}*/
if !self.process_mesh(entity, transform.transform, mesh) && model_epoch == last_epoch {
self.update_mesh_buffers(entity, mesh);
}
let shader = mesh.material().shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh.uuid, transform.transform, None);
let job = RenderJob::new(entity, shader, mesh.uuid, transform_val, None);
self.render_jobs.push_back(job);
}
}