render: use WorldTransforms in the renderer
ci/woodpecker/push/debug Pipeline failed Details

This commit is contained in:
SeanOMik 2024-04-17 20:46:46 -04:00
parent 12c8ece418
commit 25aa902e02
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
7 changed files with 161 additions and 75 deletions

5
Cargo.lock generated
View File

@ -97,9 +97,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.79" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
@ -1865,6 +1865,7 @@ dependencies = [
name = "lyra-scene" name = "lyra-scene"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"lyra-ecs", "lyra-ecs",
"lyra-math", "lyra-math",
] ]

View File

@ -1,6 +1,6 @@
use std::{ptr::NonNull, thread, time::Duration}; use std::{ptr::NonNull, thread, time::Duration};
use lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{Res, View}, system::{Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime}; use lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{Res, View}, system::{Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, scene::{self, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
use lyra_engine::assets::ResourceManager; use lyra_engine::assets::ResourceManager;
struct FixedTimestep { struct FixedTimestep {
@ -97,6 +97,7 @@ async fn main() {
world.spawn(( world.spawn((
sponza_scene.clone(), sponza_scene.clone(),
WorldTransform::default(),
Transform::from_xyz(0.0, 0.0, 0.0), Transform::from_xyz(0.0, 0.0, 0.0),
)); ));
@ -117,6 +118,15 @@ async fn main() {
} }
{ {
let t = Transform::new(
//Vec3::new(-5.0, 1.0, -1.28),
Vec3::new(-5.0, 1.0, -0.0),
//Vec3::new(-10.0, 0.94, -0.28),
Quat::IDENTITY,
Vec3::new(0.25, 0.25, 0.25),
);
world.spawn(( world.spawn((
PointLight { PointLight {
enabled: true, enabled: true,
@ -125,16 +135,19 @@ async fn main() {
range: 2.0, range: 2.0,
..Default::default() ..Default::default()
}, },
Transform::new( WorldTransform::from(t),
//Vec3::new(-5.0, 1.0, -1.28), t,
Vec3::new(-5.0, 1.0, -0.0), cube_mesh.clone(),
));
let t = Transform::new(
Vec3::new(-3.0, 0.2, -1.5),
//Vec3::new(-5.0, 1.0, -0.28),
//Vec3::new(-10.0, 0.94, -0.28), //Vec3::new(-10.0, 0.94, -0.28),
Quat::IDENTITY, Quat::IDENTITY,
Vec3::new(0.25, 0.25, 0.25), Vec3::new(0.15, 0.15, 0.15),
), );
cube_mesh.clone(),
));
world.spawn(( world.spawn((
PointLight { PointLight {
@ -144,16 +157,19 @@ async fn main() {
range: 1.0, range: 1.0,
..Default::default() ..Default::default()
}, },
Transform::new( WorldTransform::from(t),
Vec3::new(-3.0, 0.2, -1.5), t,
cube_mesh.clone(),
));
let t = Transform::new(
Vec3::new(0.0, 0.2, -1.5),
//Vec3::new(-5.0, 1.0, -0.28), //Vec3::new(-5.0, 1.0, -0.28),
//Vec3::new(-10.0, 0.94, -0.28), //Vec3::new(-10.0, 0.94, -0.28),
Quat::IDENTITY, Quat::IDENTITY,
Vec3::new(0.15, 0.15, 0.15), Vec3::new(0.15, 0.15, 0.15),
), );
cube_mesh.clone(),
));
world.spawn(( world.spawn((
SpotLight { SpotLight {
@ -164,14 +180,8 @@ async fn main() {
//cutoff: math::Angle::Degrees(45.0), //cutoff: math::Angle::Degrees(45.0),
..Default::default() ..Default::default()
}, },
Transform::new( WorldTransform::from(t),
Vec3::new(0.0, 0.2, -1.5), t,
//Vec3::new(-5.0, 1.0, -0.28),
//Vec3::new(-10.0, 0.94, -0.28),
Quat::IDENTITY,
Vec3::new(0.15, 0.15, 0.15),
),
cube_mesh.clone(), cube_mesh.clone(),
)); ));
} }
@ -245,6 +255,7 @@ async fn main() {
}; };
game.with_system("camera_debug_trigger", sys, &[]); game.with_system("camera_debug_trigger", sys, &[]);
game.with_system("update_world_transforms", scene::system_update_world_transforms, &[]);
}; };
let action_handler_plugin = |game: &mut Game| { let action_handler_plugin = |game: &mut Game| {

View File

@ -1,4 +1,36 @@
use crate::{query::{AsQuery, Query}, Archetype, World}; use crate::{query::{AsQuery, Fetch, Query}, Archetype, World};
pub struct OrFetch<'a, Q1: Query, Q2: Query> {
left: Option<Q1::Fetch<'a>>,
right: Option<Q2::Fetch<'a>>,
}
impl<'a, Q1: Query, Q2: Query> Fetch<'a> for OrFetch<'a, Q1, Q2> {
type Item = (Option<Q1::Item<'a>>, Option<Q2::Item<'a>>);
fn dangling() -> Self {
Self {
left: None,
right: None,
}
}
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
let mut res = (None, None);
if let Some(left) = self.left.as_mut() {
let i = left.get_item(entity);
res.0 = Some(i);
}
if let Some(right) = self.right.as_mut() {
let i = right.get_item(entity);
res.1 = Some(i);
}
res
}
}
/// A filter query returning when either `Q1` or `Q2` returns. /// A filter query returning when either `Q1` or `Q2` returns.
/// ///
@ -25,25 +57,34 @@ use crate::{query::{AsQuery, Query}, Archetype, World};
pub struct Or<Q1: AsQuery, Q2: AsQuery> { pub struct Or<Q1: AsQuery, Q2: AsQuery> {
left: Q1::Query, left: Q1::Query,
right: Q2::Query, right: Q2::Query,
can_visit_left: bool,
can_visit_right: bool,
} }
impl<Q1: AsQuery, Q2: AsQuery> Copy for Or<Q1, Q2> {} impl<Q1: AsQuery, Q2: AsQuery> Copy for Or<Q1, Q2> {}
impl<Q1: AsQuery, Q2: AsQuery> Clone for Or<Q1, Q2> { impl<Q1: AsQuery, Q2: AsQuery> Clone for Or<Q1, Q2> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { left: self.left.clone(), right: self.right.clone() } Self {
left: self.left.clone(),
right: self.right.clone(),
can_visit_left: self.can_visit_left,
can_visit_right: self.can_visit_right,
}
} }
} }
impl<Q1: AsQuery, Q2: AsQuery> Query for Or<Q1, Q2> { impl<Q1: AsQuery, Q2: AsQuery> Query for Or<Q1, Q2> {
type Item<'a> = (); type Item<'a> = (Option<<Q1::Query as Query>::Item<'a>>, Option<<Q2::Query as Query>::Item<'a>>);
type Fetch<'a> = (); type Fetch<'a> = OrFetch<'a, Q1::Query, Q2::Query>;
fn new() -> Self { fn new() -> Self {
Or { Or {
left: Q1::Query::new(), left: Q1::Query::new(),
right: Q2::Query::new(), right: Q2::Query::new(),
can_visit_left: false,
can_visit_right: false,
} }
} }
@ -51,8 +92,19 @@ impl<Q1: AsQuery, Q2: AsQuery> Query for Or<Q1, Q2> {
self.left.can_visit_archetype(archetype) || self.right.can_visit_archetype(archetype) self.left.can_visit_archetype(archetype) || self.right.can_visit_archetype(archetype)
} }
unsafe fn fetch<'a>(&self, _world: &'a World, _: &'a Archetype, _: crate::Tick) -> Self::Fetch<'a> { unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
() let mut f = OrFetch::<Q1::Query, Q2::Query>::dangling();
// TODO: store the result of Self::can_visit_archetype so this isn't ran twice
if self.left.can_visit_archetype(archetype) {
f.left = Some(self.left.fetch(world, archetype, tick));
}
if self.right.can_visit_archetype(archetype) {
f.right = Some(self.right.fetch(world, archetype, tick));
}
f
} }
} }

View File

@ -6,11 +6,12 @@ use std::borrow::Cow;
use glam::Vec3; use glam::Vec3;
use instant::Instant; use instant::Instant;
use itertools::izip; use itertools::izip;
use lyra_ecs::query::filter::{Has, Or}; use lyra_ecs::query::filter::{Has, Not, Or};
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
use lyra_ecs::{Entity, Tick}; use lyra_ecs::{Entity, Tick};
use lyra_ecs::query::{Entities, TickOf}; use lyra_ecs::query::{Entities, TickOf};
use lyra_ecs::World; use lyra_ecs::World;
use lyra_scene::SceneGraph; use lyra_scene::{SceneGraph, WorldTransform};
use tracing::{debug, warn}; use tracing::{debug, warn};
use uuid::Uuid; use uuid::Uuid;
use wgpu::{BindGroupLayout, Limits}; use wgpu::{BindGroupLayout, Limits};
@ -430,17 +431,16 @@ impl BasicRenderer {
impl Renderer for BasicRenderer { impl Renderer for BasicRenderer {
fn prepare(&mut self, main_world: &mut World) { fn prepare(&mut self, main_world: &mut World) {
let last_epoch = main_world.current_tick(); let last_epoch = main_world.current_tick();
let now_inst = Instant::now();
let mut alive_entities = HashSet::new(); let mut alive_entities = HashSet::new();
let now_inst = Instant::now(); let view = main_world.view_iter::<(Entities, &Transform, TickOf<Transform>,
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>)>();
let view = main_world.filtered_view_iter::<(Entities, &Transform, TickOf<Transform>), Or<Has<MeshHandle>, Has<SceneHandle>>>(); for (entity, transform, transform_epoch, (mesh_pair, scene_pair)) in view {
for (entity, transform, transform_epoch) in view {
alive_entities.insert(entity); alive_entities.insert(entity);
let mesh_view = main_world.view_one::<(&MeshHandle, TickOf<MeshHandle>)>(entity); if let Some((mesh_han, mesh_epoch)) = mesh_pair {
if let Some((mesh_han, mesh_epoch)) = mesh_view.get() {
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
if let Some(mesh) = mesh_han.data_ref() { if let Some(mesh) = mesh_han.data_ref() {
@ -464,17 +464,16 @@ impl Renderer for BasicRenderer {
} }
} }
let scene_view = main_world.view_one::<(&SceneHandle, TickOf<SceneHandle>)>(entity); if let Some((scene_han, scene_epoch)) = scene_pair {
if let Some((scene_han, scene_epoch)) = scene_view.get() {
// TODO: Rendering scenes
if let Some(scene) = scene_han.data_ref() { if let Some(scene) = scene_han.data_ref() {
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
scene.traverse_down(|sw: &World, mesh_han, pos| { for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
if let Some(mesh_han) = sw.view_one::<&MeshHandle>(mesh_han.entity()).get() {
if let Some(mesh) = mesh_han.data_ref() { if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + pos; let mesh_interpo = interpo_pos + **pos;
// if process mesh did not just create a new mesh, and the epoch // if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers // shows that the scene has changed, verify that the mesh buffers
@ -496,7 +495,6 @@ impl Renderer for BasicRenderer {
self.render_jobs.push_back(job); self.render_jobs.push_back(job);
} }
} }
});
} }
} }
} }
@ -567,7 +565,14 @@ impl Renderer for BasicRenderer {
render_pass.set_pipeline(pipeline.get_wgpu_pipeline()); render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
// get the mesh (containing vertices) and the buffers from storage // get the mesh (containing vertices) and the buffers from storage
let buffers = self.mesh_buffers.get(&job.mesh_uuid).unwrap(); let buffers = self.mesh_buffers.get(&job.mesh_uuid);
if buffers.is_none() {
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
continue;
}
let buffers = buffers.unwrap();
/* let buffers = self.mesh_buffers.get(&job.mesh_uuid)
.expect("missing render job mesh"); */
// Bind the optional texture // Bind the optional texture
if let Some(tex) = buffers.material.as_ref() if let Some(tex) = buffers.material.as_ref()

View File

@ -6,3 +6,5 @@ pub use camera::*;
pub mod free_fly_camera; pub mod free_fly_camera;
pub use free_fly_camera::*; pub use free_fly_camera::*;
pub use lyra_scene::*;

View File

@ -2,8 +2,9 @@ use std::{ffi::OsStr, path::{Path, PathBuf}, sync::Arc};
use glam::{Quat, Vec3}; use glam::{Quat, Vec3};
use instant::Instant; use instant::Instant;
use lyra_ecs::query;
use lyra_math::Transform; use lyra_math::Transform;
use lyra_scene::{SceneGraph, SceneNode}; use lyra_scene::{SceneGraph, SceneNode, WorldTransform};
use thiserror::Error; use thiserror::Error;
use crate::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage}; use crate::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage};
@ -76,7 +77,7 @@ impl ModelLoader {
}; };
node.name = gnode.name().map(str::to_string); node.name = gnode.name().map(str::to_string);
let scene_node = scene.add_node_under(scene_parent, node.transform, ()); let scene_node = scene.add_node_under(scene_parent, (WorldTransform::from(node.transform), node.transform));
if let Some(mesh) = gnode.mesh() { if let Some(mesh) = gnode.mesh() {
let mut new_mesh = Mesh::default(); let mut new_mesh = Mesh::default();
@ -225,6 +226,10 @@ impl ResourceLoader for ModelLoader {
} }
} }
for en in graph.world().view_iter::<query::Entities>() {
graph.world().view_one::<(&WorldTransform, &Transform)>(en).get().expect("Scene node is missing world and local transform bundle!");
}
let graph = ResHandle::new_ready(Some(path.as_str()), graph); let graph = ResHandle::new_ready(Some(path.as_str()), graph);
gltf_out.scenes.push(graph); gltf_out.scenes.push(graph);
@ -268,6 +273,7 @@ impl ResourceLoader for ModelLoader {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use lyra_ecs::{query::Entities, relation::ChildOf}; use lyra_ecs::{query::Entities, relation::ChildOf};
use lyra_scene::WorldTransform;
use crate::tests::busy_wait_resource; use crate::tests::busy_wait_resource;
@ -293,7 +299,9 @@ mod tests {
.data_ref().unwrap(); .data_ref().unwrap();
let mut node = None; let mut node = None;
scene.traverse_down(|_, no, _tran| { //scene.world().view::<SceneNodeFlag()
scene.traverse_down::<_, &WorldTransform>(|_, no, tran| {
tran.get().expect("scene node is missing a WorldTransform");
node = Some(no.clone()); node = Some(no.clone());
}); });

View File

@ -22,6 +22,12 @@ impl Deref for WorldTransform {
} }
} }
impl From<Transform> for WorldTransform {
fn from(value: Transform) -> Self {
Self(value)
}
}
/// A system that updates the [`WorldTransform`]'s for entities and their children. /// A system that updates the [`WorldTransform`]'s for entities and their children.
/// ///
/// For entities without parents, this will update world transform to match local transform. /// For entities without parents, this will update world transform to match local transform.
@ -47,7 +53,8 @@ fn recurse_update_trans(world: &World, parent_transform: &WorldTransform, entity
.relates_to::<ChildOf>(entity) .relates_to::<ChildOf>(entity)
.into_iter() .into_iter()
{ {
world_tran.0 = parent_transform.0 + *tran; world_tran.0 = *tran;
world_tran.0.translation = parent_transform.0.translation + tran.translation;
next_entities.push((en, world_tran.0.clone())); next_entities.push((en, world_tran.0.clone()));
} }