Compare commits
2 Commits
c4aebdb25d
...
3a80c069c9
Author | SHA1 | Date |
---|---|---|
SeanOMik | 3a80c069c9 | |
SeanOMik | 7ff67a194b |
|
@ -3075,6 +3075,16 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shadows"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-std",
|
||||||
|
"lyra-engine",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
|
@ -5,7 +5,6 @@ edition = "2021"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"examples/testbed",
|
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
|
@ -14,10 +13,12 @@ members = [
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-scene",
|
"lyra-scene",
|
||||||
|
|
||||||
|
"examples/testbed",
|
||||||
"examples/many-lights",
|
"examples/many-lights",
|
||||||
"examples/fixed-timestep-rotating-model",
|
"examples/fixed-timestep-rotating-model",
|
||||||
"examples/lua-scripting",
|
"examples/lua-scripting",
|
||||||
"examples/simple_scene"
|
"examples/simple_scene",
|
||||||
|
"examples/shadows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "shadows"
|
||||||
|
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,59 @@
|
||||||
|
---Return the userdata's name from its metatable
|
||||||
|
---@param val userdata
|
||||||
|
---@return string
|
||||||
|
function udname(val)
|
||||||
|
return getmetatable(val).__name
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_init()
|
||||||
|
local cube = world:request_res("../assets/cube-texture-embedded.gltf")
|
||||||
|
print("Loaded textured cube (" .. udname(cube) .. ")")
|
||||||
|
|
||||||
|
cube:wait_until_loaded()
|
||||||
|
local scenes = cube:scenes()
|
||||||
|
local cube_scene = scenes[1]
|
||||||
|
|
||||||
|
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
|
||||||
|
|
||||||
|
local e = world:spawn(pos, cube_scene)
|
||||||
|
print("spawned entity " .. tostring(e))
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ function on_first()
|
||||||
|
print("Lua's first function was called")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_pre_update()
|
||||||
|
print("Lua's pre-update function was called")
|
||||||
|
end ]]
|
||||||
|
|
||||||
|
function on_update()
|
||||||
|
--[[ ---@type number
|
||||||
|
local dt = world:resource(DeltaTime)
|
||||||
|
local act = world:resource(ActionHandler)
|
||||||
|
---@type number
|
||||||
|
local move_objs = act:get_axis("ObjectsMoveUpDown")
|
||||||
|
|
||||||
|
world:view(function (t)
|
||||||
|
if move_objs ~= nil then
|
||||||
|
t:translate(0, move_objs * 0.35 * dt, 0)
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end, Transform) ]]
|
||||||
|
|
||||||
|
---@type number
|
||||||
|
local dt = world:resource(DeltaTime)
|
||||||
|
|
||||||
|
world:view(function (t)
|
||||||
|
t:translate(0, 0.15 * dt, 0)
|
||||||
|
return t
|
||||||
|
end, Transform)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ function on_post_update()
|
||||||
|
print("Lua's post-update function was called")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_last()
|
||||||
|
print("Lua's last function was called")
|
||||||
|
end ]]
|
|
@ -0,0 +1,165 @@
|
||||||
|
use lyra_engine::{
|
||||||
|
assets::{gltf::Gltf, ResourceManager},
|
||||||
|
game::Game,
|
||||||
|
input::{
|
||||||
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
|
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
let action_handler_plugin = |game: &mut Game| {
|
||||||
|
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::W).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_UP_DOWN,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_UP_DOWN,
|
||||||
|
&[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_ROLL,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
"Debug",
|
||||||
|
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let world = game.world_mut();
|
||||||
|
world.add_resource(action_handler);
|
||||||
|
game.with_plugin(InputActionPlugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
Game::initialize()
|
||||||
|
.await
|
||||||
|
.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
|
.with_plugin(setup_scene_plugin)
|
||||||
|
.with_plugin(action_handler_plugin)
|
||||||
|
//.with_plugin(camera_debug_plugin)
|
||||||
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
.run()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_scene_plugin(game: &mut Game) {
|
||||||
|
let world = game.world_mut();
|
||||||
|
let resman = world.get_resource_mut::<ResourceManager>();
|
||||||
|
|
||||||
|
/* 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];
|
||||||
|
|
||||||
|
let platform_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/wood-platform.glb")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
platform_gltf.wait_recurse_dependencies_load();
|
||||||
|
let platform_mesh = &platform_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
cube_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -2.0, -5.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
platform_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -5.0, -5.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut light_tran = Transform::from_xyz(-5.5, 2.5, -3.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(-35.0));
|
||||||
|
world.spawn((
|
||||||
|
cube_mesh.clone(),
|
||||||
|
DirectionalLight {
|
||||||
|
enabled: true,
|
||||||
|
color: Vec3::new(1.0, 0.95, 0.9),
|
||||||
|
intensity: 1.0,
|
||||||
|
},
|
||||||
|
light_tran,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut camera = CameraComponent::new_3d();
|
||||||
|
camera.transform.translation += math::Vec3::new(0.0, 2.0, 10.5);
|
||||||
|
camera.transform.rotate_x(math::Angle::Degrees(-17.0));
|
||||||
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
|
}
|
|
@ -444,6 +444,11 @@ impl World {
|
||||||
.and_then(|r| r.try_get_mut())
|
.and_then(|r| r.try_get_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
|
||||||
|
self.resources.get(&TypeId::of::<T>())
|
||||||
|
.map(|r| r.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Increments the TickTracker which is used for tracking changes to components.
|
/// Increments the TickTracker which is used for tracking changes to components.
|
||||||
///
|
///
|
||||||
/// Most users wont need to call this manually, its done for you through queries and views.
|
/// Most users wont need to call this manually, its done for you through queries and views.
|
||||||
|
|
|
@ -95,9 +95,9 @@ struct NodeEntry {
|
||||||
struct BindGroupEntry {
|
struct BindGroupEntry {
|
||||||
label: RenderGraphLabelValue,
|
label: RenderGraphLabelValue,
|
||||||
/// BindGroup
|
/// BindGroup
|
||||||
bg: Rc<wgpu::BindGroup>,
|
bg: Arc<wgpu::BindGroup>,
|
||||||
/// BindGroupLayout
|
/// BindGroupLayout
|
||||||
layout: Option<Rc<wgpu::BindGroupLayout>>,
|
layout: Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -368,22 +368,23 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> {
|
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroup>> {
|
||||||
self.bind_groups.get(&label.into()).map(|e| &e.bg)
|
self.bind_groups.get(&label.into()).map(|e| &e.bg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> {
|
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroup> {
|
||||||
self.try_bind_group(label).expect("Unknown id for bind group")
|
let l = label.into();
|
||||||
|
self.try_bind_group(l.clone()).unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> {
|
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroupLayout>> {
|
||||||
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
|
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> {
|
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroupLayout> {
|
||||||
let l = label.into();
|
let l = label.into();
|
||||||
self.try_bind_group_layout(l.clone())
|
self.try_bind_group_layout(l.clone())
|
||||||
.unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))
|
.unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc};
|
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use bind_match::bind_match;
|
use bind_match::bind_match;
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
|
@ -54,16 +54,16 @@ pub enum SlotValue {
|
||||||
/// The value will be set during a later phase of the render graph. To see the type of value
|
/// The value will be set during a later phase of the render graph. To see the type of value
|
||||||
/// this will be set to, see the slots type.
|
/// this will be set to, see the slots type.
|
||||||
Lazy,
|
Lazy,
|
||||||
TextureView(Rc<wgpu::TextureView>),
|
TextureView(Arc<wgpu::TextureView>),
|
||||||
Sampler(Rc<wgpu::Sampler>),
|
Sampler(Rc<wgpu::Sampler>),
|
||||||
Texture(Rc<wgpu::Texture>),
|
Texture(Rc<wgpu::Texture>),
|
||||||
Buffer(Rc<wgpu::Buffer>),
|
Buffer(Arc<wgpu::Buffer>),
|
||||||
RenderTarget(Rc<RefCell<RenderTarget>>),
|
RenderTarget(Rc<RefCell<RenderTarget>>),
|
||||||
Frame(Rc<RefCell<Option<Frame>>>),
|
Frame(Rc<RefCell<Option<Frame>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotValue {
|
impl SlotValue {
|
||||||
pub fn as_texture_view(&self) -> Option<&Rc<wgpu::TextureView>> {
|
pub fn as_texture_view(&self) -> Option<&Arc<wgpu::TextureView>> {
|
||||||
bind_match!(self, Self::TextureView(v) => v)
|
bind_match!(self, Self::TextureView(v) => v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ impl SlotValue {
|
||||||
bind_match!(self, Self::Texture(v) => v)
|
bind_match!(self, Self::Texture(v) => v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_buffer(&self) -> Option<&Rc<wgpu::Buffer>> {
|
pub fn as_buffer(&self) -> Option<&Arc<wgpu::Buffer>> {
|
||||||
bind_match!(self, Self::Buffer(v) => v)
|
bind_match!(self, Self::Buffer(v) => v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,8 +189,8 @@ pub struct NodeDesc {
|
||||||
/// This makes the bind groups accessible to other Nodes.
|
/// This makes the bind groups accessible to other Nodes.
|
||||||
pub bind_groups: Vec<(
|
pub bind_groups: Vec<(
|
||||||
RenderGraphLabelValue,
|
RenderGraphLabelValue,
|
||||||
Rc<wgpu::BindGroup>,
|
Arc<wgpu::BindGroup>,
|
||||||
Option<Rc<wgpu::BindGroupLayout>>,
|
Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
)>,
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ impl NodeDesc {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pass_type: NodeType,
|
pass_type: NodeType,
|
||||||
pipeline_desc: Option<PipelineDescriptor>,
|
pipeline_desc: Option<PipelineDescriptor>,
|
||||||
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
bind_groups: Vec<(&dyn RenderGraphLabel, Arc<wgpu::BindGroup>, Option<Arc<wgpu::BindGroupLayout>>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ty: pass_type,
|
ty: pass_type,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
@ -56,8 +56,8 @@ impl Node for BasePass {
|
||||||
.buffer_dynamic_offset(false)
|
.buffer_dynamic_offset(false)
|
||||||
.contents(&[self.screen_size])
|
.contents(&[self.screen_size])
|
||||||
.finish_parts(graph.device());
|
.finish_parts(graph.device());
|
||||||
let screen_size_bgl = Rc::new(screen_size_bgl);
|
let screen_size_bgl = Arc::new(screen_size_bgl);
|
||||||
let screen_size_bg = Rc::new(screen_size_bg);
|
let screen_size_bg = Arc::new(screen_size_bg);
|
||||||
|
|
||||||
let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder()
|
let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder()
|
||||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||||
|
@ -66,17 +66,17 @@ impl Node for BasePass {
|
||||||
.buffer_dynamic_offset(false)
|
.buffer_dynamic_offset(false)
|
||||||
.contents(&[CameraUniform::default()])
|
.contents(&[CameraUniform::default()])
|
||||||
.finish_parts(graph.device());
|
.finish_parts(graph.device());
|
||||||
let camera_bgl = Rc::new(camera_bgl);
|
let camera_bgl = Arc::new(camera_bgl);
|
||||||
let camera_bg = Rc::new(camera_bg);
|
let camera_bg = Arc::new(camera_bg);
|
||||||
|
|
||||||
// create the depth texture using the utility struct, then take all the required fields
|
// create the depth texture using the utility struct, then take all the required fields
|
||||||
let mut depth_texture = RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
|
let mut depth_texture = RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
|
||||||
depth_texture.create_bind_group(&graph.device);
|
depth_texture.create_bind_group(&graph.device);
|
||||||
|
|
||||||
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
||||||
let depth_texture_bg = Rc::new(dt_bg_pair.bindgroup);
|
let depth_texture_bg = Arc::new(dt_bg_pair.bindgroup);
|
||||||
let depth_texture_bgl = dt_bg_pair.layout;
|
let depth_texture_bgl = dt_bg_pair.layout;
|
||||||
let depth_texture_view = Rc::new(depth_texture.view);
|
let depth_texture_view = Arc::new(depth_texture.view);
|
||||||
|
|
||||||
let mut desc = NodeDesc::new(
|
let mut desc = NodeDesc::new(
|
||||||
NodeType::Node,
|
NodeType::Node,
|
||||||
|
@ -102,12 +102,12 @@ impl Node for BasePass {
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
BasePassSlots::ScreenSize,
|
BasePassSlots::ScreenSize,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
|
Some(SlotValue::Buffer(Arc::new(screen_size_buf))),
|
||||||
);
|
);
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
BasePassSlots::Camera,
|
BasePassSlots::Camera,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(camera_buf))),
|
Some(SlotValue::Buffer(Arc::new(camera_buf))),
|
||||||
);
|
);
|
||||||
|
|
||||||
desc
|
desc
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ pub struct FxaaPassLabel;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct FxaaPass {
|
pub struct FxaaPass {
|
||||||
target_sampler: Option<wgpu::Sampler>,
|
target_sampler: Option<wgpu::Sampler>,
|
||||||
bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
/// Store bind groups for the input textures.
|
/// Store bind groups for the input textures.
|
||||||
/// The texture may change due to resizes, or changes to the view target chain
|
/// The texture may change due to resizes, or changes to the view target chain
|
||||||
/// from other nodes.
|
/// from other nodes.
|
||||||
|
@ -54,7 +54,7 @@ impl Node for FxaaPass {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
let bgl = Rc::new(bgl);
|
let bgl = Arc::new(bgl);
|
||||||
self.bgl = Some(bgl.clone());
|
self.bgl = Some(bgl.clone());
|
||||||
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
|
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some("fxaa sampler"),
|
label: Some("fxaa sampler"),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{mem, rc::Rc};
|
use std::{mem, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use glam::Vec2Swizzles;
|
use glam::Vec2Swizzles;
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
|
@ -63,7 +63,7 @@ impl Node for LightCullComputePass {
|
||||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
});
|
});
|
||||||
|
|
||||||
let light_indices_bg_layout = Rc::new(device.create_bind_group_layout(
|
let light_indices_bg_layout = Arc::new(device.create_bind_group_layout(
|
||||||
&wgpu::BindGroupLayoutDescriptor {
|
&wgpu::BindGroupLayoutDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
@ -128,7 +128,7 @@ impl Node for LightCullComputePass {
|
||||||
array_layer_count: None,
|
array_layer_count: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let light_indices_bg = Rc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let light_indices_bg = Arc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
layout: &light_indices_bg_layout,
|
layout: &light_indices_bg_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
|
@ -194,7 +194,7 @@ impl Node for LightCullComputePass {
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
LightCullComputePassSlots::IndexCounterBuffer,
|
LightCullComputePassSlots::IndexCounterBuffer,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
|
Some(SlotValue::Buffer(Arc::new(light_index_counter_buffer))),
|
||||||
);
|
);
|
||||||
|
|
||||||
desc
|
desc
|
||||||
|
|
|
@ -0,0 +1,767 @@
|
||||||
|
use std::{
|
||||||
|
collections::{HashSet, VecDeque},
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use glam::{UVec2, Vec3};
|
||||||
|
use image::GenericImageView;
|
||||||
|
use itertools::izip;
|
||||||
|
use lyra_ecs::{
|
||||||
|
query::{
|
||||||
|
filter::{Has, Not, Or},
|
||||||
|
Entities, Res, ResMut, TickOf,
|
||||||
|
},
|
||||||
|
relation::{ChildOf, RelationOriginComponent},
|
||||||
|
Component, Entity, ResourceObject, World,
|
||||||
|
};
|
||||||
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
use lyra_math::Transform;
|
||||||
|
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||||
|
use lyra_scene::{SceneGraph, WorldTransform};
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
use uuid::Uuid;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
render::{
|
||||||
|
graph::{Node, NodeDesc, NodeType},
|
||||||
|
render_buffer::BufferStorage,
|
||||||
|
render_job::RenderJob,
|
||||||
|
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
|
||||||
|
transform_buffer_storage::{TransformBuffers, TransformGroup},
|
||||||
|
vertex::Vertex,
|
||||||
|
},
|
||||||
|
DeltaTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
type MeshHandle = ResHandle<Mesh>;
|
||||||
|
type SceneHandle = ResHandle<SceneGraph>;
|
||||||
|
|
||||||
|
pub struct MeshBufferStorage {
|
||||||
|
pub buffer_vertex: BufferStorage,
|
||||||
|
pub buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
|
||||||
|
|
||||||
|
// maybe this should just be a Uuid and the material can be retrieved though
|
||||||
|
// MeshPass's `material_buffers` field?
|
||||||
|
pub material: Option<Arc<GpuMaterial>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Component)]
|
||||||
|
struct InterpTransform {
|
||||||
|
last_transform: Transform,
|
||||||
|
alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
|
pub struct MeshPrepNodeLabel;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MeshPrepNode {
|
||||||
|
pub material_bgl: Arc<wgpu::BindGroupLayout>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MeshPrepNode {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let bgl = GpuMaterial::create_bind_group_layout(device);
|
||||||
|
|
||||||
|
Self { material_bgl: bgl }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the mesh buffers in the GPU need to be updated.
|
||||||
|
#[instrument(skip(self, device, mesh_buffers, queue, mesh_han))]
|
||||||
|
fn check_mesh_buffers(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
mesh_buffers: &mut FxHashMap<uuid::Uuid, MeshBufferStorage>,
|
||||||
|
mesh_han: &ResHandle<Mesh>,
|
||||||
|
) {
|
||||||
|
let mesh_uuid = mesh_han.uuid();
|
||||||
|
|
||||||
|
if let (Some(mesh), Some(buffers)) = (mesh_han.data_ref(), mesh_buffers.get_mut(&mesh_uuid))
|
||||||
|
{
|
||||||
|
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
||||||
|
let vertices = mesh.position().unwrap();
|
||||||
|
if buffers.buffer_vertex.count() != vertices.len() {
|
||||||
|
debug!("Recreating buffers for mesh {}", mesh_uuid.to_string());
|
||||||
|
let (vert, idx) = self.create_vertex_index_buffers(device, &mesh);
|
||||||
|
|
||||||
|
// have to re-get buffers because of borrow checker
|
||||||
|
let buffers = mesh_buffers.get_mut(&mesh_uuid).unwrap();
|
||||||
|
buffers.buffer_indices = idx;
|
||||||
|
buffers.buffer_vertex = vert;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update vertices
|
||||||
|
let vertex_buffer = buffers.buffer_vertex.buffer();
|
||||||
|
let vertices = vertices.as_slice();
|
||||||
|
// align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT)
|
||||||
|
let (_, vertices, _) = bytemuck::pod_align_to::<Vec3, u32>(vertices);
|
||||||
|
queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(vertices));
|
||||||
|
|
||||||
|
// update the indices if they're given
|
||||||
|
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
||||||
|
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
||||||
|
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
||||||
|
lyra_resource::gltf::MeshIndices::U16(v) => {
|
||||||
|
bytemuck::pod_align_to::<u16, u32>(v).1
|
||||||
|
}
|
||||||
|
lyra_resource::gltf::MeshIndices::U32(v) => {
|
||||||
|
bytemuck::pod_align_to::<u32, u32>(v).1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let index_buffer = index_buffer.1.buffer();
|
||||||
|
queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(aligned_indices));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, device, mesh))]
|
||||||
|
fn create_vertex_index_buffers(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
mesh: &Mesh,
|
||||||
|
) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
||||||
|
let positions = mesh.position().unwrap();
|
||||||
|
let tex_coords: Vec<glam::Vec2> = mesh
|
||||||
|
.tex_coords()
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
|
||||||
|
let normals = mesh.normals().unwrap();
|
||||||
|
|
||||||
|
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
|
||||||
|
|
||||||
|
let mut vertex_inputs = vec![];
|
||||||
|
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
|
||||||
|
vertex_inputs.push(Vertex::new(*v, *t, *n));
|
||||||
|
}
|
||||||
|
|
||||||
|
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Vertex Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(vertex_inputs.as_slice()), //vertex_combined.as_slice(),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len());
|
||||||
|
|
||||||
|
let indices = match mesh.indices.as_ref() {
|
||||||
|
Some(indices) => {
|
||||||
|
let (idx_type, len, contents) = match indices {
|
||||||
|
lyra_resource::gltf::MeshIndices::U16(v) => {
|
||||||
|
(wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v))
|
||||||
|
}
|
||||||
|
lyra_resource::gltf::MeshIndices::U32(v) => {
|
||||||
|
(wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Index Buffer"),
|
||||||
|
contents,
|
||||||
|
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let buffer_indices = BufferStorage::new(index_buffer, 0, len);
|
||||||
|
|
||||||
|
Some((idx_type, buffer_indices))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
(vertex_buffer, indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, device, queue, material_buffers, mesh))]
|
||||||
|
fn create_mesh_buffers(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
material_buffers: &mut RenderAssets<Arc<GpuMaterial>>,
|
||||||
|
mesh: &Mesh,
|
||||||
|
) -> MeshBufferStorage {
|
||||||
|
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(device, mesh);
|
||||||
|
|
||||||
|
let material = mesh
|
||||||
|
.material
|
||||||
|
.as_ref()
|
||||||
|
.expect("Material resource not loaded yet");
|
||||||
|
let material_ref = material.data_ref().unwrap();
|
||||||
|
|
||||||
|
let material = material_buffers.entry(material.uuid()).or_insert_with(|| {
|
||||||
|
debug!(
|
||||||
|
uuid = material.uuid().to_string(),
|
||||||
|
"Sending material to gpu"
|
||||||
|
);
|
||||||
|
Arc::new(GpuMaterial::from_resource(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
&self.material_bgl,
|
||||||
|
&material_ref,
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
MeshBufferStorage {
|
||||||
|
buffer_vertex: vertex_buffer,
|
||||||
|
buffer_indices,
|
||||||
|
material: Some(material.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||||
|
#[instrument(skip(
|
||||||
|
self,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
mesh_buffers,
|
||||||
|
material_buffers,
|
||||||
|
entity_meshes,
|
||||||
|
mesh,
|
||||||
|
entity
|
||||||
|
))]
|
||||||
|
fn process_mesh(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
mesh_buffers: &mut RenderAssets<MeshBufferStorage>,
|
||||||
|
material_buffers: &mut RenderAssets<Arc<GpuMaterial>>,
|
||||||
|
entity_meshes: &mut FxHashMap<Entity, uuid::Uuid>,
|
||||||
|
entity: Entity,
|
||||||
|
mesh: &Mesh,
|
||||||
|
mesh_uuid: Uuid,
|
||||||
|
) -> bool {
|
||||||
|
#[allow(clippy::map_entry)]
|
||||||
|
if !mesh_buffers.contains_key(&mesh_uuid) {
|
||||||
|
// create the mesh's buffers
|
||||||
|
let buffers = self.create_mesh_buffers(device, queue, material_buffers, mesh);
|
||||||
|
mesh_buffers.insert(mesh_uuid, buffers);
|
||||||
|
entity_meshes.insert(entity, mesh_uuid);
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the resource does not exist in the world, add the default
|
||||||
|
fn try_init_resource<T: ResourceObject + Default>(world: &mut World) {
|
||||||
|
if !world.has_resource::<T>() {
|
||||||
|
world.add_resource_default::<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for MeshPrepNode {
|
||||||
|
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();
|
||||||
|
|
||||||
|
let last_epoch = world.current_tick();
|
||||||
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
// 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::<RenderAssets<MeshBufferStorage>>(world);
|
||||||
|
Self::try_init_resource::<RenderAssets<Arc<GpuMaterial>>>(world);
|
||||||
|
Self::try_init_resource::<FxHashMap<Entity, uuid::Uuid>>(world);
|
||||||
|
|
||||||
|
let mut render_meshes = world.get_resource_mut::<RenderMeshes>();
|
||||||
|
render_meshes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = world.view_iter::<(
|
||||||
|
Entities,
|
||||||
|
&Transform,
|
||||||
|
TickOf<Transform>,
|
||||||
|
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>,
|
||||||
|
Option<&mut InterpTransform>,
|
||||||
|
Res<DeltaTime>,
|
||||||
|
ResMut<TransformBuffers>,
|
||||||
|
ResMut<RenderMeshes>,
|
||||||
|
ResMut<RenderAssets<MeshBufferStorage>>,
|
||||||
|
ResMut<RenderAssets<Arc<GpuMaterial>>>,
|
||||||
|
ResMut<FxHashMap<Entity, uuid::Uuid>>,
|
||||||
|
)>();
|
||||||
|
|
||||||
|
// used to store InterpTransform components to add to entities later
|
||||||
|
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
|
||||||
|
|
||||||
|
for (
|
||||||
|
entity,
|
||||||
|
transform,
|
||||||
|
_transform_epoch,
|
||||||
|
(mesh_pair, scene_pair),
|
||||||
|
interp_tran,
|
||||||
|
delta_time,
|
||||||
|
mut transforms,
|
||||||
|
mut render_meshes,
|
||||||
|
mut mesh_buffers,
|
||||||
|
mut material_buffers,
|
||||||
|
mut entity_meshes,
|
||||||
|
) in view
|
||||||
|
{
|
||||||
|
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) = mesh_han.data_ref() {
|
||||||
|
// 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(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
&mut mesh_buffers,
|
||||||
|
&mut material_buffers,
|
||||||
|
&mut entity_meshes,
|
||||||
|
entity,
|
||||||
|
&mesh,
|
||||||
|
mesh_han.uuid(),
|
||||||
|
) && mesh_epoch == last_epoch
|
||||||
|
{
|
||||||
|
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 shader = material.shader_uuid.unwrap_or(0);
|
||||||
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
||||||
|
render_meshes.push_back(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((scene_han, scene_epoch)) = scene_pair {
|
||||||
|
if let Some(scene) = scene_han.data_ref() {
|
||||||
|
if scene_epoch == last_epoch {
|
||||||
|
let view = scene.world().view::<(
|
||||||
|
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() {
|
||||||
|
let mesh_interpo = interp_transform + **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(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
&mut mesh_buffers,
|
||||||
|
&mut material_buffers,
|
||||||
|
&mut entity_meshes,
|
||||||
|
entity,
|
||||||
|
&mesh,
|
||||||
|
mesh_han.uuid(),
|
||||||
|
) && scene_epoch == last_epoch
|
||||||
|
{
|
||||||
|
self.check_mesh_buffers(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
&mut mesh_buffers,
|
||||||
|
&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 = 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 shader = material.shader_uuid.unwrap_or(0);
|
||||||
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
||||||
|
render_meshes.push_back(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (en, interp) in component_queue {
|
||||||
|
world.insert(en, interp);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut transforms = world.get_resource_mut::<TransformBuffers>();
|
||||||
|
transforms.send_to_gpu(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(
|
||||||
|
&mut self,
|
||||||
|
_: &mut crate::render::graph::RenderGraph,
|
||||||
|
_: &crate::render::graph::NodeDesc,
|
||||||
|
_: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct RenderAssets<T>(FxHashMap<Uuid, T>);
|
||||||
|
|
||||||
|
impl<T> Deref for RenderAssets<T> {
|
||||||
|
type Target = FxHashMap<Uuid, T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for RenderAssets<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for RenderAssets<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RenderAssets<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct GpuMaterial {
|
||||||
|
pub bind_group: Arc<wgpu::BindGroup>,
|
||||||
|
bind_group_layout: Arc<wgpu::BindGroupLayout>,
|
||||||
|
material_properties_buffer: wgpu::Buffer,
|
||||||
|
diffuse_texture: wgpu::Texture,
|
||||||
|
diffuse_texture_sampler: wgpu::Sampler,
|
||||||
|
/* specular_texture: wgpu::Texture,
|
||||||
|
specular_texture_sampler: wgpu::Sampler, */
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpuMaterial {
|
||||||
|
fn create_bind_group_layout(device: &wgpu::Device) -> Arc<wgpu::BindGroupLayout> {
|
||||||
|
Arc::new(
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("bgl_material"),
|
||||||
|
entries: &[
|
||||||
|
// material properties
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None, /* Some(
|
||||||
|
NonZeroU64::new(mem::size_of::<MaterialPropertiesUniform>() as _)
|
||||||
|
.unwrap(),
|
||||||
|
) */
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// diffuse texture
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// diffuse texture sampler
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// specular texture
|
||||||
|
/* wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 3,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
// specular texture sampler
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 4,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||||
|
count: None,
|
||||||
|
}, */
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn texture_desc(label: &str, size: UVec2) -> wgpu::TextureDescriptor {
|
||||||
|
//debug!("Texture desc size: {:?}", size);
|
||||||
|
wgpu::TextureDescriptor {
|
||||||
|
label: Some(label),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: size.x,
|
||||||
|
height: size.y,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1, // TODO
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_texture(queue: &wgpu::Queue, texture: &wgpu::Texture, img: &lyra_resource::Image) {
|
||||||
|
let dim = img.dimensions();
|
||||||
|
//debug!("Write texture size: {:?}", dim);
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
texture: &texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
},
|
||||||
|
&img.to_rgba8(),
|
||||||
|
wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: std::num::NonZeroU32::new(4 * dim.0),
|
||||||
|
rows_per_image: std::num::NonZeroU32::new(dim.1),
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: dim.0,
|
||||||
|
height: dim.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_resource(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
layout: &Arc<wgpu::BindGroupLayout>,
|
||||||
|
mat: &lyra_resource::gltf::Material,
|
||||||
|
) -> Self {
|
||||||
|
//let specular = mat.specular.as_ref().unwrap_or_default();
|
||||||
|
//let specular_
|
||||||
|
|
||||||
|
let prop = MaterialPropertiesUniform {
|
||||||
|
ambient: Vec3::ONE,
|
||||||
|
_padding1: 0,
|
||||||
|
diffuse: Vec3::ONE,
|
||||||
|
shininess: 32.0,
|
||||||
|
specular_factor: 0.0,
|
||||||
|
_padding2: [0; 3],
|
||||||
|
specular_color_factor: Vec3::ZERO,
|
||||||
|
_padding3: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let properties_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("buffer_material"),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
contents: bytemuck::bytes_of(&prop),
|
||||||
|
});
|
||||||
|
|
||||||
|
let diffuse_tex = mat.base_color_texture.as_ref().unwrap();
|
||||||
|
let diffuse_tex = diffuse_tex.data_ref().unwrap();
|
||||||
|
let diffuse_tex_img = diffuse_tex.image.data_ref().unwrap();
|
||||||
|
let diffuse_tex_dim = diffuse_tex_img.dimensions();
|
||||||
|
let diffuse_texture = device.create_texture(&Self::texture_desc(
|
||||||
|
"material_diffuse_texture",
|
||||||
|
UVec2::new(diffuse_tex_dim.0, diffuse_tex_dim.1),
|
||||||
|
));
|
||||||
|
let diffuse_tex_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let sampler_desc = match &diffuse_tex.sampler {
|
||||||
|
Some(sampler) => {
|
||||||
|
let magf = res_filter_to_wgpu(
|
||||||
|
sampler
|
||||||
|
.mag_filter
|
||||||
|
.unwrap_or(lyra_resource::FilterMode::Linear),
|
||||||
|
);
|
||||||
|
let minf = res_filter_to_wgpu(
|
||||||
|
sampler
|
||||||
|
.min_filter
|
||||||
|
.unwrap_or(lyra_resource::FilterMode::Nearest),
|
||||||
|
);
|
||||||
|
let mipf = res_filter_to_wgpu(
|
||||||
|
sampler
|
||||||
|
.mipmap_filter
|
||||||
|
.unwrap_or(lyra_resource::FilterMode::Nearest),
|
||||||
|
);
|
||||||
|
|
||||||
|
let wrap_u = res_wrap_to_wgpu(sampler.wrap_u);
|
||||||
|
let wrap_v = res_wrap_to_wgpu(sampler.wrap_v);
|
||||||
|
let wrap_w = res_wrap_to_wgpu(sampler.wrap_w);
|
||||||
|
|
||||||
|
wgpu::SamplerDescriptor {
|
||||||
|
address_mode_u: wrap_u,
|
||||||
|
address_mode_v: wrap_v,
|
||||||
|
address_mode_w: wrap_w,
|
||||||
|
mag_filter: magf,
|
||||||
|
min_filter: minf,
|
||||||
|
mipmap_filter: mipf,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => wgpu::SamplerDescriptor {
|
||||||
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let diffuse_sampler = device.create_sampler(&sampler_desc);
|
||||||
|
|
||||||
|
Self::write_texture(queue, &diffuse_texture, &diffuse_tex_img);
|
||||||
|
|
||||||
|
debug!("TODO: specular texture");
|
||||||
|
|
||||||
|
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("bg_material"),
|
||||||
|
layout: &layout,
|
||||||
|
entries: &[
|
||||||
|
// material properties
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||||
|
buffer: &properties_buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
// diffuse texture
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&diffuse_tex_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&diffuse_sampler),
|
||||||
|
},
|
||||||
|
// TODO: specular textures
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
bind_group: Arc::new(bg),
|
||||||
|
bind_group_layout: layout.clone(),
|
||||||
|
material_properties_buffer: properties_buffer,
|
||||||
|
diffuse_texture,
|
||||||
|
diffuse_texture_sampler: diffuse_sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uniform for MaterialProperties in a shader
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct MaterialPropertiesUniform {
|
||||||
|
ambient: glam::Vec3,
|
||||||
|
_padding1: u32,
|
||||||
|
diffuse: glam::Vec3,
|
||||||
|
shininess: f32,
|
||||||
|
specular_factor: f32,
|
||||||
|
_padding2: [u32; 3],
|
||||||
|
specular_color_factor: glam::Vec3,
|
||||||
|
_padding3: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct RenderMeshes(VecDeque<RenderJob>);
|
||||||
|
|
||||||
|
impl Deref for RenderMeshes {
|
||||||
|
type Target = VecDeque<RenderJob>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for RenderMeshes {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,476 +1,153 @@
|
||||||
use std::{collections::{HashSet, VecDeque}, rc::Rc};
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use glam::Vec3;
|
use lyra_ecs::{AtomicRef, ResourceData};
|
||||||
use itertools::izip;
|
|
||||||
use lyra_ecs::{query::{filter::{Has, Not, Or}, Entities, Res, TickOf}, relation::{ChildOf, RelationOriginComponent}, Component, Entity};
|
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
use lyra_math::Transform;
|
use tracing::{instrument, warn};
|
||||||
use lyra_resource::{gltf::Mesh, ResHandle};
|
|
||||||
use lyra_scene::{SceneGraph, WorldTransform};
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use tracing::{debug, instrument, warn};
|
|
||||||
use uuid::Uuid;
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::render::{
|
||||||
render::{
|
desc_buf_lay::DescVertexBufferLayout,
|
||||||
desc_buf_lay::DescVertexBufferLayout, graph::{
|
graph::{Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext},
|
||||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext
|
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex
|
texture::RenderTexture,
|
||||||
},
|
transform_buffer_storage::TransformBuffers,
|
||||||
DeltaTime,
|
vertex::Vertex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BasePassSlots, LightBasePassSlots, LightCullComputePassSlots};
|
use super::{
|
||||||
|
BasePassSlots, LightBasePassSlots, LightCullComputePassSlots, MeshBufferStorage, RenderAssets,
|
||||||
type MeshHandle = ResHandle<Mesh>;
|
RenderMeshes,
|
||||||
type SceneHandle = ResHandle<SceneGraph>;
|
};
|
||||||
|
|
||||||
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
|
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
|
||||||
pub struct MeshesPassLabel;
|
pub struct MeshesPassLabel;
|
||||||
|
|
||||||
#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)]
|
#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)]
|
||||||
pub enum MeshesPassSlots {
|
pub enum MeshesPassSlots {
|
||||||
Material
|
Material,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MeshBufferStorage {
|
//#[derive(Default)]
|
||||||
buffer_vertex: BufferStorage,
|
#[allow(dead_code)]
|
||||||
buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
|
|
||||||
|
|
||||||
// maybe this should just be a Uuid and the material can be retrieved though
|
|
||||||
// MeshPass's `material_buffers` field?
|
|
||||||
material: Option<Rc<Material>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Component)]
|
|
||||||
struct InterpTransform {
|
|
||||||
last_transform: Transform,
|
|
||||||
alpha: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MeshPass {
|
pub struct MeshPass {
|
||||||
transforms: Option<TransformBuffers>,
|
|
||||||
mesh_buffers: FxHashMap<uuid::Uuid, MeshBufferStorage>,
|
|
||||||
render_jobs: VecDeque<RenderJob>,
|
|
||||||
|
|
||||||
texture_bind_group_layout: Option<Rc<wgpu::BindGroupLayout>>,
|
|
||||||
material_buffer: Option<wgpu::Buffer>,
|
|
||||||
material_buffers: FxHashMap<uuid::Uuid, Rc<Material>>,
|
|
||||||
entity_meshes: FxHashMap<Entity, uuid::Uuid>,
|
|
||||||
|
|
||||||
default_texture: Option<RenderTexture>,
|
default_texture: Option<RenderTexture>,
|
||||||
|
|
||||||
pipeline: Option<RenderPipeline>,
|
pipeline: Option<RenderPipeline>,
|
||||||
material_bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
material_bgl: Arc<wgpu::BindGroupLayout>,
|
||||||
|
transform_buffers: Option<ResourceData>,
|
||||||
|
// TODO: find a better way to extract these resources from the main world to be used in the
|
||||||
|
// render stage.
|
||||||
|
render_meshes: Option<ResourceData>,
|
||||||
|
mesh_buffers: Option<ResourceData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MeshPass {
|
impl MeshPass {
|
||||||
pub fn new() -> Self {
|
pub fn new(material_bgl: Arc<wgpu::BindGroupLayout>) -> Self {
|
||||||
Self::default()
|
Self {
|
||||||
}
|
default_texture: None,
|
||||||
|
pipeline: None,
|
||||||
/// Checks if the mesh buffers in the GPU need to be updated.
|
material_bgl,
|
||||||
#[instrument(skip(self, device, queue, mesh_han))]
|
transform_buffers: None,
|
||||||
fn check_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh_han: &ResHandle<Mesh>) {
|
render_meshes: None,
|
||||||
let mesh_uuid = mesh_han.uuid();
|
mesh_buffers: None,
|
||||||
|
|
||||||
if let (Some(mesh), Some(buffers)) = (mesh_han.data_ref(), self.mesh_buffers.get_mut(&mesh_uuid)) {
|
|
||||||
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
|
||||||
let vertices = mesh.position().unwrap();
|
|
||||||
if buffers.buffer_vertex.count() != vertices.len() {
|
|
||||||
debug!("Recreating buffers for mesh {}", mesh_uuid.to_string());
|
|
||||||
let (vert, idx) = self.create_vertex_index_buffers(device, &mesh);
|
|
||||||
|
|
||||||
// have to re-get buffers because of borrow checker
|
|
||||||
let buffers = self.mesh_buffers.get_mut(&mesh_uuid).unwrap();
|
|
||||||
buffers.buffer_indices = idx;
|
|
||||||
buffers.buffer_vertex = vert;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update vertices
|
|
||||||
let vertex_buffer = buffers.buffer_vertex.buffer();
|
|
||||||
let vertices = vertices.as_slice();
|
|
||||||
// align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT)
|
|
||||||
let (_, vertices, _) = bytemuck::pod_align_to::<Vec3, u32>(vertices);
|
|
||||||
queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(vertices));
|
|
||||||
|
|
||||||
// update the indices if they're given
|
|
||||||
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
|
||||||
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
|
||||||
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
|
||||||
lyra_resource::gltf::MeshIndices::U16(v) => bytemuck::pod_align_to::<u16, u32>(v).1,
|
|
||||||
lyra_resource::gltf::MeshIndices::U32(v) => bytemuck::pod_align_to::<u32, u32>(v).1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let index_buffer = index_buffer.1.buffer();
|
|
||||||
queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(aligned_indices));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(self, device, mesh))]
|
|
||||||
fn create_vertex_index_buffers(&mut self, device: &wgpu::Device, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
|
||||||
let positions = mesh.position().unwrap();
|
|
||||||
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
|
||||||
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
|
|
||||||
let normals = mesh.normals().unwrap();
|
|
||||||
|
|
||||||
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
|
|
||||||
|
|
||||||
let mut vertex_inputs = vec![];
|
|
||||||
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
|
|
||||||
vertex_inputs.push(Vertex::new(*v, *t, *n));
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer_init(
|
|
||||||
&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Vertex Buffer"),
|
|
||||||
contents: bytemuck::cast_slice(vertex_inputs.as_slice()),//vertex_combined.as_slice(),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages:: COPY_DST,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len());
|
|
||||||
|
|
||||||
let indices = match mesh.indices.as_ref() {
|
|
||||||
Some(indices) => {
|
|
||||||
let (idx_type, len, contents) = match indices {
|
|
||||||
lyra_resource::gltf::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v)),
|
|
||||||
lyra_resource::gltf::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let index_buffer = device.create_buffer_init(
|
|
||||||
&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Index Buffer"),
|
|
||||||
contents,
|
|
||||||
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages:: COPY_DST,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffer_indices = BufferStorage::new(index_buffer, 0, len);
|
|
||||||
|
|
||||||
Some((idx_type, buffer_indices))
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
( vertex_buffer, indices )
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(self, device, queue, mesh))]
|
|
||||||
fn create_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh: &Mesh) -> MeshBufferStorage {
|
|
||||||
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(device, mesh);
|
|
||||||
|
|
||||||
let material = mesh.material.as_ref()
|
|
||||||
.expect("Material resource not loaded yet");
|
|
||||||
let material_ref = material.data_ref()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let material = self.material_buffers.entry(material.uuid())
|
|
||||||
.or_insert_with(|| {
|
|
||||||
debug!(uuid=material.uuid().to_string(), "Sending material to gpu");
|
|
||||||
Rc::new(Material::from_resource(device, queue, self.texture_bind_group_layout.clone().unwrap(), &material_ref))
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: support material uniforms from multiple uniforms
|
|
||||||
let uni = MaterialUniform::from(&**material);
|
|
||||||
queue.write_buffer(self.material_buffer.as_ref().unwrap(), 0, bytemuck::bytes_of(&uni));
|
|
||||||
|
|
||||||
MeshBufferStorage {
|
|
||||||
buffer_vertex: vertex_buffer,
|
|
||||||
buffer_indices,
|
|
||||||
material: Some(material.clone()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
fn transform_buffers(&self) -> AtomicRef<TransformBuffers> {
|
||||||
#[instrument(skip(self, device, queue, mesh, entity))]
|
self.transform_buffers
|
||||||
fn process_mesh(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, entity: Entity, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
.as_ref()
|
||||||
#[allow(clippy::map_entry)]
|
.unwrap()
|
||||||
if !self.mesh_buffers.contains_key(&mesh_uuid) {
|
.get()
|
||||||
// create the mesh's buffers
|
}
|
||||||
let buffers = self.create_mesh_buffers(device, queue, mesh);
|
|
||||||
self.mesh_buffers.insert(mesh_uuid, buffers);
|
|
||||||
self.entity_meshes.insert(entity, mesh_uuid);
|
|
||||||
|
|
||||||
true
|
fn render_meshes(&self) -> AtomicRef<RenderMeshes> {
|
||||||
} else { false }
|
self.render_meshes
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mesh_buffers(&self) -> AtomicRef<RenderAssets<MeshBufferStorage>> {
|
||||||
|
self.mesh_buffers
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for MeshPass {
|
impl Node for MeshPass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
_: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::NodeDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
|
|
||||||
let device = graph.device();
|
|
||||||
|
|
||||||
let transforms = TransformBuffers::new(device);
|
|
||||||
//let transform_bgl = transforms.bindgroup_layout.clone();
|
|
||||||
self.transforms = Some(transforms);
|
|
||||||
|
|
||||||
let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(device));
|
|
||||||
self.texture_bind_group_layout = Some(texture_bind_group_layout.clone());
|
|
||||||
|
|
||||||
let (material_bgl, material_bg, material_buf, _) = BufferWrapper::builder()
|
|
||||||
.label_prefix("material")
|
|
||||||
.visibility(wgpu::ShaderStages::FRAGMENT)
|
|
||||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
|
||||||
.contents(&[MaterialUniform::default()])
|
|
||||||
.finish_parts(device);
|
|
||||||
let material_bgl = Rc::new(material_bgl);
|
|
||||||
self.material_bgl = Some(material_bgl.clone());
|
|
||||||
let material_bg = Rc::new(material_bg);
|
|
||||||
|
|
||||||
self.material_buffer = Some(material_buf);
|
|
||||||
|
|
||||||
// load the default texture
|
// load the default texture
|
||||||
let bytes = include_bytes!("../../default_texture.png");
|
//let bytes = include_bytes!("../../default_texture.png");
|
||||||
self.default_texture = Some(RenderTexture::from_bytes(device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
|
//self.default_texture = Some(RenderTexture::from_bytes(device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
|
||||||
|
|
||||||
// get surface config format
|
|
||||||
/* let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
|
|
||||||
.and_then(|s| s.as_render_target())
|
|
||||||
.expect("missing main render target");
|
|
||||||
let surface_config_format = main_rt.format();
|
|
||||||
drop(main_rt); */
|
|
||||||
|
|
||||||
/* let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
|
||||||
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
|
|
||||||
let light_grid_bgl = graph
|
|
||||||
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
|
||||||
|
|
||||||
let shader = Rc::new(Shader {
|
|
||||||
label: Some("base_shader".into()),
|
|
||||||
source: include_str!("../../shaders/base.wgsl").to_string(),
|
|
||||||
}); */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NodeDesc::new(
|
NodeDesc::new(
|
||||||
NodeType::Render,
|
NodeType::Render,
|
||||||
None,
|
None,
|
||||||
/* Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
|
|
||||||
label: Some("meshes".into()),
|
|
||||||
layouts: vec![
|
|
||||||
texture_bind_group_layout.clone(),
|
|
||||||
transform_bgl,
|
|
||||||
camera_bgl.clone(),
|
|
||||||
lights_bgl.clone(),
|
|
||||||
material_bgl.clone(),
|
|
||||||
texture_bind_group_layout,
|
|
||||||
light_grid_bgl.clone(),
|
|
||||||
],
|
|
||||||
push_constant_ranges: vec![],
|
|
||||||
vertex: VertexState {
|
|
||||||
module: shader.clone(),
|
|
||||||
entry_point: "vs_main".into(),
|
|
||||||
buffers: vec![
|
|
||||||
Vertex::desc().into(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fragment: Some(FragmentState {
|
|
||||||
module: shader,
|
|
||||||
entry_point: "fs_main".into(),
|
|
||||||
targets: vec![Some(wgpu::ColorTargetState {
|
|
||||||
format: surface_config_format,
|
|
||||||
blend: Some(wgpu::BlendState::REPLACE),
|
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: RenderTexture::DEPTH_FORMAT,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: wgpu::CompareFunction::Less,
|
|
||||||
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
|
|
||||||
bias: wgpu::DepthBiasState::default(),
|
|
||||||
}),
|
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
|
||||||
multisample: wgpu::MultisampleState::default(),
|
|
||||||
multiview: None,
|
|
||||||
})), */
|
|
||||||
vec![
|
vec![
|
||||||
(&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
|
//(&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, graph, world, context))]
|
#[instrument(skip(self, graph, world))]
|
||||||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
fn prepare(
|
||||||
let device = &context.device;
|
&mut self,
|
||||||
let queue = &context.queue;
|
graph: &mut RenderGraph,
|
||||||
let render_limits = device.limits();
|
world: &mut lyra_ecs::World,
|
||||||
|
_: &mut RenderGraphContext,
|
||||||
let last_epoch = world.current_tick();
|
) {
|
||||||
let mut alive_entities = HashSet::new();
|
|
||||||
|
|
||||||
let view = world.view_iter::<(
|
|
||||||
Entities,
|
|
||||||
&Transform,
|
|
||||||
TickOf<Transform>,
|
|
||||||
Or<
|
|
||||||
(&MeshHandle, TickOf<MeshHandle>),
|
|
||||||
(&SceneHandle, TickOf<SceneHandle>)
|
|
||||||
>,
|
|
||||||
Option<&mut InterpTransform>,
|
|
||||||
Res<DeltaTime>,
|
|
||||||
)>();
|
|
||||||
|
|
||||||
// used to store InterpTransform components to add to entities later
|
|
||||||
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
|
|
||||||
|
|
||||||
for (
|
|
||||||
entity,
|
|
||||||
transform,
|
|
||||||
_transform_epoch,
|
|
||||||
(
|
|
||||||
mesh_pair,
|
|
||||||
scene_pair
|
|
||||||
),
|
|
||||||
interp_tran,
|
|
||||||
delta_time,
|
|
||||||
) in view
|
|
||||||
{
|
|
||||||
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
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
|
||||||
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) = mesh_han.data_ref() {
|
|
||||||
// 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(device, queue, entity, &mesh, mesh_han.uuid())
|
|
||||||
&& mesh_epoch == last_epoch {
|
|
||||||
self.check_mesh_buffers(device, queue, &mesh_han);
|
|
||||||
}
|
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
|
||||||
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 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 let Some((scene_han, scene_epoch)) = scene_pair {
|
|
||||||
if let Some(scene) = scene_han.data_ref() {
|
|
||||||
if scene_epoch == last_epoch {
|
|
||||||
let view = scene.world().view::<(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() {
|
|
||||||
let mesh_interpo = interp_transform + **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(device, queue, entity, &mesh, mesh_han.uuid())
|
|
||||||
&& scene_epoch == last_epoch {
|
|
||||||
self.check_mesh_buffers(device, queue, &mesh_han);
|
|
||||||
}
|
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
|
||||||
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 shader = material.shader_uuid.unwrap_or(0);
|
|
||||||
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
|
||||||
self.render_jobs.push_back(job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (en, interp) in component_queue {
|
|
||||||
world.insert(en, interp);
|
|
||||||
}
|
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
|
||||||
transforms.send_to_gpu(queue);
|
|
||||||
|
|
||||||
if self.pipeline.is_none() {
|
if self.pipeline.is_none() {
|
||||||
let device = graph.device();
|
let device = graph.device();
|
||||||
let surface_config_format = graph.view_target().format();
|
let surface_config_format = graph.view_target().format();
|
||||||
|
|
||||||
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
||||||
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
|
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
|
||||||
let light_grid_bgl = graph
|
let light_grid_bgl =
|
||||||
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
graph.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||||
|
|
||||||
let shader = Rc::new(Shader {
|
let shader = Rc::new(Shader {
|
||||||
label: Some("base_shader".into()),
|
label: Some("base_shader".into()),
|
||||||
source: include_str!("../../shaders/base.wgsl").to_string(),
|
source: include_str!("../../shaders/base.wgsl").to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
self.pipeline = Some(RenderPipeline::create(device, &RenderPipelineDescriptor {
|
|
||||||
|
let transforms = world
|
||||||
|
.try_get_resource_data::<TransformBuffers>()
|
||||||
|
.expect("Missing transform buffers");
|
||||||
|
self.transform_buffers = Some(transforms.clone());
|
||||||
|
|
||||||
|
let render_meshes = world
|
||||||
|
.try_get_resource_data::<RenderMeshes>()
|
||||||
|
.expect("Missing transform buffers");
|
||||||
|
self.render_meshes = Some(render_meshes.clone());
|
||||||
|
|
||||||
|
let mesh_buffers = world
|
||||||
|
.try_get_resource_data::<RenderAssets<MeshBufferStorage>>()
|
||||||
|
.expect("Missing render meshes");
|
||||||
|
self.mesh_buffers = Some(mesh_buffers.clone());
|
||||||
|
|
||||||
|
|
||||||
|
let transforms = transforms.get::<TransformBuffers>();
|
||||||
|
|
||||||
|
self.pipeline = Some(RenderPipeline::create(
|
||||||
|
device,
|
||||||
|
&RenderPipelineDescriptor {
|
||||||
label: Some("meshes".into()),
|
label: Some("meshes".into()),
|
||||||
layouts: vec![
|
layouts: vec![
|
||||||
self.texture_bind_group_layout.as_ref().unwrap().clone(),
|
self.material_bgl.clone(),
|
||||||
//transform_bgl
|
transforms.bindgroup_layout.clone(),
|
||||||
self.transforms.as_ref().unwrap().bindgroup_layout.clone(),
|
|
||||||
camera_bgl.clone(),
|
camera_bgl.clone(),
|
||||||
lights_bgl.clone(),
|
lights_bgl.clone(),
|
||||||
self.material_bgl.as_ref().unwrap().clone(),
|
|
||||||
self.texture_bind_group_layout.as_ref().unwrap().clone(),
|
|
||||||
light_grid_bgl.clone(),
|
light_grid_bgl.clone(),
|
||||||
],
|
],
|
||||||
push_constant_ranges: vec![],
|
push_constant_ranges: vec![],
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
module: shader.clone(),
|
module: shader.clone(),
|
||||||
entry_point: "vs_main".into(),
|
entry_point: "vs_main".into(),
|
||||||
buffers: vec![
|
buffers: vec![Vertex::desc().into()],
|
||||||
Vertex::desc().into(),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
fragment: Some(FragmentState {
|
fragment: Some(FragmentState {
|
||||||
module: shader,
|
module: shader,
|
||||||
|
@ -491,7 +168,8 @@ impl Node for MeshPass {
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
multisample: wgpu::MultisampleState::default(),
|
multisample: wgpu::MultisampleState::default(),
|
||||||
multiview: None,
|
multiview: None,
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,22 +196,23 @@ impl Node for MeshPass {
|
||||||
.as_texture_view()
|
.as_texture_view()
|
||||||
.expect("BasePassSlots::DepthTextureView was not a TextureView slot");
|
.expect("BasePassSlots::DepthTextureView was not a TextureView slot");
|
||||||
|
|
||||||
let camera_bg = graph
|
let camera_bg = graph.bind_group(BasePassSlots::Camera);
|
||||||
.bind_group(BasePassSlots::Camera);
|
|
||||||
|
|
||||||
let lights_bg = graph
|
let lights_bg = graph.bind_group(LightBasePassSlots::Lights);
|
||||||
.bind_group(LightBasePassSlots::Lights);
|
|
||||||
|
|
||||||
let light_grid_bg = graph
|
let light_grid_bg = graph.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||||
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
|
|
||||||
|
|
||||||
let material_bg = graph
|
//let material_bg = graph.bind_group(MeshesPassSlots::Material);
|
||||||
.bind_group(MeshesPassSlots::Material);
|
|
||||||
|
|
||||||
/* let pipeline = graph.pipeline(context.label.clone())
|
/* let pipeline = graph.pipeline(context.label.clone())
|
||||||
.expect("Failed to find pipeline for MeshPass"); */
|
.expect("Failed to find pipeline for MeshPass"); */
|
||||||
let pipeline = self.pipeline.as_ref().unwrap();
|
let pipeline = self.pipeline.as_ref().unwrap();
|
||||||
|
|
||||||
|
let transforms = self.transform_buffers();
|
||||||
|
let render_meshes = self.render_meshes();
|
||||||
|
let mesh_buffers = self.mesh_buffers();
|
||||||
|
|
||||||
|
{
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("Render Pass"),
|
label: Some("Render Pass"),
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
@ -562,13 +241,11 @@ impl Node for MeshPass {
|
||||||
|
|
||||||
pass.set_pipeline(pipeline);
|
pass.set_pipeline(pipeline);
|
||||||
|
|
||||||
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();
|
//let default_texture = self.default_texture.as_ref().unwrap();
|
||||||
let default_texture = self.default_texture.as_ref().unwrap();
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
|
||||||
|
|
||||||
while let Some(job) = self.render_jobs.pop_front() {
|
for job in render_meshes.iter() {
|
||||||
// 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);
|
let buffers = mesh_buffers.get(&job.mesh_uuid);
|
||||||
if buffers.is_none() {
|
if buffers.is_none() {
|
||||||
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
||||||
continue;
|
continue;
|
||||||
|
@ -576,7 +253,7 @@ impl Node for MeshPass {
|
||||||
let buffers = buffers.unwrap();
|
let buffers = buffers.unwrap();
|
||||||
|
|
||||||
// Bind the optional texture
|
// Bind the optional texture
|
||||||
if let Some(tex) = buffers.material.as_ref()
|
/* if let Some(tex) = buffers.material.as_ref()
|
||||||
.and_then(|m| m.diffuse_texture.as_ref()) {
|
.and_then(|m| m.diffuse_texture.as_ref()) {
|
||||||
pass.set_bind_group(0, tex.bind_group(), &[]);
|
pass.set_bind_group(0, tex.bind_group(), &[]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -589,32 +266,44 @@ impl Node for MeshPass {
|
||||||
pass.set_bind_group(5, tex.bind_group(), &[]);
|
pass.set_bind_group(5, tex.bind_group(), &[]);
|
||||||
} else {
|
} else {
|
||||||
pass.set_bind_group(5, default_texture.bind_group(), &[]);
|
pass.set_bind_group(5, default_texture.bind_group(), &[]);
|
||||||
|
} */
|
||||||
|
if let Some(mat) = buffers.material.as_ref() {
|
||||||
|
pass.set_bind_group(0, &mat.bind_group, &[]);
|
||||||
|
} else {
|
||||||
|
todo!("cannot render mesh without material");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||||
let bindgroup = transforms.bind_group(job.transform_id);
|
let bindgroup = transforms.bind_group(job.transform_id);
|
||||||
let offset = transforms.buffer_offset(job.transform_id);
|
let offset = transforms.buffer_offset(job.transform_id);
|
||||||
pass.set_bind_group(1, bindgroup, &[ offset, ]);
|
pass.set_bind_group(1, bindgroup, &[offset]);
|
||||||
|
|
||||||
pass.set_bind_group(2, camera_bg, &[]);
|
pass.set_bind_group(2, camera_bg, &[]);
|
||||||
pass.set_bind_group(3, lights_bg, &[]);
|
pass.set_bind_group(3, lights_bg, &[]);
|
||||||
pass.set_bind_group(4, material_bg, &[]);
|
//pass.set_bind_group(4, material_bg, &[]);
|
||||||
|
|
||||||
pass.set_bind_group(6, light_grid_bg, &[]);
|
pass.set_bind_group(4, light_grid_bg, &[]);
|
||||||
|
|
||||||
// if this mesh uses indices, use them to draw the mesh
|
// if this mesh uses indices, use them to draw the mesh
|
||||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||||
let indices_len = indices.count() as u32;
|
let indices_len = indices.count() as u32;
|
||||||
|
|
||||||
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
pass.set_vertex_buffer(
|
||||||
|
buffers.buffer_vertex.slot(),
|
||||||
|
buffers.buffer_vertex.buffer().slice(..),
|
||||||
|
);
|
||||||
pass.set_index_buffer(indices.buffer().slice(..), *idx_type);
|
pass.set_index_buffer(indices.buffer().slice(..), *idx_type);
|
||||||
pass.draw_indexed(0..indices_len, 0, 0..1);
|
pass.draw_indexed(0..indices_len, 0, 0..1);
|
||||||
} else {
|
} else {
|
||||||
let vertex_count = buffers.buffer_vertex.count();
|
let vertex_count = buffers.buffer_vertex.count();
|
||||||
|
|
||||||
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
pass.set_vertex_buffer(
|
||||||
|
buffers.buffer_vertex.slot(),
|
||||||
|
buffers.buffer_vertex.buffer().slice(..),
|
||||||
|
);
|
||||||
pass.draw(0..vertex_count as u32, 0..1);
|
pass.draw(0..vertex_count as u32, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,3 +21,9 @@ pub use tint::*;
|
||||||
|
|
||||||
mod fxaa;
|
mod fxaa;
|
||||||
pub use fxaa::*;
|
pub use fxaa::*;
|
||||||
|
|
||||||
|
/* mod shadow_maps;
|
||||||
|
pub use shadow_maps::*; */
|
||||||
|
|
||||||
|
mod mesh_prepare;
|
||||||
|
pub use mesh_prepare::*;
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ pub struct TintPassLabel;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TintPass {
|
pub struct TintPass {
|
||||||
target_sampler: Option<wgpu::Sampler>,
|
target_sampler: Option<wgpu::Sampler>,
|
||||||
bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
/// Store bind groups for the input textures.
|
/// Store bind groups for the input textures.
|
||||||
/// The texture may change due to resizes, or changes to the view target chain
|
/// The texture may change due to resizes, or changes to the view target chain
|
||||||
/// from other nodes.
|
/// from other nodes.
|
||||||
|
@ -54,7 +54,7 @@ impl Node for TintPass {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
let bgl = Rc::new(bgl);
|
let bgl = Arc::new(bgl);
|
||||||
self.bgl = Some(bgl.clone());
|
self.bgl = Some(bgl.clone());
|
||||||
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor::default()));
|
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor::default()));
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use lyra_ecs::{Entity, Tick, World};
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
pub use spotlight::*;
|
pub use spotlight::*;
|
||||||
|
|
||||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, rc::Rc};
|
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, sync::Arc};
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
|
|
||||||
|
@ -98,9 +98,9 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LightUniformBuffers {
|
pub(crate) struct LightUniformBuffers {
|
||||||
pub buffer: Rc<wgpu::Buffer>,
|
pub buffer: Arc<wgpu::Buffer>,
|
||||||
pub bind_group: Rc<wgpu::BindGroup>,
|
pub bind_group: Arc<wgpu::BindGroup>,
|
||||||
pub bind_group_layout: Rc<wgpu::BindGroupLayout>,
|
pub bind_group_layout: Arc<wgpu::BindGroupLayout>,
|
||||||
max_light_count: u64,
|
max_light_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +155,9 @@ impl LightUniformBuffers {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
buffer: Rc::new(buffer),
|
buffer: Arc::new(buffer),
|
||||||
bind_group: Rc::new(bindgroup),
|
bind_group: Arc::new(bindgroup),
|
||||||
bind_group_layout: Rc::new(bindgroup_layout),
|
bind_group_layout: Arc::new(bindgroup_layout),
|
||||||
max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64,
|
max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use lyra_resource::{ResHandle, Texture};
|
use lyra_resource::{ResHandle, Texture};
|
||||||
|
|
||||||
use super::texture::RenderTexture;
|
use super::texture::RenderTexture;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct MaterialSpecular {
|
pub struct MaterialSpecular {
|
||||||
pub factor: f32,
|
pub factor: f32,
|
||||||
pub color_factor: glam::Vec3,
|
pub color_factor: glam::Vec3,
|
||||||
|
@ -11,7 +12,7 @@ pub struct MaterialSpecular {
|
||||||
pub color_texture: Option<RenderTexture>,
|
pub color_texture: Option<RenderTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Rc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
|
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
|
||||||
if let Some(tex) = i {
|
if let Some(tex) = i {
|
||||||
RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok()
|
RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok()
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +21,7 @@ fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Rc<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialSpecular {
|
impl MaterialSpecular {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
||||||
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
|
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
|
||||||
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
|
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ pub struct Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material {
|
impl Material {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
||||||
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
|
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
|
||||||
|
|
||||||
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{num::NonZeroU32, rc::Rc};
|
use std::{num::NonZeroU32, sync::Arc};
|
||||||
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
@ -23,11 +23,11 @@ impl RenderBuffer {
|
||||||
|
|
||||||
pub struct BindGroupPair {
|
pub struct BindGroupPair {
|
||||||
pub bindgroup: wgpu::BindGroup,
|
pub bindgroup: wgpu::BindGroup,
|
||||||
pub layout: Rc<wgpu::BindGroupLayout>,
|
pub layout: Arc<wgpu::BindGroupLayout>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BindGroupPair {
|
impl BindGroupPair {
|
||||||
pub fn create_bind_group(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self {
|
pub fn create_bind_group(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self {
|
||||||
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
layout: &layout,
|
layout: &layout,
|
||||||
entries,
|
entries,
|
||||||
|
@ -43,7 +43,7 @@ impl BindGroupPair {
|
||||||
pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self {
|
pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bindgroup,
|
bindgroup,
|
||||||
layout: Rc::new(layout),
|
layout: Arc::new(layout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl BufferWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the bind group layout, the bind group, and the buffer out of the wrapper.
|
/// Take the bind group layout, the bind group, and the buffer out of the wrapper.
|
||||||
pub fn parts(self) -> (Option<Rc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
|
pub fn parts(self) -> (Option<Arc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
|
||||||
if let Some(pair) = self.bindgroup_pair {
|
if let Some(pair) = self.bindgroup_pair {
|
||||||
(Some(pair.layout), Some(pair.bindgroup), self.inner_buf)
|
(Some(pair.layout), Some(pair.bindgroup), self.inner_buf)
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,7 +297,7 @@ impl BufferWrapperBuilder {
|
||||||
|
|
||||||
BindGroupPair {
|
BindGroupPair {
|
||||||
bindgroup: bg,
|
bindgroup: bg,
|
||||||
layout: Rc::new(bg_layout),
|
layout: Arc::new(bg_layout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -308,7 +308,7 @@ impl BufferWrapperBuilder {
|
||||||
len: Some(self.count.unwrap_or_default() as usize),
|
len: Some(self.count.unwrap_or_default() as usize),
|
||||||
} */
|
} */
|
||||||
|
|
||||||
(Rc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize)
|
(Arc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(self, device: &wgpu::Device) -> BufferWrapper {
|
pub fn finish(self, device: &wgpu::Device) -> BufferWrapper {
|
||||||
|
@ -316,7 +316,7 @@ impl BufferWrapperBuilder {
|
||||||
|
|
||||||
BufferWrapper {
|
BufferWrapper {
|
||||||
bindgroup_pair: Some(BindGroupPair {
|
bindgroup_pair: Some(BindGroupPair {
|
||||||
layout: Rc::new(bgl),
|
layout: Arc::new(bgl),
|
||||||
bindgroup: bg
|
bindgroup: bg
|
||||||
}),
|
}),
|
||||||
inner_buf: buff,
|
inner_buf: buff,
|
||||||
|
|
|
@ -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, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget};
|
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget};
|
||||||
|
|
||||||
use super::graph::RenderGraph;
|
use super::graph::RenderGraph;
|
||||||
use super::{resource::RenderPipeline, render_job::RenderJob};
|
use super::{resource::RenderPipeline, render_job::RenderJob};
|
||||||
|
@ -147,9 +147,13 @@ impl BasicRenderer {
|
||||||
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
||||||
|
|
||||||
debug!("Adding mesh pass");
|
debug!("Adding mesh pass");
|
||||||
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new());
|
let mesh_prep = MeshPrepNode::new(&device);
|
||||||
|
let material_bgl = mesh_prep.material_bgl.clone();
|
||||||
|
forward_plus_graph.add_node(MeshPrepNodeLabel, mesh_prep);
|
||||||
|
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
|
||||||
|
|
||||||
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
||||||
|
forward_plus_graph.add_edge(MeshPrepNodeLabel, MeshesPassLabel);
|
||||||
|
|
||||||
main_graph.add_sub_graph(TestSubGraphLabel, forward_plus_graph);
|
main_graph.add_sub_graph(TestSubGraphLabel, forward_plus_graph);
|
||||||
main_graph.add_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel,
|
main_graph.add_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ops::Deref, rc::Rc};
|
use std::{ops::Deref, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use wgpu::PipelineLayout;
|
use wgpu::PipelineLayout;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use super::Shader;
|
||||||
//#[derive(Debug, Clone)]
|
//#[derive(Debug, Clone)]
|
||||||
pub struct ComputePipelineDescriptor {
|
pub struct ComputePipelineDescriptor {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
|
||||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||||
// TODO: make this a ResHandle<Shader>
|
// TODO: make this a ResHandle<Shader>
|
||||||
/// The compiled shader module for the stage.
|
/// The compiled shader module for the stage.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
use std::{num::NonZeroU32, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use wgpu::PipelineLayout;
|
use wgpu::PipelineLayout;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use super::{FragmentState, VertexState};
|
||||||
//#[derive(Debug, Clone)]
|
//#[derive(Debug, Clone)]
|
||||||
pub struct RenderPipelineDescriptor {
|
pub struct RenderPipelineDescriptor {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
|
||||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||||
pub vertex: VertexState,
|
pub vertex: VertexState,
|
||||||
pub fragment: Option<FragmentState>,
|
pub fragment: Option<FragmentState>,
|
||||||
|
@ -87,7 +87,7 @@ impl RenderPipeline {
|
||||||
// an Rc was used here so that this shader could be reused by the fragment stage if
|
// an Rc was used here so that this shader could be reused by the fragment stage if
|
||||||
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||||
// the borrow checker
|
// the borrow checker
|
||||||
let vrtx_shad = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let vrtx_shad = Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: desc.vertex.module.label.as_deref(),
|
label: desc.vertex.module.label.as_deref(),
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||||
&desc.vertex.module.source,
|
&desc.vertex.module.source,
|
||||||
|
@ -103,7 +103,7 @@ impl RenderPipeline {
|
||||||
if f.module == desc.vertex.module {
|
if f.module == desc.vertex.module {
|
||||||
vrtx_shad.clone()
|
vrtx_shad.clone()
|
||||||
} else {
|
} else {
|
||||||
Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: f.module.label.as_deref(),
|
label: f.module.label.as_deref(),
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -87,28 +87,31 @@ fn vs_main(
|
||||||
// Fragment shader
|
// Fragment shader
|
||||||
|
|
||||||
struct Material {
|
struct Material {
|
||||||
ambient: vec4<f32>,
|
ambient: vec3<f32>,
|
||||||
diffuse: vec4<f32>,
|
diffuse: vec3<f32>,
|
||||||
specular: vec4<f32>,
|
|
||||||
shininess: f32,
|
shininess: f32,
|
||||||
|
specular_factor: f32,
|
||||||
|
specular_color: vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var t_diffuse: texture_2d<f32>;
|
var<uniform> u_material: Material;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
|
var t_diffuse: texture_2d<f32>;
|
||||||
|
@group(0) @binding(2)
|
||||||
var s_diffuse: sampler;
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
@group(4) @binding(0)
|
/*@group(4) @binding(0)
|
||||||
var<uniform> u_material: Material;
|
var<uniform> u_material: Material;
|
||||||
|
|
||||||
@group(5) @binding(0)
|
@group(5) @binding(0)
|
||||||
var t_specular: texture_2d<f32>;
|
var t_specular: texture_2d<f32>;
|
||||||
@group(5) @binding(1)
|
@group(5) @binding(1)
|
||||||
var s_specular: sampler;
|
var s_specular: sampler;*/
|
||||||
|
|
||||||
@group(6) @binding(0)
|
@group(4) @binding(0)
|
||||||
var<storage, read_write> u_light_indices: array<u32>;
|
var<storage, read_write> u_light_indices: array<u32>;
|
||||||
@group(6) @binding(1)
|
@group(4) @binding(1)
|
||||||
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // vec2<u32>
|
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // vec2<u32>
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
|
@ -118,7 +121,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
let specular_color: vec3<f32> = textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
let specular_color: vec3<f32> = vec3<f32>(0.0); //textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
||||||
var light_res = vec3<f32>(0.0);
|
var light_res = vec3<f32>(0.0);
|
||||||
|
|
||||||
if (object_color.a < ALPHA_CUTOFF) {
|
if (object_color.a < ALPHA_CUTOFF) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
|
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
|
||||||
|
@ -44,7 +44,7 @@ impl RenderTexture {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bind_group_pair(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair {
|
fn create_bind_group_pair(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair {
|
||||||
let bg = device.create_bind_group(
|
let bg = device.create_bind_group(
|
||||||
&wgpu::BindGroupDescriptor {
|
&wgpu::BindGroupDescriptor {
|
||||||
layout: &layout,
|
layout: &layout,
|
||||||
|
@ -68,12 +68,12 @@ impl RenderTexture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
|
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
|
||||||
let img = image::load_from_memory(bytes)?;
|
let img = image::load_from_memory(bytes)?;
|
||||||
Self::from_image(device, queue, bg_layout, &img, Some(label))
|
Self::from_image(device, queue, bg_layout, &img, Some(label))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> {
|
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> {
|
||||||
let rgba = img.to_rgba8();
|
let rgba = img.to_rgba8();
|
||||||
let dimensions = img.dimensions();
|
let dimensions = img.dimensions();
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ impl RenderTexture {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, texture_res: &ResHandle<Texture>, label: Option<&str>) -> anyhow::Result<Self> {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, texture_res: &ResHandle<Texture>, label: Option<&str>) -> anyhow::Result<Self> {
|
||||||
let texture_ref = texture_res.data_ref().unwrap();
|
let texture_ref = texture_res.data_ref().unwrap();
|
||||||
let img = texture_ref.image.data_ref().unwrap();
|
let img = texture_ref.image.data_ref().unwrap();
|
||||||
|
|
||||||
|
@ -371,7 +371,7 @@ impl RenderTexture {
|
||||||
|
|
||||||
/// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`]
|
/// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
pub(crate) fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
||||||
match wmode {
|
match wmode {
|
||||||
WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
|
WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
|
||||||
WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
|
WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
|
||||||
|
@ -381,7 +381,7 @@ fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
||||||
|
|
||||||
/// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`]
|
/// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
|
pub(crate) fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
|
||||||
match fmode {
|
match fmode {
|
||||||
FilterMode::Nearest => wgpu::FilterMode::Nearest,
|
FilterMode::Nearest => wgpu::FilterMode::Nearest,
|
||||||
FilterMode::Linear => wgpu::FilterMode::Linear,
|
FilterMode::Linear => wgpu::FilterMode::Linear,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, rc::Rc};
|
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, sync::Arc};
|
||||||
|
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::Entity;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
@ -165,7 +165,7 @@ impl<K: Hash + Eq + PartialEq + Clone, V: Clone, S: BuildHasher> CachedValMap<K,
|
||||||
/// [`TransformGroup`]s are used to represent entries in the buffer. They are used to insert,
|
/// [`TransformGroup`]s are used to represent entries in the buffer. They are used to insert,
|
||||||
/// update, and retrieve the transforms.
|
/// update, and retrieve the transforms.
|
||||||
pub struct TransformBuffers {
|
pub struct TransformBuffers {
|
||||||
pub bindgroup_layout: Rc<wgpu::BindGroupLayout>,
|
pub bindgroup_layout: Arc<wgpu::BindGroupLayout>,
|
||||||
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
||||||
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
||||||
entries: Vec<BufferEntry>,
|
entries: Vec<BufferEntry>,
|
||||||
|
@ -195,7 +195,7 @@ impl TransformBuffers {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
bindgroup_layout: Rc::new(bindgroup_layout),
|
bindgroup_layout: Arc::new(bindgroup_layout),
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()),
|
max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()),
|
||||||
limits,
|
limits,
|
||||||
|
@ -345,9 +345,6 @@ impl TransformBuffers {
|
||||||
/// Returns a boolean indicating if the buffers need to be expanded
|
/// Returns a boolean indicating if the buffers need to be expanded
|
||||||
pub fn needs_expand(&self) -> bool {
|
pub fn needs_expand(&self) -> bool {
|
||||||
false
|
false
|
||||||
/* self.entries.last()
|
|
||||||
.map(|entry| entry.len >= self.max_transform_count)
|
|
||||||
.unwrap_or(false) */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl From<gltf::material::AlphaMode> for AlphaMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Reflect)]
|
#[derive(Clone, Reflect, Default)]
|
||||||
pub struct Specular {
|
pub struct Specular {
|
||||||
/// The strength of the specular reflection, default of 1.0
|
/// The strength of the specular reflection, default of 1.0
|
||||||
pub factor: f32,
|
pub factor: f32,
|
||||||
|
|
Loading…
Reference in New Issue