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

View File

@ -1,6 +1,6 @@
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;
struct FixedTimestep {
@ -97,6 +97,7 @@ async fn main() {
world.spawn((
sponza_scene.clone(),
WorldTransform::default(),
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((
PointLight {
enabled: true,
@ -125,17 +135,20 @@ async fn main() {
range: 2.0,
..Default::default()
},
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),
),
WorldTransform::from(t),
t,
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),
Quat::IDENTITY,
Vec3::new(0.15, 0.15, 0.15),
);
world.spawn((
PointLight {
enabled: true,
@ -144,17 +157,20 @@ async fn main() {
range: 1.0,
..Default::default()
},
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),
Quat::IDENTITY,
Vec3::new(0.15, 0.15, 0.15),
),
WorldTransform::from(t),
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(-10.0, 0.94, -0.28),
Quat::IDENTITY,
Vec3::new(0.15, 0.15, 0.15),
);
world.spawn((
SpotLight {
enabled: true,
@ -164,14 +180,8 @@ async fn main() {
//cutoff: math::Angle::Degrees(45.0),
..Default::default()
},
Transform::new(
Vec3::new(0.0, 0.2, -1.5),
//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),
),
WorldTransform::from(t),
t,
cube_mesh.clone(),
));
}
@ -245,6 +255,7 @@ async fn main() {
};
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| {

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.
///
@ -25,25 +57,34 @@ use crate::{query::{AsQuery, Query}, Archetype, World};
pub struct Or<Q1: AsQuery, Q2: AsQuery> {
left: Q1::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> Clone for Or<Q1, Q2> {
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> {
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 {
Or {
left: Q1::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)
}
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 instant::Instant;
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::query::{Entities, TickOf};
use lyra_ecs::World;
use lyra_scene::SceneGraph;
use lyra_scene::{SceneGraph, WorldTransform};
use tracing::{debug, warn};
use uuid::Uuid;
use wgpu::{BindGroupLayout, Limits};
@ -430,17 +431,16 @@ impl BasicRenderer {
impl Renderer for BasicRenderer {
fn prepare(&mut self, main_world: &mut World) {
let last_epoch = main_world.current_tick();
let now_inst = Instant::now();
let mut alive_entities = HashSet::new();
let now_inst = Instant::now();
let view = main_world.filtered_view_iter::<(Entities, &Transform, TickOf<Transform>), Or<Has<MeshHandle>, Has<SceneHandle>>>();
for (entity, transform, transform_epoch) in view {
let view = main_world.view_iter::<(Entities, &Transform, TickOf<Transform>,
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>)>();
for (entity, transform, transform_epoch, (mesh_pair, scene_pair)) in view {
alive_entities.insert(entity);
let mesh_view = main_world.view_one::<(&MeshHandle, TickOf<MeshHandle>)>(entity);
if let Some((mesh_han, mesh_epoch)) = mesh_view.get() {
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
if let Some(mesh) = mesh_han.data_ref() {
@ -464,39 +464,37 @@ impl Renderer for BasicRenderer {
}
}
let scene_view = main_world.view_one::<(&SceneHandle, TickOf<SceneHandle>)>(entity);
if let Some((scene_han, scene_epoch)) = scene_view.get() {
// TODO: Rendering scenes
if let Some((scene_han, scene_epoch)) = scene_pair {
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);
scene.traverse_down(|sw: &World, mesh_han, pos| {
if let Some(mesh_han) = sw.view_one::<&MeshHandle>(mesh_han.entity()).get() {
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + pos;
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid())
&& scene_epoch == last_epoch {
self.check_mesh_buffers(entity, &mesh_han);
}
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + **pos;
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
group, || ( mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) ));
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
self.render_jobs.push_back(job);
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid())
&& scene_epoch == last_epoch {
self.check_mesh_buffers(entity, &mesh_han);
}
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
group, || ( mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) ));
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
self.render_jobs.push_back(job);
}
});
}
}
}
}
@ -567,7 +565,14 @@ impl Renderer for BasicRenderer {
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
// 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
if let Some(tex) = buffers.material.as_ref()

View File

@ -5,4 +5,6 @@ pub mod camera;
pub use 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 instant::Instant;
use lyra_ecs::query;
use lyra_math::Transform;
use lyra_scene::{SceneGraph, SceneNode};
use lyra_scene::{SceneGraph, SceneNode, WorldTransform};
use thiserror::Error;
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);
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() {
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);
gltf_out.scenes.push(graph);
@ -268,6 +273,7 @@ impl ResourceLoader for ModelLoader {
#[cfg(test)]
mod tests {
use lyra_ecs::{query::Entities, relation::ChildOf};
use lyra_scene::WorldTransform;
use crate::tests::busy_wait_resource;
@ -293,7 +299,9 @@ mod tests {
.data_ref().unwrap();
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());
});

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.
///
/// 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)
.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()));
}