render: create a transform pass for sending transforms to the GPU
CI / build (push) Has been cancelled
Details
CI / build (push) Has been cancelled
Details
This commit is contained in:
parent
7ae0eae6ac
commit
4ff7e40624
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "simple_scene"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lyra-engine = { path = "../../", features = ["tracy"] }
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
async-std = "1.12.0"
|
||||||
|
tracing = "0.1.37"
|
|
@ -0,0 +1,142 @@
|
||||||
|
use lyra_engine::{
|
||||||
|
assets::{gltf::Gltf, ResourceManager}, ecs::query::View, game::App, input::{
|
||||||
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
|
InputActionPlugin, KeyCode, LayoutId,
|
||||||
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
|
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,
|
||||||
|
}, winit::WindowOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
let action_handler_plugin = |app: &mut App| {
|
||||||
|
let action_handler = ActionHandler::builder()
|
||||||
|
.add_layout(LayoutId::from(0))
|
||||||
|
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||||
|
.add_action("Debug", Action::new(ActionKind::Button))
|
||||||
|
.add_mapping(
|
||||||
|
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_FORWARD_BACKWARD,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyW).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyS).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyA).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyD).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_UP_DOWN,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyC).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyZ).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
//ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_UP_DOWN,
|
||||||
|
&[
|
||||||
|
//ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_ROLL,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyE).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::KeyQ).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
"Debug",
|
||||||
|
&[ActionSource::Keyboard(KeyCode::KeyB).into_binding()],
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
//let world = app.world;
|
||||||
|
app.add_resource(action_handler);
|
||||||
|
app.with_plugin(InputActionPlugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut a = App::new();
|
||||||
|
a.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
|
.with_plugin(setup_scene_plugin)
|
||||||
|
.with_plugin(action_handler_plugin)
|
||||||
|
//.with_plugin(camera_debug_plugin)
|
||||||
|
.with_plugin(FreeFlyCameraPlugin);
|
||||||
|
a.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_scene_plugin(app: &mut App) {
|
||||||
|
let world = &mut app.world;
|
||||||
|
let resman = world.get_resource_mut::<ResourceManager>().unwrap();
|
||||||
|
|
||||||
|
/* let camera_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
camera_gltf.wait_recurse_dependencies_load();
|
||||||
|
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
camera_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -5.0, -2.0),
|
||||||
|
)); */
|
||||||
|
|
||||||
|
let cube_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/cube-texture-embedded.gltf")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cube_gltf.wait_recurse_dependencies_load();
|
||||||
|
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
cube_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, 0.0, -2.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||||
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
|
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
||||||
|
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
||||||
|
world.spawn((
|
||||||
|
DirectionalLight {
|
||||||
|
enabled: true,
|
||||||
|
color: Vec3::ONE,
|
||||||
|
intensity: 0.15, //..Default::default()
|
||||||
|
},
|
||||||
|
light_tran,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut camera = CameraComponent::new_2d();
|
||||||
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||||
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"rdocCaptureSettings": 1,
|
||||||
|
"settings": {
|
||||||
|
"autoStart": false,
|
||||||
|
"commandLine": "",
|
||||||
|
"environment": [
|
||||||
|
],
|
||||||
|
"executable": "/media/data_drive/Development/Rust/lyra-engine/target/debug/lua-scripting",
|
||||||
|
"inject": false,
|
||||||
|
"numQueuedFrames": 2,
|
||||||
|
"options": {
|
||||||
|
"allowFullscreen": true,
|
||||||
|
"allowVSync": true,
|
||||||
|
"apiValidation": false,
|
||||||
|
"captureAllCmdLists": false,
|
||||||
|
"captureCallstacks": false,
|
||||||
|
"captureCallstacksOnlyDraws": false,
|
||||||
|
"debugOutputMute": true,
|
||||||
|
"delayForDebugger": 0,
|
||||||
|
"hookIntoChildren": false,
|
||||||
|
"refAllResources": false,
|
||||||
|
"softMemoryLimit": 0,
|
||||||
|
"verifyBufferAccess": false
|
||||||
|
},
|
||||||
|
"queuedFrameCap": 5,
|
||||||
|
"workingDir": "/media/data_drive/Development/Rust/lyra-engine/examples/lua-scripting"
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,7 @@ use lyra_engine::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
InputActionPlugin, KeyCode, LayoutId,
|
InputActionPlugin, KeyCode, LayoutId,
|
||||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
system_update_world_transforms, 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
|
||||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
|
||||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
|
||||||
}, winit::WindowOptions
|
}, winit::WindowOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +83,8 @@ async fn main() {
|
||||||
.with_plugin(setup_scene_plugin)
|
.with_plugin(setup_scene_plugin)
|
||||||
.with_plugin(action_handler_plugin)
|
.with_plugin(action_handler_plugin)
|
||||||
//.with_plugin(camera_debug_plugin)
|
//.with_plugin(camera_debug_plugin)
|
||||||
.with_plugin(FreeFlyCameraPlugin);
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
.with_system("system_update_world_transforms", system_update_world_transforms, &[]);
|
||||||
a.run();
|
a.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +117,7 @@ fn setup_scene_plugin(app: &mut App) {
|
||||||
world.spawn((
|
world.spawn((
|
||||||
cube_mesh.clone(),
|
cube_mesh.clone(),
|
||||||
WorldTransform::default(),
|
WorldTransform::default(),
|
||||||
Transform::from_xyz(0.0, 0.0, -2.0),
|
Transform::from_xyz(0.0, 0.0, -20.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,8 @@ pub mod winit;
|
||||||
pub mod as_any;
|
pub mod as_any;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
|
|
||||||
|
pub mod sprite;
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
|
|
||||||
|
|
|
@ -8,32 +8,24 @@ use glam::{UVec2, Vec3};
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use lyra_ecs::{
|
use lyra_ecs::{
|
||||||
query::{
|
query::{filter::Or, Entities, ResMut, TickOf},
|
||||||
filter::{Has, Not, Or},
|
Entity, ResourceObject, World,
|
||||||
Entities, Res, ResMut, TickOf,
|
|
||||||
},
|
|
||||||
relation::{ChildOf, RelationOriginComponent},
|
|
||||||
Component, Entity, ResourceObject, World,
|
|
||||||
};
|
};
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
use lyra_math::Transform;
|
|
||||||
use lyra_resource::{gltf::Mesh, ResHandle};
|
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||||
use lyra_scene::{SceneGraph, WorldTransform};
|
use lyra_scene::SceneGraph;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::render::{
|
||||||
render::{
|
|
||||||
graph::{Node, NodeDesc, NodeType},
|
graph::{Node, NodeDesc, NodeType},
|
||||||
render_buffer::BufferStorage,
|
render_buffer::BufferStorage,
|
||||||
render_job::RenderJob,
|
render_job::RenderJob,
|
||||||
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
|
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
|
||||||
transform_buffer_storage::{TransformBuffers, TransformGroup},
|
transform_buffer_storage::TransformIndex,
|
||||||
vertex::Vertex,
|
vertex::Vertex,
|
||||||
},
|
|
||||||
DeltaTime,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type MeshHandle = ResHandle<Mesh>;
|
type MeshHandle = ResHandle<Mesh>;
|
||||||
|
@ -48,12 +40,6 @@ pub struct MeshBufferStorage {
|
||||||
pub material: Option<Arc<GpuMaterial>>,
|
pub material: Option<Arc<GpuMaterial>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Component)]
|
|
||||||
struct InterpTransform {
|
|
||||||
last_transform: Transform,
|
|
||||||
alpha: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
pub struct MeshPrepNodeLabel;
|
pub struct MeshPrepNodeLabel;
|
||||||
|
|
||||||
|
@ -270,35 +256,27 @@ impl Node for MeshPrepNode {
|
||||||
) {
|
) {
|
||||||
let device = &context.device;
|
let device = &context.device;
|
||||||
let queue = &context.queue;
|
let queue = &context.queue;
|
||||||
let render_limits = device.limits();
|
|
||||||
|
|
||||||
let last_epoch = world.current_tick();
|
let last_epoch = world.current_tick();
|
||||||
let mut alive_entities = HashSet::new();
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
// prepare the world with resources
|
// prepare the world with resources
|
||||||
if !world.has_resource::<TransformBuffers>() {
|
|
||||||
let buffers = TransformBuffers::new(device);
|
|
||||||
world.add_resource(buffers);
|
|
||||||
}
|
|
||||||
Self::try_init_resource::<RenderMeshes>(world);
|
Self::try_init_resource::<RenderMeshes>(world);
|
||||||
Self::try_init_resource::<RenderAssets<MeshBufferStorage>>(world);
|
Self::try_init_resource::<RenderAssets<MeshBufferStorage>>(world);
|
||||||
Self::try_init_resource::<RenderAssets<Arc<GpuMaterial>>>(world);
|
Self::try_init_resource::<RenderAssets<Arc<GpuMaterial>>>(world);
|
||||||
Self::try_init_resource::<FxHashMap<Entity, uuid::Uuid>>(world);
|
Self::try_init_resource::<FxHashMap<Entity, uuid::Uuid>>(world);
|
||||||
|
|
||||||
let mut render_meshes = world.get_resource_mut::<RenderMeshes>()
|
let mut render_meshes = world
|
||||||
|
.get_resource_mut::<RenderMeshes>()
|
||||||
.expect("world missing RenderMeshes resource");
|
.expect("world missing RenderMeshes resource");
|
||||||
render_meshes.clear();
|
render_meshes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
let view = world.view_iter::<(
|
let view = world.view_iter::<(
|
||||||
Entities,
|
Entities,
|
||||||
&Transform,
|
&TransformIndex,
|
||||||
TickOf<Transform>,
|
|
||||||
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>,
|
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>,
|
||||||
Option<&mut InterpTransform>,
|
|
||||||
Res<DeltaTime>,
|
|
||||||
ResMut<TransformBuffers>,
|
|
||||||
ResMut<RenderMeshes>,
|
ResMut<RenderMeshes>,
|
||||||
ResMut<RenderAssets<MeshBufferStorage>>,
|
ResMut<RenderAssets<MeshBufferStorage>>,
|
||||||
ResMut<RenderAssets<Arc<GpuMaterial>>>,
|
ResMut<RenderAssets<Arc<GpuMaterial>>>,
|
||||||
|
@ -306,16 +284,10 @@ impl Node for MeshPrepNode {
|
||||||
)>();
|
)>();
|
||||||
|
|
||||||
// used to store InterpTransform components to add to entities later
|
// used to store InterpTransform components to add to entities later
|
||||||
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
transform,
|
transform_index,
|
||||||
_transform_epoch,
|
|
||||||
(mesh_pair, scene_pair),
|
(mesh_pair, scene_pair),
|
||||||
interp_tran,
|
|
||||||
delta_time,
|
|
||||||
mut transforms,
|
|
||||||
mut render_meshes,
|
mut render_meshes,
|
||||||
mut mesh_buffers,
|
mut mesh_buffers,
|
||||||
mut material_buffers,
|
mut material_buffers,
|
||||||
|
@ -324,40 +296,6 @@ impl Node for MeshPrepNode {
|
||||||
{
|
{
|
||||||
alive_entities.insert(entity);
|
alive_entities.insert(entity);
|
||||||
|
|
||||||
// Interpolate the transform for this entity using a component.
|
|
||||||
// If the entity does not have the component then it will be queued to be added
|
|
||||||
// to it after all the entities are prepared for rendering.
|
|
||||||
let interp_transform = match interp_tran {
|
|
||||||
Some(mut interp_transform) => {
|
|
||||||
// found in https://youtu.be/YJB1QnEmlTs?t=472
|
|
||||||
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time);
|
|
||||||
|
|
||||||
interp_transform.last_transform = interp_transform
|
|
||||||
.last_transform
|
|
||||||
.lerp(*transform, interp_transform.alpha);
|
|
||||||
interp_transform.last_transform
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let interp = InterpTransform {
|
|
||||||
last_transform: *transform,
|
|
||||||
alpha: 0.5,
|
|
||||||
};
|
|
||||||
component_queue.push((entity, interp));
|
|
||||||
|
|
||||||
*transform
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
// expand the transform buffers if they need to be.
|
|
||||||
// this is done in its own scope to avoid multiple mutable references to self at
|
|
||||||
// once; aka, make the borrow checker happy
|
|
||||||
if transforms.needs_expand() {
|
|
||||||
debug!("Expanding transform buffers");
|
|
||||||
transforms.expand_buffers(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
// 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
|
||||||
|
@ -377,41 +315,19 @@ impl Node for MeshPrepNode {
|
||||||
self.check_mesh_buffers(device, queue, &mut mesh_buffers, &mesh_han);
|
self.check_mesh_buffers(device, queue, &mut mesh_buffers, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
|
||||||
let transform_id = transforms.update_or_push(
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
&render_limits,
|
|
||||||
group,
|
|
||||||
interp_transform.calculate_mat4(),
|
|
||||||
glam::Mat3::from_quat(interp_transform.rotation),
|
|
||||||
);
|
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap().data_ref().unwrap();
|
let material = mesh.material.as_ref().unwrap().data_ref().unwrap();
|
||||||
let shader = material.shader_uuid.unwrap_or(0);
|
let shader = material.shader_uuid.unwrap_or(0);
|
||||||
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), *transform_index);
|
||||||
render_meshes.push_back(job);
|
render_meshes.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((scene_han, scene_epoch)) = scene_pair {
|
if let Some((scene_han, scene_epoch)) = scene_pair {
|
||||||
if let Some(scene) = scene_han.data_ref() {
|
if let Some(scene) = scene_han.data_ref() {
|
||||||
if scene_epoch == last_epoch {
|
for (mesh_han, transform_index) in
|
||||||
let view = scene.world().view::<(
|
scene.world().view_iter::<(&MeshHandle, &TransformIndex)>()
|
||||||
Entities,
|
|
||||||
&mut WorldTransform,
|
|
||||||
&Transform,
|
|
||||||
Not<Has<RelationOriginComponent<ChildOf>>>,
|
|
||||||
)>();
|
|
||||||
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (mesh_han, pos) in
|
|
||||||
scene.world().view_iter::<(&MeshHandle, &WorldTransform)>()
|
|
||||||
{
|
{
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
let mesh_interpo = interp_transform + **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
|
||||||
// dont need to be resent to the gpu.
|
// dont need to be resent to the gpu.
|
||||||
|
@ -434,35 +350,16 @@ impl Node for MeshPrepNode {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let scene_mesh_group =
|
|
||||||
TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
|
||||||
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
|
||||||
let transform_id = transforms.update_or_push(
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
&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 material = mesh.material.as_ref().unwrap().data_ref().unwrap();
|
||||||
let shader = material.shader_uuid.unwrap_or(0);
|
let shader = material.shader_uuid.unwrap_or(0);
|
||||||
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
let job =
|
||||||
|
RenderJob::new(entity, shader, mesh_han.uuid(), *transform_index);
|
||||||
render_meshes.push_back(job);
|
render_meshes.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (en, interp) in component_queue {
|
|
||||||
world.insert(en, interp);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut transforms = world.get_resource_mut::<TransformBuffers>()
|
|
||||||
.expect("world missing TransformBuffers resource");
|
|
||||||
transforms.send_to_gpu(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
|
|
|
@ -27,3 +27,6 @@ pub use shadows::*;
|
||||||
|
|
||||||
mod mesh_prepare;
|
mod mesh_prepare;
|
||||||
pub use mesh_prepare::*;
|
pub use mesh_prepare::*;
|
||||||
|
|
||||||
|
mod transform;
|
||||||
|
pub use transform::*;
|
|
@ -0,0 +1,226 @@
|
||||||
|
use lyra_ecs::{
|
||||||
|
query::{
|
||||||
|
filter::{Has, Not, Or},
|
||||||
|
Entities, TickOf,
|
||||||
|
},
|
||||||
|
relation::{ChildOf, RelationOriginComponent},
|
||||||
|
Component, Entity,
|
||||||
|
};
|
||||||
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
use lyra_math::Transform;
|
||||||
|
use lyra_resource::ResHandle;
|
||||||
|
use lyra_scene::{SceneGraph, WorldTransform};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
render::{
|
||||||
|
graph::{Node, NodeDesc, NodeType},
|
||||||
|
transform_buffer_storage::{TransformBuffers, TransformIndex},
|
||||||
|
},
|
||||||
|
DeltaTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An interpolated transform.
|
||||||
|
///
|
||||||
|
/// This transform is interpolated between frames to make movement appear smoother when the
|
||||||
|
/// transform is updated less often than rendering.
|
||||||
|
#[derive(Clone, Debug, Component)]
|
||||||
|
pub struct InterpTransform {
|
||||||
|
last_transform: Transform,
|
||||||
|
alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
|
pub struct TransformsNodeLabel;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TransformsNode {}
|
||||||
|
|
||||||
|
impl TransformsNode {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_component_queue(world: &mut lyra_ecs::World, component_queue: Vec<(Entity, Option<InterpTransform>, Option<TransformIndex>)>) {
|
||||||
|
for (en, interp, index) in component_queue {
|
||||||
|
println!("writing index {:?} for entity {}", index, en.id().0);
|
||||||
|
|
||||||
|
match (interp, index) {
|
||||||
|
(None, None) => unreachable!(),
|
||||||
|
(None, Some(index)) => world.insert(en, index),
|
||||||
|
(Some(interp), None) => world.insert(en, interp),
|
||||||
|
(Some(interp), Some(index)) => world.insert(en, (interp, index)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_transforms(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
limits: &wgpu::Limits,
|
||||||
|
world: &mut lyra_ecs::World,
|
||||||
|
delta_time: DeltaTime,
|
||||||
|
buffers: &mut TransformBuffers,
|
||||||
|
parent_transform: Transform,
|
||||||
|
) {
|
||||||
|
let current_tick = world.current_tick();
|
||||||
|
let mut component_queue = vec![];
|
||||||
|
|
||||||
|
let view = world.view_iter::<(
|
||||||
|
Entities,
|
||||||
|
Or<&WorldTransform, &Transform>,
|
||||||
|
Option<&mut InterpTransform>,
|
||||||
|
Option<&TransformIndex>,
|
||||||
|
Option<(&ResHandle<SceneGraph>, TickOf<ResHandle<SceneGraph>>)>,
|
||||||
|
)>();
|
||||||
|
|
||||||
|
for (entity, transform, interp_tran, transform_index, scene_graph) in view {
|
||||||
|
// expand the transform buffers if they need to be.
|
||||||
|
if buffers.needs_expand() {
|
||||||
|
debug!("Expanding transform buffers");
|
||||||
|
buffers.expand_buffers(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the world transform of the entity, else fall back to the transform
|
||||||
|
let transform = match transform {
|
||||||
|
(None, None) => unreachable!(),
|
||||||
|
(None, Some(t)) => *t,
|
||||||
|
(Some(wt), None) => **wt,
|
||||||
|
// Assume world transform since it *should* be updated by world systems
|
||||||
|
(Some(wt), Some(_)) => **wt,
|
||||||
|
};
|
||||||
|
// offset this transform by its parent
|
||||||
|
let transform = transform + parent_transform;
|
||||||
|
|
||||||
|
// Interpolate the transform for this entity using a component.
|
||||||
|
// If the entity does not have the component then it will be queued to be added
|
||||||
|
// to it after all the entities are prepared for rendering.
|
||||||
|
let transform = match interp_tran {
|
||||||
|
Some(mut interp_transform) => {
|
||||||
|
// found in https://youtu.be/YJB1QnEmlTs?t=472
|
||||||
|
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(*delta_time);
|
||||||
|
|
||||||
|
interp_transform.last_transform = interp_transform
|
||||||
|
.last_transform
|
||||||
|
.lerp(transform, interp_transform.alpha);
|
||||||
|
interp_transform.last_transform
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let interp = InterpTransform {
|
||||||
|
last_transform: transform,
|
||||||
|
alpha: 0.5,
|
||||||
|
};
|
||||||
|
component_queue.push((entity, Some(interp), None));
|
||||||
|
transform
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the TransformIndex from the entity, or reserve a new one if the entity doesn't have
|
||||||
|
// the component.
|
||||||
|
let index = match transform_index {
|
||||||
|
Some(i) => *i,
|
||||||
|
None => {
|
||||||
|
let i = buffers.reserve_transform(&device);
|
||||||
|
debug!(
|
||||||
|
"Reserved transform index {:?} for entity {}",
|
||||||
|
i,
|
||||||
|
entity.id().0
|
||||||
|
);
|
||||||
|
|
||||||
|
component_queue.push((entity, None, Some(i)));
|
||||||
|
i
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: only update if the transform changed.
|
||||||
|
buffers.update(
|
||||||
|
&queue,
|
||||||
|
index,
|
||||||
|
transform.calculate_mat4(),
|
||||||
|
glam::Mat3::from_quat(transform.rotation),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((scene, scene_tick)) = scene_graph {
|
||||||
|
if let Some(mut scene) = scene.data_mut() {
|
||||||
|
if *scene_tick + 1 >= *current_tick {
|
||||||
|
// Must manually call the world transform update system for scenes
|
||||||
|
// TODO: Separate gltf from lyra-resource so lyra-scene can depend on resource to
|
||||||
|
// query for scenes in the system.
|
||||||
|
let view = scene.world().view::<(
|
||||||
|
Entities,
|
||||||
|
&mut WorldTransform,
|
||||||
|
&Transform,
|
||||||
|
Not<Has<RelationOriginComponent<ChildOf>>>,
|
||||||
|
)>();
|
||||||
|
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
update_transforms(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
limits,
|
||||||
|
scene.world_mut(),
|
||||||
|
delta_time,
|
||||||
|
buffers,
|
||||||
|
transform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process_component_queue(world, component_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for TransformsNode {
|
||||||
|
fn desc(
|
||||||
|
&mut self,
|
||||||
|
_: &mut crate::render::graph::RenderGraph,
|
||||||
|
) -> crate::render::graph::NodeDesc {
|
||||||
|
NodeDesc::new(NodeType::Node, None, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(
|
||||||
|
&mut self,
|
||||||
|
_: &mut crate::render::graph::RenderGraph,
|
||||||
|
world: &mut lyra_ecs::World,
|
||||||
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
let device = &context.device;
|
||||||
|
let queue = &context.queue;
|
||||||
|
let render_limits = device.limits();
|
||||||
|
|
||||||
|
// prepare the world with resources
|
||||||
|
if !world.has_resource::<TransformBuffers>() {
|
||||||
|
let buffers = TransformBuffers::new(device);
|
||||||
|
world.add_resource(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// I have to do this weird garbage to borrow the `TransformBuffers`
|
||||||
|
// without running into a borrow checker error from passing `world` as mutable.
|
||||||
|
// This is safe since I know that the recursive function isn't accessing this
|
||||||
|
// TransformBuffers, or any other ones in other worlds.
|
||||||
|
let buffers = world.get_resource_data::<TransformBuffers>()
|
||||||
|
.map(|r| r.clone()).unwrap();
|
||||||
|
let mut buffers = buffers.get_mut();
|
||||||
|
let dt = world.get_resource::<DeltaTime>().unwrap().clone();
|
||||||
|
|
||||||
|
update_transforms(
|
||||||
|
&device,
|
||||||
|
&queue,
|
||||||
|
&render_limits,
|
||||||
|
world,
|
||||||
|
dt,
|
||||||
|
&mut buffers,
|
||||||
|
Transform::default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(
|
||||||
|
&mut self,
|
||||||
|
_: &mut crate::render::graph::RenderGraph,
|
||||||
|
_: &crate::render::graph::NodeDesc,
|
||||||
|
_: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ use lyra_game_derive::RenderGraphLabel;
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, ShadowMapsPass, ShadowMapsPassLabel, SubGraphNode, ViewTarget};
|
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, ShadowMapsPass, ShadowMapsPassLabel, SubGraphNode, TransformsNode, TransformsNodeLabel, ViewTarget};
|
||||||
|
|
||||||
use super::graph::RenderGraph;
|
use super::graph::RenderGraph;
|
||||||
use super::{resource::RenderPipeline, render_job::RenderJob};
|
use super::{resource::RenderPipeline, render_job::RenderJob};
|
||||||
|
@ -83,7 +83,7 @@ impl BasicRenderer {
|
||||||
|
|
||||||
let adapter = instance.request_adapter(
|
let adapter = instance.request_adapter(
|
||||||
&wgpu::RequestAdapterOptions {
|
&wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||||
compatible_surface: Some(&surface),
|
compatible_surface: Some(&surface),
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: false,
|
||||||
},
|
},
|
||||||
|
@ -149,14 +149,19 @@ impl BasicRenderer {
|
||||||
debug!("Adding light cull compute pass");
|
debug!("Adding light cull compute pass");
|
||||||
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
||||||
|
|
||||||
debug!("Adding mesh pass");
|
debug!("Adding Transforms node");
|
||||||
|
forward_plus_graph.add_node(TransformsNodeLabel, TransformsNode::new());
|
||||||
|
|
||||||
|
debug!("Adding shadow maps pass");
|
||||||
forward_plus_graph.add_node(ShadowMapsPassLabel, ShadowMapsPass::new(&device));
|
forward_plus_graph.add_node(ShadowMapsPassLabel, ShadowMapsPass::new(&device));
|
||||||
|
|
||||||
|
debug!("Adding mesh prep node");
|
||||||
let mesh_prep = MeshPrepNode::new(&device);
|
let mesh_prep = MeshPrepNode::new(&device);
|
||||||
let material_bgl = mesh_prep.material_bgl.clone();
|
let material_bgl = mesh_prep.material_bgl.clone();
|
||||||
forward_plus_graph.add_node(MeshPrepNodeLabel, mesh_prep);
|
forward_plus_graph.add_node(MeshPrepNodeLabel, mesh_prep);
|
||||||
|
debug!("Adding mesh pass");
|
||||||
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
|
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
|
||||||
|
forward_plus_graph.add_edge(TransformsNodeLabel, MeshPrepNodeLabel);
|
||||||
|
|
||||||
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
||||||
forward_plus_graph.add_edge(LightCullComputePassLabel, MeshesPassLabel);
|
forward_plus_graph.add_edge(LightCullComputePassLabel, MeshesPassLabel);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
const ALPHA_CUTOFF = 0.1;
|
||||||
|
|
||||||
|
struct VertexInput {
|
||||||
|
@location(0) position: vec3<f32>,
|
||||||
|
@location(1) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
@location(1) world_position: vec3<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransformData {
|
||||||
|
transform: mat4x4<f32>,
|
||||||
|
normal_matrix: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CameraUniform {
|
||||||
|
view: mat4x4<f32>,
|
||||||
|
inverse_projection: mat4x4<f32>,
|
||||||
|
view_projection: mat4x4<f32>,
|
||||||
|
projection: mat4x4<f32>,
|
||||||
|
position: vec3<f32>,
|
||||||
|
tile_debug: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> u_model_transform_data: TransformData;
|
||||||
|
|
||||||
|
@group(2) @binding(0)
|
||||||
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
in: VertexInput,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(in.position, 1.0);
|
||||||
|
out.world_position = world_position.xyz;
|
||||||
|
out.tex_coords = in.tex_coords;
|
||||||
|
out.clip_position = u_camera.view_projection * world_position;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var t_diffuse: texture_2d<f32>;
|
||||||
|
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
|
if (object_color.a < ALPHA_CUTOFF) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_color;
|
||||||
|
}
|
|
@ -1,14 +1,11 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, sync::Arc};
|
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, sync::Arc};
|
||||||
|
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::{Component, Entity};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::Limits;
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::render::avec::AVec;
|
|
||||||
|
|
||||||
/// A group id created from a [`TransformGroup`].
|
/// A group id created from a [`TransformGroup`].
|
||||||
///
|
///
|
||||||
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
||||||
|
@ -57,7 +54,7 @@ pub enum TransformGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The index of a specific Transform inside of the buffers.
|
/// The index of a specific Transform inside of the buffers.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Component)]
|
||||||
pub struct TransformIndex {
|
pub struct TransformIndex {
|
||||||
/// The index of the entry in the buffer chain.
|
/// The index of the entry in the buffer chain.
|
||||||
entry_index: usize,
|
entry_index: usize,
|
||||||
|
@ -70,9 +67,6 @@ struct BufferEntry {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub bindgroup: wgpu::BindGroup,
|
pub bindgroup: wgpu::BindGroup,
|
||||||
pub buffer: wgpu::Buffer,
|
pub buffer: wgpu::Buffer,
|
||||||
transforms: AVec<TransformNormalMatPair>,
|
|
||||||
//pub normal_buffer: wgpu::Buffer,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A HashMap that caches values for reuse.
|
/// A HashMap that caches values for reuse.
|
||||||
|
@ -166,8 +160,6 @@ impl<K: Hash + Eq + PartialEq + Clone, V: Clone, S: BuildHasher> CachedValMap<K,
|
||||||
/// update, and retrieve the transforms.
|
/// update, and retrieve the transforms.
|
||||||
pub struct TransformBuffers {
|
pub struct TransformBuffers {
|
||||||
pub bindgroup_layout: Arc<wgpu::BindGroupLayout>,
|
pub bindgroup_layout: Arc<wgpu::BindGroupLayout>,
|
||||||
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
|
||||||
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
|
||||||
entries: Vec<BufferEntry>,
|
entries: Vec<BufferEntry>,
|
||||||
limits: wgpu::Limits,
|
limits: wgpu::Limits,
|
||||||
max_transform_count: usize,
|
max_transform_count: usize,
|
||||||
|
@ -208,40 +200,14 @@ impl TransformBuffers {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the transform buffers to the gpu.
|
/// Reserve a transform index.
|
||||||
///
|
///
|
||||||
/// This uses [`wgpu::Queue::write_buffer`], so the write is not immediately submitted,
|
/// The buffer chain may expand if its required
|
||||||
/// and instead enqueued internally to happen at the start of the next submit() call.
|
pub fn reserve_transform(&mut self, device: &wgpu::Device) -> TransformIndex {
|
||||||
#[instrument(skip(self, queue))]
|
|
||||||
pub fn send_to_gpu(&mut self, queue: &wgpu::Queue) {
|
|
||||||
self.next_index = 0;
|
|
||||||
|
|
||||||
for entry in &mut self.entries {
|
|
||||||
entry.len = 0;
|
|
||||||
|
|
||||||
let p = entry.transforms.as_ptr();
|
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(p, entry.transforms.len() * entry.transforms.align()) };
|
|
||||||
|
|
||||||
queue.write_buffer(&entry.buffer, 0, bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update an existing transform group or if its not existing yet, pushes it to the buffer.
|
|
||||||
///
|
|
||||||
/// Returns: the index that the transform is at in the buffers.
|
|
||||||
#[instrument(skip(self, device, queue, limits, group, transform, normal_matrix))]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn update_or_push(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, limits: &Limits, group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex
|
|
||||||
{
|
|
||||||
// maybe will be used at some point again
|
|
||||||
let _ = (queue, limits, group);
|
|
||||||
|
|
||||||
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
|
||||||
|
|
||||||
let index = self.next_index;
|
let index = self.next_index;
|
||||||
self.next_index += 1;
|
self.next_index += 1;
|
||||||
|
|
||||||
// the index of the entry to put the transform into
|
// the index of the transform buffer
|
||||||
let entry_index = index / self.max_transform_count;
|
let entry_index = index / self.max_transform_count;
|
||||||
// the index of the transform in the buffer
|
// the index of the transform in the buffer
|
||||||
let transform_index = index % self.max_transform_count;
|
let transform_index = index % self.max_transform_count;
|
||||||
|
@ -251,20 +217,28 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = self.entries.get_mut(entry_index).unwrap();
|
let entry = self.entries.get_mut(entry_index).unwrap();
|
||||||
|
|
||||||
// write the transform and normal to the end of the transform
|
|
||||||
entry.transforms.set_at(transform_index, TransformNormalMatPair {
|
|
||||||
transform,
|
|
||||||
normal_mat: normal_matrix,
|
|
||||||
});
|
|
||||||
entry.len += 1;
|
entry.len += 1;
|
||||||
|
|
||||||
TransformIndex {
|
TransformIndex {
|
||||||
entry_index: 0,
|
entry_index,
|
||||||
transform_index: index,
|
transform_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates a Transform at `index`.
|
||||||
|
#[instrument(skip(self, queue, index, transform, normal_matrix))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn update(&mut self, queue: &wgpu::Queue, index: TransformIndex, transform: glam::Mat4, normal_matrix: glam::Mat3) {
|
||||||
|
let pair = TransformNormalMatPair {
|
||||||
|
transform,
|
||||||
|
normal_mat: glam::Mat4::from_mat3(normal_matrix),
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = self.entries.get(index.entry_index).expect("invalid entry index, no entry!");
|
||||||
|
let offset = self.buffer_offset(index);
|
||||||
|
queue.write_buffer(&entry.buffer, offset as _, bytemuck::bytes_of(&pair));
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand the Transform buffers by adding another uniform buffer binding.
|
/// Expand the Transform buffers by adding another uniform buffer binding.
|
||||||
///
|
///
|
||||||
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
||||||
|
@ -302,18 +276,10 @@ impl TransformBuffers {
|
||||||
label: Some("BG_Transforms"),
|
label: Some("BG_Transforms"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut transforms = AVec::new(limits.min_uniform_buffer_offset_alignment as _);
|
|
||||||
transforms.resize(self.max_transform_count, TransformNormalMatPair {
|
|
||||||
transform: glam::Mat4::IDENTITY,
|
|
||||||
normal_mat: glam::Mat4::IDENTITY,
|
|
||||||
});
|
|
||||||
|
|
||||||
let entry = BufferEntry {
|
let entry = BufferEntry {
|
||||||
bindgroup,
|
bindgroup,
|
||||||
buffer: transform_buffer,
|
buffer: transform_buffer,
|
||||||
len: 0,
|
len: 0,
|
||||||
|
|
||||||
transforms,
|
|
||||||
};
|
};
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ pub struct Vertex {
|
||||||
pub position: glam::Vec3,
|
pub position: glam::Vec3,
|
||||||
pub tex_coords: glam::Vec2,
|
pub tex_coords: glam::Vec2,
|
||||||
pub normals: glam::Vec3,
|
pub normals: glam::Vec3,
|
||||||
//pub color: [f32; 3], // TODO: add color again
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
impl Vertex {
|
||||||
|
@ -59,3 +58,37 @@ impl DescVertexBufferLayout for Vertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct Vertex2D {
|
||||||
|
pub position: glam::Vec3,
|
||||||
|
pub tex_coords: glam::Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vertex2D {
|
||||||
|
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self {
|
||||||
|
Self {
|
||||||
|
position, tex_coords
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &[
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: 0,
|
||||||
|
shader_location: 0,
|
||||||
|
format: wgpu::VertexFormat::Float32x3, // Vec3
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::size_of::<glam::Vec3>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 1,
|
||||||
|
format: wgpu::VertexFormat::Float32x2, // Vec2
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
use lyra_ecs::Component;
|
||||||
|
use lyra_reflect::Reflect;
|
||||||
|
use lyra_resource::ResHandle;
|
||||||
|
use lyra_math::{Vec3, Vec2};
|
||||||
|
|
||||||
|
/// How the sprite is positioned and rotated relative to its [`Transform`].
|
||||||
|
///
|
||||||
|
/// Default pivot is `Pivot::Center`, this makes it easier to rotate the sprites.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Default, Component, Reflect)]
|
||||||
|
pub enum Pivot {
|
||||||
|
#[default]
|
||||||
|
Center,
|
||||||
|
CenterLeft,
|
||||||
|
CenterRight,
|
||||||
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
TopCenter,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight,
|
||||||
|
BottomCenter,
|
||||||
|
/// A custom anchor point relative to top left.
|
||||||
|
/// Top left is `(0.0, 0.0)`.
|
||||||
|
Custom(Vec2)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pivot {
|
||||||
|
/// Get the pivot point as a Vec2.
|
||||||
|
///
|
||||||
|
/// The point is offset from the top left `(0.0, 0.0)`.
|
||||||
|
pub fn as_vec(&self) -> Vec2 {
|
||||||
|
match self {
|
||||||
|
Pivot::Center => Vec2::new(0.5, 0.5),
|
||||||
|
Pivot::CenterLeft => Vec2::new(0.0, 0.5),
|
||||||
|
Pivot::CenterRight => Vec2::new(1.0, 0.5),
|
||||||
|
Pivot::TopLeft => Vec2::ZERO,
|
||||||
|
Pivot::TopRight => Vec2::new(1.0, 0.0),
|
||||||
|
Pivot::TopCenter => Vec2::new(0.0, 0.5),
|
||||||
|
Pivot::BottomLeft => Vec2::new(0.0, 1.0),
|
||||||
|
Pivot::BottomRight => Vec2::new(1.0, 1.0),
|
||||||
|
Pivot::BottomCenter => Vec2::new(0.5, 1.0),
|
||||||
|
Pivot::Custom(v) => *v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Component, Reflect)]
|
||||||
|
pub struct Sprite {
|
||||||
|
pub texture: ResHandle<lyra_resource::Texture>,
|
||||||
|
pub color: Vec3,
|
||||||
|
pub pivot: Pivot,
|
||||||
|
}
|
|
@ -67,6 +67,39 @@ impl ResourceState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ResourceDataRefMut<'a, T> {
|
||||||
|
guard: std::sync::RwLockWriteGuard<'a, UntypedResource>,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'static> std::ops::Deref for ResourceDataRefMut<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match &self.guard.state {
|
||||||
|
ResourceState::Ready(d) => {
|
||||||
|
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
||||||
|
let d = d.as_ref().as_any();
|
||||||
|
d.downcast_ref::<T>().unwrap()
|
||||||
|
},
|
||||||
|
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'static> std::ops::DerefMut for ResourceDataRefMut<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
match &mut self.guard.state {
|
||||||
|
ResourceState::Ready(d) => {
|
||||||
|
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
||||||
|
let d = d.as_mut().as_any_mut();
|
||||||
|
d.downcast_mut::<T>().unwrap()
|
||||||
|
},
|
||||||
|
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ResourceDataRef<'a, T> {
|
pub struct ResourceDataRef<'a, T> {
|
||||||
guard: std::sync::RwLockReadGuard<'a, UntypedResource>,
|
guard: std::sync::RwLockReadGuard<'a, UntypedResource>,
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
|
@ -310,6 +343,18 @@ impl<T: ResourceData> ResHandle<T> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn data_mut<'a>(&'a self) -> Option<ResourceDataRefMut<'a, T>> {
|
||||||
|
if self.is_loaded() {
|
||||||
|
let d = self.handle.write();
|
||||||
|
Some(ResourceDataRefMut {
|
||||||
|
guard: d,
|
||||||
|
_marker: PhantomData::<T>
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ResourceData> ResourceStorage for ResHandle<T> {
|
impl<T: ResourceData> ResourceStorage for ResHandle<T> {
|
||||||
|
|
|
@ -129,6 +129,10 @@ impl SceneGraph {
|
||||||
pub fn world(&self) -> &World {
|
pub fn world(&self) -> &World {
|
||||||
&self.world
|
&self.world
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn world_mut(&mut self) -> &mut World {
|
||||||
|
&mut self.world
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a node under a parent node.
|
/// Add a node under a parent node.
|
||||||
|
|
Loading…
Reference in New Issue