Compare commits

..

3 Commits

Author SHA1 Message Date
SeanOMik 73160172ee
Merge branch 'feature/fixrate-interpolate'
ci/woodpecker/push/build Pipeline was successful Details
2023-11-03 19:50:14 -04:00
SeanOMik b9b2c9f8e7
Finish fixed rate transform interpolation for the renderer
ci/woodpecker/push/build Pipeline failed Details
2023-11-03 19:50:00 -04:00
SeanOMik 1b723cc30b
Attempt to interpolate transforms in the renderer
ci/woodpecker/push/build Pipeline was successful Details
2023-10-31 14:28:22 -04:00
8 changed files with 88 additions and 3134 deletions

18
Cargo.lock generated
View File

@ -763,6 +763,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fps_counter"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3aaba7ff514ee9d802b562927f80b1e94e93d8e74c31b134c9c3762dabf1a36b"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -2293,6 +2299,18 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "testbed"
version = "0.1.0"
dependencies = [
"anyhow",
"async-std",
"edict",
"fps_counter",
"lyra-engine",
"tracing",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.48" version = "1.0.48"

View File

@ -5,7 +5,8 @@ edition = "2021"
[workspace] [workspace]
members = [ members = [
"lyra-resource" "lyra-resource",
"examples/testbed"
] ]
[dependencies] [dependencies]

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,4 @@ anyhow = "1.0.75"
async-std = "1.12.0" async-std = "1.12.0"
tracing = "0.1.37" tracing = "0.1.37"
fps_counter = "2.0.0" fps_counter = "2.0.0"
edict = "0.5.0" edict = "0.5.0"
[workspace]

View File

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

View File

@ -85,4 +85,18 @@ impl Transform {
pub fn rotate_z(&mut self, angle: Angle) { pub fn rotate_z(&mut self, angle: Angle) {
self.rotate(Quat::from_rotation_z(angle.to_radians())) 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 crate::math::Transform;
use super::renderer::CachedTransform;
pub struct RenderJob { pub struct RenderJob {
pub entity: EntityId, pub entity: EntityId,
pub shader_id: u64, pub shader_id: u64,
pub mesh_buffer_id: uuid::Uuid, pub mesh_buffer_id: uuid::Uuid,
pub transform: Transform, pub transform: Transform,
pub last_transform: Option<Transform>, // TODO: render interpolation pub last_transform: Option<CachedTransform>, // TODO: render interpolation
} }
impl RenderJob { 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 { Self {
entity, entity,
shader_id, shader_id,

View File

@ -7,6 +7,7 @@ use std::borrow::Cow;
use edict::query::EpochOf; use edict::query::EpochOf;
use edict::{EntityId, Entities}; use edict::{EntityId, Entities};
use glam::Vec3; use glam::Vec3;
use instant::Instant;
use tracing::{debug, warn}; use tracing::{debug, warn};
use wgpu::{BindGroup, BindGroupLayout, Limits}; use wgpu::{BindGroup, BindGroupLayout, Limits};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
@ -48,6 +49,14 @@ struct MeshBufferStorage {
transform_index: TransformBufferIndices, transform_index: TransformBufferIndices,
} }
#[derive(Clone, Debug)]
pub struct CachedTransform {
last_updated_at: Option<Instant>,
cached_at: Instant,
to_transform: Transform,
from_transform: Transform,
}
pub struct BasicRenderer { pub struct BasicRenderer {
pub surface: wgpu::Surface, pub surface: wgpu::Surface,
pub device: wgpu::Device, pub device: wgpu::Device,
@ -63,6 +72,7 @@ pub struct BasicRenderer {
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
entity_meshes: HashMap<EntityId, uuid::Uuid>, entity_meshes: HashMap<EntityId, uuid::Uuid>,
entity_last_transforms: HashMap<EntityId, CachedTransform>,
transform_buffers: TransformBuffers, transform_buffers: TransformBuffers,
transform_bind_group_layout: BindGroupLayout, transform_bind_group_layout: BindGroupLayout,
@ -312,6 +322,7 @@ impl BasicRenderer {
texture_bind_group_layout, texture_bind_group_layout,
default_texture_bind_group: default_tex_bindgroup, default_texture_bind_group: default_tex_bindgroup,
depth_buffer_texture: depth_texture, depth_buffer_texture: depth_texture,
entity_last_transforms: HashMap::new(),
}; };
// create the default pipelines // create the default pipelines
@ -510,18 +521,50 @@ impl Renderer for BasicRenderer {
let mut alive_entities = HashSet::new(); 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); alive_entities.insert(entity);
let model = model.data.as_ref().unwrap().as_ref(); let cached = match self.entity_last_transforms.get_mut(&entity) {
Some(last) if transform_epoch == last_epoch => {
last.from_transform = last.to_transform;
last.to_transform = transform.transform;
last.last_updated_at = Some(last.cached_at);
last.cached_at = now_inst;
last.clone()
},
Some(last) => last.clone(),
None => {
let cached = CachedTransform {
last_updated_at: None,
cached_at: now_inst,
from_transform: transform.transform,
to_transform: transform.transform,
};
self.entity_last_transforms.insert(entity, cached.clone());
cached
}
};
let fixed_time = match cached.last_updated_at {
Some(last_updated_at) => cached.cached_at - last_updated_at,
None => now_inst - cached.cached_at
}.as_secs_f32();
let accumulator = (now_inst - cached.cached_at).as_secs_f32();
let alpha = accumulator / fixed_time;
let transform_val = cached.from_transform.lerp(cached.to_transform, alpha);
let model = model.data.as_ref().unwrap().as_ref();
for mesh in model.meshes.iter() { for mesh in model.meshes.iter() {
if !self.process_mesh(entity, transform.transform, mesh) && model_epoch == last_epoch { if !self.process_mesh(entity, transform_val, mesh) && model_epoch == last_epoch {
self.update_mesh_buffers(entity, mesh); self.update_mesh_buffers(entity, mesh);
} }
let shader = mesh.material().shader_uuid.unwrap_or(0); 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); self.render_jobs.push_back(job);
} }
} }