Compare commits
No commits in common. "3a80c069c991bbcda8a0249133bed5784b78fd01" and "c4aebdb25dc479cba3b3c6b6f29973944194b432" have entirely different histories.
3a80c069c9
...
c4aebdb25d
26 changed files with 604 additions and 1322 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -3075,16 +3075,6 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shadows"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-std",
|
||||
"lyra-engine",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[workspace]
|
||||
members = [
|
||||
"examples/testbed",
|
||||
"lyra-resource",
|
||||
"lyra-ecs",
|
||||
"lyra-reflect",
|
||||
|
@ -13,12 +14,10 @@ members = [
|
|||
"lyra-math",
|
||||
"lyra-scene",
|
||||
|
||||
"examples/testbed",
|
||||
"examples/many-lights",
|
||||
"examples/fixed-timestep-rotating-model",
|
||||
"examples/lua-scripting",
|
||||
"examples/simple_scene",
|
||||
"examples/shadows",
|
||||
"examples/simple_scene"
|
||||
]
|
||||
|
||||
[features]
|
||||
|
|
Binary file not shown.
|
@ -1,10 +0,0 @@
|
|||
[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"
|
|
@ -1,59 +0,0 @@
|
|||
---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 ]]
|
|
@ -1,165 +0,0 @@
|
|||
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,11 +444,6 @@ impl World {
|
|||
.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.
|
||||
///
|
||||
/// Most users wont need to call this manually, its done for you through queries and views.
|
||||
|
|
|
@ -95,9 +95,9 @@ struct NodeEntry {
|
|||
struct BindGroupEntry {
|
||||
label: RenderGraphLabelValue,
|
||||
/// BindGroup
|
||||
bg: Arc<wgpu::BindGroup>,
|
||||
bg: Rc<wgpu::BindGroup>,
|
||||
/// BindGroupLayout
|
||||
layout: Option<Arc<wgpu::BindGroupLayout>>,
|
||||
layout: Option<Rc<wgpu::BindGroupLayout>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -368,23 +368,22 @@ impl RenderGraph {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroup>> {
|
||||
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> {
|
||||
self.bind_groups.get(&label.into()).map(|e| &e.bg)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroup> {
|
||||
let l = label.into();
|
||||
self.try_bind_group(l.clone()).unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))
|
||||
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> {
|
||||
self.try_bind_group(label).expect("Unknown id for bind group")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroupLayout>> {
|
||||
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> {
|
||||
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroupLayout> {
|
||||
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> {
|
||||
let l = label.into();
|
||||
self.try_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, sync::Arc};
|
||||
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc};
|
||||
|
||||
use bind_match::bind_match;
|
||||
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
|
||||
/// this will be set to, see the slots type.
|
||||
Lazy,
|
||||
TextureView(Arc<wgpu::TextureView>),
|
||||
TextureView(Rc<wgpu::TextureView>),
|
||||
Sampler(Rc<wgpu::Sampler>),
|
||||
Texture(Rc<wgpu::Texture>),
|
||||
Buffer(Arc<wgpu::Buffer>),
|
||||
Buffer(Rc<wgpu::Buffer>),
|
||||
RenderTarget(Rc<RefCell<RenderTarget>>),
|
||||
Frame(Rc<RefCell<Option<Frame>>>),
|
||||
}
|
||||
|
||||
impl SlotValue {
|
||||
pub fn as_texture_view(&self) -> Option<&Arc<wgpu::TextureView>> {
|
||||
pub fn as_texture_view(&self) -> Option<&Rc<wgpu::TextureView>> {
|
||||
bind_match!(self, Self::TextureView(v) => v)
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ impl SlotValue {
|
|||
bind_match!(self, Self::Texture(v) => v)
|
||||
}
|
||||
|
||||
pub fn as_buffer(&self) -> Option<&Arc<wgpu::Buffer>> {
|
||||
pub fn as_buffer(&self) -> Option<&Rc<wgpu::Buffer>> {
|
||||
bind_match!(self, Self::Buffer(v) => v)
|
||||
}
|
||||
|
||||
|
@ -189,8 +189,8 @@ pub struct NodeDesc {
|
|||
/// This makes the bind groups accessible to other Nodes.
|
||||
pub bind_groups: Vec<(
|
||||
RenderGraphLabelValue,
|
||||
Arc<wgpu::BindGroup>,
|
||||
Option<Arc<wgpu::BindGroupLayout>>,
|
||||
Rc<wgpu::BindGroup>,
|
||||
Option<Rc<wgpu::BindGroupLayout>>,
|
||||
)>,
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ impl NodeDesc {
|
|||
pub fn new(
|
||||
pass_type: NodeType,
|
||||
pipeline_desc: Option<PipelineDescriptor>,
|
||||
bind_groups: Vec<(&dyn RenderGraphLabel, Arc<wgpu::BindGroup>, Option<Arc<wgpu::BindGroupLayout>>)>,
|
||||
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ty: pass_type,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use glam::UVec2;
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
@ -56,8 +56,8 @@ impl Node for BasePass {
|
|||
.buffer_dynamic_offset(false)
|
||||
.contents(&[self.screen_size])
|
||||
.finish_parts(graph.device());
|
||||
let screen_size_bgl = Arc::new(screen_size_bgl);
|
||||
let screen_size_bg = Arc::new(screen_size_bg);
|
||||
let screen_size_bgl = Rc::new(screen_size_bgl);
|
||||
let screen_size_bg = Rc::new(screen_size_bg);
|
||||
|
||||
let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder()
|
||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
|
@ -66,17 +66,17 @@ impl Node for BasePass {
|
|||
.buffer_dynamic_offset(false)
|
||||
.contents(&[CameraUniform::default()])
|
||||
.finish_parts(graph.device());
|
||||
let camera_bgl = Arc::new(camera_bgl);
|
||||
let camera_bg = Arc::new(camera_bg);
|
||||
let camera_bgl = Rc::new(camera_bgl);
|
||||
let camera_bg = Rc::new(camera_bg);
|
||||
|
||||
// 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");
|
||||
depth_texture.create_bind_group(&graph.device);
|
||||
|
||||
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
||||
let depth_texture_bg = Arc::new(dt_bg_pair.bindgroup);
|
||||
let depth_texture_bg = Rc::new(dt_bg_pair.bindgroup);
|
||||
let depth_texture_bgl = dt_bg_pair.layout;
|
||||
let depth_texture_view = Arc::new(depth_texture.view);
|
||||
let depth_texture_view = Rc::new(depth_texture.view);
|
||||
|
||||
let mut desc = NodeDesc::new(
|
||||
NodeType::Node,
|
||||
|
@ -102,12 +102,12 @@ impl Node for BasePass {
|
|||
desc.add_buffer_slot(
|
||||
BasePassSlots::ScreenSize,
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Arc::new(screen_size_buf))),
|
||||
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
|
||||
);
|
||||
desc.add_buffer_slot(
|
||||
BasePassSlots::Camera,
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Arc::new(camera_buf))),
|
||||
Some(SlotValue::Buffer(Rc::new(camera_buf))),
|
||||
);
|
||||
|
||||
desc
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub struct FxaaPassLabel;
|
|||
#[derive(Debug, Default)]
|
||||
pub struct FxaaPass {
|
||||
target_sampler: Option<wgpu::Sampler>,
|
||||
bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||
bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
||||
/// Store bind groups for the input textures.
|
||||
/// The texture may change due to resizes, or changes to the view target chain
|
||||
/// from other nodes.
|
||||
|
@ -54,7 +54,7 @@ impl Node for FxaaPass {
|
|||
},
|
||||
],
|
||||
});
|
||||
let bgl = Arc::new(bgl);
|
||||
let bgl = Rc::new(bgl);
|
||||
self.bgl = Some(bgl.clone());
|
||||
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("fxaa sampler"),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, rc::Rc, sync::Arc};
|
||||
use std::{mem, rc::Rc};
|
||||
|
||||
use glam::Vec2Swizzles;
|
||||
use lyra_ecs::World;
|
||||
|
@ -63,7 +63,7 @@ impl Node for LightCullComputePass {
|
|||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let light_indices_bg_layout = Arc::new(device.create_bind_group_layout(
|
||||
let light_indices_bg_layout = Rc::new(device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
|
@ -128,7 +128,7 @@ impl Node for LightCullComputePass {
|
|||
array_layer_count: None,
|
||||
});
|
||||
|
||||
let light_indices_bg = Arc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
let light_indices_bg = Rc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &light_indices_bg_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
|
@ -194,7 +194,7 @@ impl Node for LightCullComputePass {
|
|||
desc.add_buffer_slot(
|
||||
LightCullComputePassSlots::IndexCounterBuffer,
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Arc::new(light_index_counter_buffer))),
|
||||
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
|
||||
);
|
||||
|
||||
desc
|
||||
|
|
|
@ -1,767 +0,0 @@
|
|||
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,175 +1,497 @@
|
|||
use std::{rc::Rc, sync::Arc};
|
||||
use std::{collections::{HashSet, VecDeque}, rc::Rc};
|
||||
|
||||
use lyra_ecs::{AtomicRef, ResourceData};
|
||||
use glam::Vec3;
|
||||
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 tracing::{instrument, warn};
|
||||
use lyra_math::Transform;
|
||||
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::render::{
|
||||
desc_buf_lay::DescVertexBufferLayout,
|
||||
graph::{Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext},
|
||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||
texture::RenderTexture,
|
||||
transform_buffer_storage::TransformBuffers,
|
||||
vertex::Vertex,
|
||||
use crate::{
|
||||
render::{
|
||||
desc_buf_lay::DescVertexBufferLayout, graph::{
|
||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext
|
||||
}, 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
|
||||
},
|
||||
DeltaTime,
|
||||
};
|
||||
|
||||
use super::{
|
||||
BasePassSlots, LightBasePassSlots, LightCullComputePassSlots, MeshBufferStorage, RenderAssets,
|
||||
RenderMeshes,
|
||||
};
|
||||
use super::{BasePassSlots, LightBasePassSlots, LightCullComputePassSlots};
|
||||
|
||||
type MeshHandle = ResHandle<Mesh>;
|
||||
type SceneHandle = ResHandle<SceneGraph>;
|
||||
|
||||
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
|
||||
pub struct MeshesPassLabel;
|
||||
|
||||
#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)]
|
||||
pub enum MeshesPassSlots {
|
||||
Material,
|
||||
Material
|
||||
}
|
||||
|
||||
//#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
struct MeshBufferStorage {
|
||||
buffer_vertex: BufferStorage,
|
||||
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 {
|
||||
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>,
|
||||
|
||||
pipeline: Option<RenderPipeline>,
|
||||
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>,
|
||||
material_bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
||||
}
|
||||
|
||||
impl MeshPass {
|
||||
pub fn new(material_bgl: Arc<wgpu::BindGroupLayout>) -> Self {
|
||||
Self {
|
||||
default_texture: None,
|
||||
pipeline: None,
|
||||
material_bgl,
|
||||
transform_buffers: None,
|
||||
render_meshes: None,
|
||||
mesh_buffers: None,
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Checks if the mesh buffers in the GPU need to be updated.
|
||||
#[instrument(skip(self, device, queue, mesh_han))]
|
||||
fn check_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh_han: &ResHandle<Mesh>) {
|
||||
let mesh_uuid = mesh_han.uuid();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_buffers(&self) -> AtomicRef<TransformBuffers> {
|
||||
self.transform_buffers
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get()
|
||||
#[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 )
|
||||
}
|
||||
|
||||
fn render_meshes(&self) -> AtomicRef<RenderMeshes> {
|
||||
self.render_meshes
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get()
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
|
||||
fn mesh_buffers(&self) -> AtomicRef<RenderAssets<MeshBufferStorage>> {
|
||||
self.mesh_buffers
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get()
|
||||
/// 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, entity))]
|
||||
fn process_mesh(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, entity: Entity, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
||||
#[allow(clippy::map_entry)]
|
||||
if !self.mesh_buffers.contains_key(&mesh_uuid) {
|
||||
// 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
|
||||
} else { false }
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for MeshPass {
|
||||
fn desc(
|
||||
&mut self,
|
||||
_: &mut crate::render::graph::RenderGraph,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
) -> 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
|
||||
//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());
|
||||
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());
|
||||
|
||||
// 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(
|
||||
NodeType::Render,
|
||||
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![
|
||||
//(&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
|
||||
(&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(skip(self, graph, world))]
|
||||
fn prepare(
|
||||
&mut self,
|
||||
graph: &mut RenderGraph,
|
||||
world: &mut lyra_ecs::World,
|
||||
_: &mut RenderGraphContext,
|
||||
) {
|
||||
#[instrument(skip(self, graph, world, context))]
|
||||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut 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();
|
||||
|
||||
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() {
|
||||
let device = graph.device();
|
||||
let surface_config_format = graph.view_target().format();
|
||||
|
||||
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 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(),
|
||||
});
|
||||
|
||||
|
||||
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()),
|
||||
layouts: vec![
|
||||
self.material_bgl.clone(),
|
||||
transforms.bindgroup_layout.clone(),
|
||||
camera_bgl.clone(),
|
||||
lights_bgl.clone(),
|
||||
light_grid_bgl.clone(),
|
||||
self.pipeline = Some(RenderPipeline::create(device, &RenderPipelineDescriptor {
|
||||
label: Some("meshes".into()),
|
||||
layouts: vec![
|
||||
self.texture_bind_group_layout.as_ref().unwrap().clone(),
|
||||
//transform_bgl
|
||||
self.transforms.as_ref().unwrap().bindgroup_layout.clone(),
|
||||
camera_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(),
|
||||
],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
module: shader.clone(),
|
||||
entry_point: "vs_main".into(),
|
||||
buffers: vec![
|
||||
Vertex::desc().into(),
|
||||
],
|
||||
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,
|
||||
},
|
||||
));
|
||||
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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,10 +504,10 @@ impl Node for MeshPass {
|
|||
let encoder = context.encoder.as_mut().unwrap();
|
||||
|
||||
/* let view = graph
|
||||
.slot_value(BasePassSlots::WindowTextureView)
|
||||
.unwrap()
|
||||
.as_texture_view()
|
||||
.expect("BasePassSlots::WindowTextureView was not a TextureView slot"); */
|
||||
.slot_value(BasePassSlots::WindowTextureView)
|
||||
.unwrap()
|
||||
.as_texture_view()
|
||||
.expect("BasePassSlots::WindowTextureView was not a TextureView slot"); */
|
||||
|
||||
let vt = graph.view_target();
|
||||
let view = vt.render_view();
|
||||
|
@ -196,113 +518,102 @@ impl Node for MeshPass {
|
|||
.as_texture_view()
|
||||
.expect("BasePassSlots::DepthTextureView was not a TextureView slot");
|
||||
|
||||
let camera_bg = graph.bind_group(BasePassSlots::Camera);
|
||||
let camera_bg = graph
|
||||
.bind_group(BasePassSlots::Camera);
|
||||
|
||||
let lights_bg = graph
|
||||
.bind_group(LightBasePassSlots::Lights);
|
||||
|
||||
let lights_bg = graph.bind_group(LightBasePassSlots::Lights);
|
||||
let light_grid_bg = graph
|
||||
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||
|
||||
let light_grid_bg = graph.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||
|
||||
//let material_bg = graph.bind_group(MeshesPassSlots::Material);
|
||||
let material_bg = graph
|
||||
.bind_group(MeshesPassSlots::Material);
|
||||
|
||||
/* 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 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 {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
}),
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
// enable depth buffer
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: depth_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: true,
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
// enable depth buffer
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: depth_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: true,
|
||||
}),
|
||||
});
|
||||
stencil_ops: None,
|
||||
}),
|
||||
});
|
||||
|
||||
pass.set_pipeline(pipeline);
|
||||
pass.set_pipeline(pipeline);
|
||||
|
||||
//let default_texture = self.default_texture.as_ref().unwrap();
|
||||
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();
|
||||
let default_texture = self.default_texture.as_ref().unwrap();
|
||||
let transforms = self.transforms.as_mut().unwrap();
|
||||
|
||||
for job in render_meshes.iter() {
|
||||
// get the mesh (containing vertices) and the buffers from storage
|
||||
let buffers = mesh_buffers.get(&job.mesh_uuid);
|
||||
if buffers.is_none() {
|
||||
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
||||
continue;
|
||||
}
|
||||
let buffers = buffers.unwrap();
|
||||
while let Some(job) = self.render_jobs.pop_front() {
|
||||
// get the mesh (containing vertices) and the buffers from storage
|
||||
let buffers = self.mesh_buffers.get(&job.mesh_uuid);
|
||||
if buffers.is_none() {
|
||||
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
||||
continue;
|
||||
}
|
||||
let buffers = buffers.unwrap();
|
||||
|
||||
// Bind the optional texture
|
||||
/* if let Some(tex) = buffers.material.as_ref()
|
||||
.and_then(|m| m.diffuse_texture.as_ref()) {
|
||||
pass.set_bind_group(0, tex.bind_group(), &[]);
|
||||
} else {
|
||||
pass.set_bind_group(0, default_texture.bind_group(), &[]);
|
||||
}
|
||||
// Bind the optional texture
|
||||
if let Some(tex) = buffers.material.as_ref()
|
||||
.and_then(|m| m.diffuse_texture.as_ref()) {
|
||||
pass.set_bind_group(0, tex.bind_group(), &[]);
|
||||
} else {
|
||||
pass.set_bind_group(0, default_texture.bind_group(), &[]);
|
||||
}
|
||||
|
||||
if let Some(tex) = buffers.material.as_ref()
|
||||
.and_then(|m| m.specular.as_ref())
|
||||
.and_then(|s| s.texture.as_ref().or(s.color_texture.as_ref())) {
|
||||
pass.set_bind_group(5, tex.bind_group(), &[]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
if let Some(tex) = buffers.material.as_ref()
|
||||
.and_then(|m| m.specular.as_ref())
|
||||
.and_then(|s| s.texture.as_ref().or(s.color_texture.as_ref())) {
|
||||
pass.set_bind_group(5, tex.bind_group(), &[]);
|
||||
} else {
|
||||
pass.set_bind_group(5, default_texture.bind_group(), &[]);
|
||||
}
|
||||
|
||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||
let bindgroup = transforms.bind_group(job.transform_id);
|
||||
let offset = transforms.buffer_offset(job.transform_id);
|
||||
pass.set_bind_group(1, bindgroup, &[offset]);
|
||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||
let bindgroup = transforms.bind_group(job.transform_id);
|
||||
let offset = transforms.buffer_offset(job.transform_id);
|
||||
pass.set_bind_group(1, bindgroup, &[ offset, ]);
|
||||
|
||||
pass.set_bind_group(2, camera_bg, &[]);
|
||||
pass.set_bind_group(3, lights_bg, &[]);
|
||||
//pass.set_bind_group(4, material_bg, &[]);
|
||||
pass.set_bind_group(2, camera_bg, &[]);
|
||||
pass.set_bind_group(3, lights_bg, &[]);
|
||||
pass.set_bind_group(4, material_bg, &[]);
|
||||
|
||||
pass.set_bind_group(4, light_grid_bg, &[]);
|
||||
pass.set_bind_group(6, light_grid_bg, &[]);
|
||||
|
||||
// if this mesh uses indices, use them to draw the mesh
|
||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||
let indices_len = indices.count() as u32;
|
||||
// if this mesh uses indices, use them to draw the mesh
|
||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||
let indices_len = indices.count() as u32;
|
||||
|
||||
pass.set_vertex_buffer(
|
||||
buffers.buffer_vertex.slot(),
|
||||
buffers.buffer_vertex.buffer().slice(..),
|
||||
);
|
||||
pass.set_index_buffer(indices.buffer().slice(..), *idx_type);
|
||||
pass.draw_indexed(0..indices_len, 0, 0..1);
|
||||
} else {
|
||||
let vertex_count = buffers.buffer_vertex.count();
|
||||
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
||||
pass.set_index_buffer(indices.buffer().slice(..), *idx_type);
|
||||
pass.draw_indexed(0..indices_len, 0, 0..1);
|
||||
} else {
|
||||
let vertex_count = buffers.buffer_vertex.count();
|
||||
|
||||
pass.set_vertex_buffer(
|
||||
buffers.buffer_vertex.slot(),
|
||||
buffers.buffer_vertex.buffer().slice(..),
|
||||
);
|
||||
pass.draw(0..vertex_count as u32, 0..1);
|
||||
}
|
||||
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
|
||||
pass.draw(0..vertex_count as u32, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,4 @@ mod tint;
|
|||
pub use tint::*;
|
||||
|
||||
mod fxaa;
|
||||
pub use fxaa::*;
|
||||
|
||||
/* mod shadow_maps;
|
||||
pub use shadow_maps::*; */
|
||||
|
||||
mod mesh_prepare;
|
||||
pub use mesh_prepare::*;
|
||||
pub use fxaa::*;
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub struct TintPassLabel;
|
|||
#[derive(Debug, Default)]
|
||||
pub struct TintPass {
|
||||
target_sampler: Option<wgpu::Sampler>,
|
||||
bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||
bgl: Option<Rc<wgpu::BindGroupLayout>>,
|
||||
/// Store bind groups for the input textures.
|
||||
/// The texture may change due to resizes, or changes to the view target chain
|
||||
/// from other nodes.
|
||||
|
@ -54,7 +54,7 @@ impl Node for TintPass {
|
|||
},
|
||||
],
|
||||
});
|
||||
let bgl = Arc::new(bgl);
|
||||
let bgl = Rc::new(bgl);
|
||||
self.bgl = Some(bgl.clone());
|
||||
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 spotlight::*;
|
||||
|
||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, sync::Arc};
|
||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, rc::Rc};
|
||||
|
||||
use crate::math::Transform;
|
||||
|
||||
|
@ -98,9 +98,9 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
|||
}
|
||||
|
||||
pub(crate) struct LightUniformBuffers {
|
||||
pub buffer: Arc<wgpu::Buffer>,
|
||||
pub bind_group: Arc<wgpu::BindGroup>,
|
||||
pub bind_group_layout: Arc<wgpu::BindGroupLayout>,
|
||||
pub buffer: Rc<wgpu::Buffer>,
|
||||
pub bind_group: Rc<wgpu::BindGroup>,
|
||||
pub bind_group_layout: Rc<wgpu::BindGroupLayout>,
|
||||
max_light_count: u64,
|
||||
}
|
||||
|
||||
|
@ -155,9 +155,9 @@ impl LightUniformBuffers {
|
|||
});
|
||||
|
||||
Self {
|
||||
buffer: Arc::new(buffer),
|
||||
bind_group: Arc::new(bindgroup),
|
||||
bind_group_layout: Arc::new(bindgroup_layout),
|
||||
buffer: Rc::new(buffer),
|
||||
bind_group: Rc::new(bindgroup),
|
||||
bind_group_layout: Rc::new(bindgroup_layout),
|
||||
max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use lyra_resource::{ResHandle, Texture};
|
||||
|
||||
use super::texture::RenderTexture;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MaterialSpecular {
|
||||
pub factor: f32,
|
||||
pub color_factor: glam::Vec3,
|
||||
|
@ -12,7 +11,7 @@ pub struct MaterialSpecular {
|
|||
pub color_texture: Option<RenderTexture>,
|
||||
}
|
||||
|
||||
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
|
||||
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Rc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
|
||||
if let Some(tex) = i {
|
||||
RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok()
|
||||
} else {
|
||||
|
@ -21,7 +20,7 @@ fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc
|
|||
}
|
||||
|
||||
impl MaterialSpecular {
|
||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
||||
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
|
||||
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
|
||||
|
||||
|
@ -46,7 +45,7 @@ pub struct Material {
|
|||
}
|
||||
|
||||
impl Material {
|
||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
||||
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));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{num::NonZeroU32, sync::Arc};
|
||||
use std::{num::NonZeroU32, rc::Rc};
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
|
@ -23,11 +23,11 @@ impl RenderBuffer {
|
|||
|
||||
pub struct BindGroupPair {
|
||||
pub bindgroup: wgpu::BindGroup,
|
||||
pub layout: Arc<wgpu::BindGroupLayout>,
|
||||
pub layout: Rc<wgpu::BindGroupLayout>,
|
||||
}
|
||||
|
||||
impl BindGroupPair {
|
||||
pub fn create_bind_group(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self {
|
||||
pub fn create_bind_group(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self {
|
||||
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &layout,
|
||||
entries,
|
||||
|
@ -43,7 +43,7 @@ impl BindGroupPair {
|
|||
pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self {
|
||||
Self {
|
||||
bindgroup,
|
||||
layout: Arc::new(layout),
|
||||
layout: Rc::new(layout),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ impl BufferWrapper {
|
|||
}
|
||||
|
||||
/// Take the bind group layout, the bind group, and the buffer out of the wrapper.
|
||||
pub fn parts(self) -> (Option<Arc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
|
||||
pub fn parts(self) -> (Option<Rc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
|
||||
if let Some(pair) = self.bindgroup_pair {
|
||||
(Some(pair.layout), Some(pair.bindgroup), self.inner_buf)
|
||||
} else {
|
||||
|
@ -297,7 +297,7 @@ impl BufferWrapperBuilder {
|
|||
|
||||
BindGroupPair {
|
||||
bindgroup: bg,
|
||||
layout: Arc::new(bg_layout),
|
||||
layout: Rc::new(bg_layout),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -308,7 +308,7 @@ impl BufferWrapperBuilder {
|
|||
len: Some(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)
|
||||
(Rc::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 {
|
||||
|
@ -316,7 +316,7 @@ impl BufferWrapperBuilder {
|
|||
|
||||
BufferWrapper {
|
||||
bindgroup_pair: Some(BindGroupPair {
|
||||
layout: Arc::new(bgl),
|
||||
layout: Rc::new(bgl),
|
||||
bindgroup: bg
|
||||
}),
|
||||
inner_buf: buff,
|
||||
|
|
|
@ -9,7 +9,7 @@ use lyra_game_derive::RenderGraphLabel;
|
|||
use tracing::{debug, instrument, warn};
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget};
|
||||
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget};
|
||||
|
||||
use super::graph::RenderGraph;
|
||||
use super::{resource::RenderPipeline, render_job::RenderJob};
|
||||
|
@ -147,13 +147,9 @@ impl BasicRenderer {
|
|||
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
||||
|
||||
debug!("Adding mesh pass");
|
||||
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_node(MeshesPassLabel, MeshPass::new());
|
||||
|
||||
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_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ops::Deref, rc::Rc, sync::Arc};
|
||||
use std::{ops::Deref, rc::Rc};
|
||||
|
||||
use wgpu::PipelineLayout;
|
||||
|
||||
|
@ -7,7 +7,7 @@ use super::Shader;
|
|||
//#[derive(Debug, Clone)]
|
||||
pub struct ComputePipelineDescriptor {
|
||||
pub label: Option<String>,
|
||||
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
|
||||
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{num::NonZeroU32, ops::Deref, sync::Arc};
|
||||
use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
||||
|
||||
use wgpu::PipelineLayout;
|
||||
|
||||
|
@ -7,7 +7,7 @@ use super::{FragmentState, VertexState};
|
|||
//#[derive(Debug, Clone)]
|
||||
pub struct RenderPipelineDescriptor {
|
||||
pub label: Option<String>,
|
||||
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
|
||||
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||
pub vertex: VertexState,
|
||||
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
|
||||
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||
// the borrow checker
|
||||
let vrtx_shad = Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
let vrtx_shad = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: desc.vertex.module.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
&desc.vertex.module.source,
|
||||
|
@ -103,7 +103,7 @@ impl RenderPipeline {
|
|||
if f.module == desc.vertex.module {
|
||||
vrtx_shad.clone()
|
||||
} else {
|
||||
Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: f.module.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
|
||||
}))
|
||||
|
|
|
@ -87,31 +87,28 @@ fn vs_main(
|
|||
// Fragment shader
|
||||
|
||||
struct Material {
|
||||
ambient: vec3<f32>,
|
||||
diffuse: vec3<f32>,
|
||||
ambient: vec4<f32>,
|
||||
diffuse: vec4<f32>,
|
||||
specular: vec4<f32>,
|
||||
shininess: f32,
|
||||
specular_factor: f32,
|
||||
specular_color: vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> u_material: Material;
|
||||
@group(0) @binding(1)
|
||||
var t_diffuse: texture_2d<f32>;
|
||||
@group(0) @binding(2)
|
||||
@group(0) @binding(1)
|
||||
var s_diffuse: sampler;
|
||||
|
||||
/*@group(4) @binding(0)
|
||||
@group(4) @binding(0)
|
||||
var<uniform> u_material: Material;
|
||||
|
||||
@group(5) @binding(0)
|
||||
var t_specular: texture_2d<f32>;
|
||||
@group(5) @binding(1)
|
||||
var s_specular: sampler;*/
|
||||
var s_specular: sampler;
|
||||
|
||||
@group(4) @binding(0)
|
||||
@group(6) @binding(0)
|
||||
var<storage, read_write> u_light_indices: array<u32>;
|
||||
@group(4) @binding(1)
|
||||
@group(6) @binding(1)
|
||||
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // vec2<u32>
|
||||
|
||||
@fragment
|
||||
|
@ -121,7 +118,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
}
|
||||
|
||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||
let specular_color: vec3<f32> = vec3<f32>(0.0); //textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
||||
let specular_color: vec3<f32> = textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
||||
var light_res = vec3<f32>(0.0);
|
||||
|
||||
if (object_color.a < ALPHA_CUTOFF) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use image::GenericImageView;
|
||||
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
|
||||
|
@ -44,7 +44,7 @@ impl RenderTexture {
|
|||
})
|
||||
}
|
||||
|
||||
fn create_bind_group_pair(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair {
|
||||
fn create_bind_group_pair(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair {
|
||||
let bg = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &layout,
|
||||
|
@ -68,12 +68,12 @@ impl RenderTexture {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
|
||||
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
|
||||
let img = image::load_from_memory(bytes)?;
|
||||
Self::from_image(device, queue, bg_layout, &img, Some(label))
|
||||
}
|
||||
|
||||
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> {
|
||||
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> {
|
||||
let rgba = img.to_rgba8();
|
||||
let dimensions = img.dimensions();
|
||||
|
||||
|
@ -134,7 +134,7 @@ impl RenderTexture {
|
|||
})
|
||||
}
|
||||
|
||||
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> {
|
||||
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> {
|
||||
let texture_ref = texture_res.data_ref().unwrap();
|
||||
let img = texture_ref.image.data_ref().unwrap();
|
||||
|
||||
|
@ -371,7 +371,7 @@ impl RenderTexture {
|
|||
|
||||
/// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`]
|
||||
#[inline(always)]
|
||||
pub(crate) fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
||||
fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
||||
match wmode {
|
||||
WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
|
||||
WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
|
||||
|
@ -381,7 +381,7 @@ pub(crate) fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
|
|||
|
||||
/// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`]
|
||||
#[inline(always)]
|
||||
pub(crate) fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
|
||||
fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
|
||||
match fmode {
|
||||
FilterMode::Nearest => wgpu::FilterMode::Nearest,
|
||||
FilterMode::Linear => wgpu::FilterMode::Linear,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, sync::Arc};
|
||||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, rc::Rc};
|
||||
|
||||
use lyra_ecs::Entity;
|
||||
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,
|
||||
/// update, and retrieve the transforms.
|
||||
pub struct TransformBuffers {
|
||||
pub bindgroup_layout: Arc<wgpu::BindGroupLayout>,
|
||||
pub bindgroup_layout: Rc<wgpu::BindGroupLayout>,
|
||||
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
||||
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
||||
entries: Vec<BufferEntry>,
|
||||
|
@ -195,7 +195,7 @@ impl TransformBuffers {
|
|||
});
|
||||
|
||||
let mut s = Self {
|
||||
bindgroup_layout: Arc::new(bindgroup_layout),
|
||||
bindgroup_layout: Rc::new(bindgroup_layout),
|
||||
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>()),
|
||||
limits,
|
||||
|
@ -345,6 +345,9 @@ impl TransformBuffers {
|
|||
/// Returns a boolean indicating if the buffers need to be expanded
|
||||
pub fn needs_expand(&self) -> bool {
|
||||
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, Default)]
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct Specular {
|
||||
/// The strength of the specular reflection, default of 1.0
|
||||
pub factor: f32,
|
||||
|
|
Loading…
Add table
Reference in a new issue