more render_graph work
This commit is contained in:
parent
d9bd2d4f15
commit
7a386b8b46
@ -6,7 +6,7 @@ use winit::{
|
|||||||
|
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
|
|
||||||
use crate::{core::Time, render::*};
|
use crate::{core::Time, render::*, app::AppBuilder};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub universe: Universe,
|
pub universe: Universe,
|
||||||
@ -30,6 +30,10 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build() -> AppBuilder {
|
||||||
|
AppBuilder::new()
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
if let Some(mut time) = self.world.resources.get_mut::<Time>() {
|
if let Some(mut time) = self.world.resources.get_mut::<Time>() {
|
||||||
time.start();
|
time.start();
|
||||||
|
|||||||
@ -18,6 +18,8 @@ pub enum MeshType {
|
|||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
pub vertices: Vec<Vertex>,
|
pub vertices: Vec<Vertex>,
|
||||||
pub indices: Vec<u16>,
|
pub indices: Vec<u16>,
|
||||||
|
|
||||||
|
// TODO: remove me
|
||||||
pub vertex_buffer: Option<Buffer>,
|
pub vertex_buffer: Option<Buffer>,
|
||||||
pub index_buffer: Option<Buffer>,
|
pub index_buffer: Option<Buffer>,
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/render/render_graph_2/example.rs
Normal file
52
src/render/render_graph_2/example.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use crate::render::{render_graph_2::*, shader::{Shader, ShaderStage}, Vertex};
|
||||||
|
|
||||||
|
fn build_example_graph() -> RenderGraph {
|
||||||
|
// TODO: read this from swap chain
|
||||||
|
let swap_chain_color_format = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||||
|
RenderGraph::build()
|
||||||
|
.add_pass(
|
||||||
|
"main",
|
||||||
|
PassDescriptor {
|
||||||
|
color_attachments: Vec::new(),
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
sample_count: 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.add_pipeline(
|
||||||
|
"forward",
|
||||||
|
PipelineDescriptor::build(Shader::from_glsl(
|
||||||
|
include_str!("../passes/forward/forward.vert"),
|
||||||
|
ShaderStage::Vertex,
|
||||||
|
))
|
||||||
|
.with_fragment_shader(Shader::from_glsl(
|
||||||
|
include_str!("../passes/forward/forward.vert"),
|
||||||
|
ShaderStage::Fragment,
|
||||||
|
))
|
||||||
|
.with_rasterization_state(wgpu::RasterizationStateDescriptor {
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: wgpu::CullMode::Back,
|
||||||
|
depth_bias: 0,
|
||||||
|
depth_bias_slope_scale: 0.0,
|
||||||
|
depth_bias_clamp: 0.0,
|
||||||
|
})
|
||||||
|
.with_depth_stencil_state(wgpu::DepthStencilStateDescriptor {
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: true,
|
||||||
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
|
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||||
|
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||||
|
stencil_read_mask: 0,
|
||||||
|
stencil_write_mask: 0,
|
||||||
|
})
|
||||||
|
.with_color_state(wgpu::ColorStateDescriptor {
|
||||||
|
format: swap_chain_color_format,
|
||||||
|
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||||
|
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||||
|
write_mask: wgpu::ColorWrite::ALL,
|
||||||
|
})
|
||||||
|
.with_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor())
|
||||||
|
.with_draw_target(mesh_draw_target)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
@ -1,77 +1,19 @@
|
|||||||
mod pipeline;
|
mod pipeline;
|
||||||
|
mod pass;
|
||||||
|
mod renderer;
|
||||||
|
mod shader;
|
||||||
|
mod render_graph;
|
||||||
|
mod example;
|
||||||
|
|
||||||
|
|
||||||
pub use pipeline::*;
|
pub use pipeline::*;
|
||||||
|
pub use pass::*;
|
||||||
use crate::prelude::*;
|
pub use renderer::*;
|
||||||
use crate::{asset::Texture, legion::{prelude::{Entity, World}, borrow::{Ref, RefMap}}, render::Albedo};
|
pub use shader::*;
|
||||||
use std::collections::HashMap;
|
pub use render_graph::*;
|
||||||
|
|
||||||
pub enum ShaderValue<'a> {
|
|
||||||
Int(u32),
|
|
||||||
Float(f32),
|
|
||||||
Vec4(Vec4),
|
|
||||||
Texture(&'a Handle<Texture>),
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShaderMaterialSelector = fn(Entity, &World) -> Option<RefMap<&dyn ShaderMaterial>>;
|
|
||||||
pub struct ShaderMaterials {
|
|
||||||
// used for distinguishing
|
|
||||||
pub materials: Vec<ShaderMaterialSelector>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ShaderMaterials {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
ShaderMaterials {
|
|
||||||
materials: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&mut self, selector: ShaderMaterialSelector) {
|
|
||||||
self.materials.push(selector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ShaderMaterial {
|
|
||||||
fn iter_properties(&self) -> std::slice::Iter<&'static str> ;
|
|
||||||
fn get_property(&self, name: &str) -> Option<ShaderValue>;
|
|
||||||
fn get_selector(&self) -> ShaderMaterialSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StandardMaterial {
|
|
||||||
pub albedo: Albedo
|
|
||||||
}
|
|
||||||
|
|
||||||
// create this from a derive macro
|
|
||||||
const STANDARD_MATERIAL_PROPERTIES: &[&str] = &["albedo"];
|
|
||||||
impl ShaderMaterial for StandardMaterial {
|
|
||||||
fn iter_properties(&self) -> std::slice::Iter<&'static str> {
|
|
||||||
STANDARD_MATERIAL_PROPERTIES.iter()
|
|
||||||
}
|
|
||||||
fn get_property(&self, name: &str) -> Option<ShaderValue> {
|
|
||||||
match name {
|
|
||||||
"albedo" => Some(match self.albedo {
|
|
||||||
Albedo::Color(color) => ShaderValue::Vec4(color),
|
|
||||||
Albedo::Texture(ref texture) => ShaderValue::Texture(texture)
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get_selector(&self) -> ShaderMaterialSelector {
|
|
||||||
|entity, world| {
|
|
||||||
world.get_component::<Self>(entity).map(
|
|
||||||
|c: Ref<StandardMaterial>| {
|
|
||||||
c.map_into(|s| {
|
|
||||||
s as &dyn ShaderMaterial
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// a named graphics resource provided by a resource provider
|
// a named graphics resource provided by a resource provider
|
||||||
struct Resource {
|
pub struct Resource {
|
||||||
resource_type: ResourceType,
|
resource_type: ResourceType,
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
@ -79,7 +21,7 @@ struct Resource {
|
|||||||
// a resource type
|
// a resource type
|
||||||
enum ResourceType {
|
enum ResourceType {
|
||||||
Texture,
|
Texture,
|
||||||
Uniform,
|
Buffer,
|
||||||
Sampler,
|
Sampler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,80 +33,3 @@ enum ResourceType {
|
|||||||
trait ResourceProvider {
|
trait ResourceProvider {
|
||||||
fn get_resources(&self) -> &[Resource];
|
fn get_resources(&self) -> &[Resource];
|
||||||
}
|
}
|
||||||
|
|
||||||
// holds on to passes, pipeline descriptions, instances
|
|
||||||
// passes: shadow, forward
|
|
||||||
struct RenderGraph {
|
|
||||||
pipeline_definitions: HashMap<String, PipelineDefinition>,
|
|
||||||
pipeline_instances: HashMap<String, wgpu::RenderPipeline>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RenderGraphBuilder {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
RenderGraph::build()
|
|
||||||
.AddPass("forward", Pass {
|
|
||||||
|
|
||||||
})
|
|
||||||
.AddPipeline(Pipeline::build()
|
|
||||||
.with_vertex_shader("pbr.vert")
|
|
||||||
.with_fragment_shader("pbr.frag")
|
|
||||||
.with_vertex_layout(Vertex::get_layout()) // maybe someday reflect this using spirv-reflect
|
|
||||||
.with_uniform_binding("camera_resource", "shader_camera") // if a uniform is not bound directly, and no uniforms are set on entity, produce an error
|
|
||||||
.with_texture_binding("some_texture", "shader_texture") // if a uniform is not bound directly, and no uniforms are set on entity, produce an error
|
|
||||||
.with_draw_target(MeshDrawTarget)
|
|
||||||
.with_draw_target(InstancedMeshDrawTarget)
|
|
||||||
)
|
|
||||||
.AddPipeline(Pipeline::build()
|
|
||||||
.with_vertex_shader("ui.vert")
|
|
||||||
.with_fragment_shader("ui.frag")
|
|
||||||
.with_vertex_layout(Vertex::get_layout())
|
|
||||||
.with_draw_target(UiDrawTarget)
|
|
||||||
)
|
|
||||||
.AddPass("shadow", Pass {
|
|
||||||
render_target: Null
|
|
||||||
depth_target: DepthTexture (TextureView)
|
|
||||||
})
|
|
||||||
.AddPipeline(Pipeline::build()
|
|
||||||
.with_vertex_shader("pbr.vert")
|
|
||||||
.with_fragment_shader("pbr.frag")
|
|
||||||
.with_vertex_layout(Vertex::get_layout())
|
|
||||||
.with_draw_target(ShadowedMeshDrawTarget)
|
|
||||||
.with_draw_target(ShadowedInstancedMeshDrawTarget)
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct RenderPassColorAttachmentDescription {
|
|
||||||
/// The actual color attachment.
|
|
||||||
pub attachment: String,
|
|
||||||
|
|
||||||
/// The resolve target for this color attachment, if any.
|
|
||||||
pub resolve_target: Option<String>,
|
|
||||||
|
|
||||||
/// The beginning-of-pass load operation for this color attachment.
|
|
||||||
pub load_op: wgpu::LoadOp,
|
|
||||||
|
|
||||||
/// The end-of-pass store operation for this color attachment.
|
|
||||||
pub store_op: wgpu::StoreOp,
|
|
||||||
|
|
||||||
/// The color that will be assigned to every pixel of this attachment when cleared.
|
|
||||||
pub clear_color: wgpu::Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RenderPassDepthStencilAttachmentDescription {
|
|
||||||
pub attachment: String,
|
|
||||||
pub depth_load_op: wgpu::LoadOp,
|
|
||||||
pub depth_store_op: wgpu::StoreOp,
|
|
||||||
pub clear_depth: f32,
|
|
||||||
pub stencil_load_op: wgpu::LoadOp,
|
|
||||||
pub stencil_store_op: wgpu::StoreOp,
|
|
||||||
pub clear_stencil: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A set of pipeline bindings and draw calls with color and depth outputs
|
|
||||||
struct Pass {
|
|
||||||
color_attachments: Vec<RenderPassColorAttachmentDescription>,
|
|
||||||
depth_stencil_attachment: Option<RenderPassDepthStencilAttachmentDescription>,
|
|
||||||
}
|
|
||||||
33
src/render/render_graph_2/pass.rs
Normal file
33
src/render/render_graph_2/pass.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
pub struct RenderPassColorAttachmentDescriptor {
|
||||||
|
/// The actual color attachment.
|
||||||
|
pub attachment: String,
|
||||||
|
|
||||||
|
/// The resolve target for this color attachment, if any.
|
||||||
|
pub resolve_target: Option<String>,
|
||||||
|
|
||||||
|
/// The beginning-of-pass load operation for this color attachment.
|
||||||
|
pub load_op: wgpu::LoadOp,
|
||||||
|
|
||||||
|
/// The end-of-pass store operation for this color attachment.
|
||||||
|
pub store_op: wgpu::StoreOp,
|
||||||
|
|
||||||
|
/// The color that will be assigned to every pixel of this attachment when cleared.
|
||||||
|
pub clear_color: wgpu::Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderPassDepthStencilAttachmentDescriptor {
|
||||||
|
pub attachment: String,
|
||||||
|
pub depth_load_op: wgpu::LoadOp,
|
||||||
|
pub depth_store_op: wgpu::StoreOp,
|
||||||
|
pub clear_depth: f32,
|
||||||
|
pub stencil_load_op: wgpu::LoadOp,
|
||||||
|
pub stencil_store_op: wgpu::StoreOp,
|
||||||
|
pub clear_stencil: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A set of pipeline bindings and draw calls with color and depth outputs
|
||||||
|
pub struct PassDescriptor {
|
||||||
|
pub color_attachments: Vec<RenderPassColorAttachmentDescriptor>,
|
||||||
|
pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachmentDescriptor>,
|
||||||
|
pub sample_count: u32,
|
||||||
|
}
|
||||||
@ -1,22 +1,15 @@
|
|||||||
use crate::{
|
use crate::render::{
|
||||||
legion::prelude::World,
|
render_graph_2::DrawTarget,
|
||||||
render::shader::{Shader, ShaderStages},
|
shader::{Shader, ShaderStages},
|
||||||
};
|
};
|
||||||
|
|
||||||
// A set of draw calls. ex: get + draw meshes, get + draw instanced meshes, draw ui meshes, etc
|
pub struct VertexBufferDescriptor {
|
||||||
// Mesh target
|
|
||||||
// trait DrawTarget {
|
|
||||||
// fn draw(device: &wgpu::Device);
|
|
||||||
// }
|
|
||||||
type DrawTarget = fn(world: &World, device: &wgpu::Device);
|
|
||||||
|
|
||||||
pub struct VertexBufferDefinition {
|
|
||||||
pub stride: wgpu::BufferAddress,
|
pub stride: wgpu::BufferAddress,
|
||||||
pub step_mode: wgpu::InputStepMode,
|
pub step_mode: wgpu::InputStepMode,
|
||||||
pub attributes: Vec<wgpu::VertexAttributeDescriptor>,
|
pub attributes: Vec<wgpu::VertexAttributeDescriptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<wgpu::VertexBufferDescriptor<'a>> for &'a VertexBufferDefinition {
|
impl<'a> Into<wgpu::VertexBufferDescriptor<'a>> for &'a VertexBufferDescriptor {
|
||||||
fn into(self) -> wgpu::VertexBufferDescriptor<'a> {
|
fn into(self) -> wgpu::VertexBufferDescriptor<'a> {
|
||||||
wgpu::VertexBufferDescriptor {
|
wgpu::VertexBufferDescriptor {
|
||||||
step_mode: self.step_mode,
|
step_mode: self.step_mode,
|
||||||
@ -26,7 +19,7 @@ impl<'a> Into<wgpu::VertexBufferDescriptor<'a>> for &'a VertexBufferDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PipelineDefinition {
|
pub struct PipelineDescriptor {
|
||||||
pub draw_targets: Vec<DrawTarget>,
|
pub draw_targets: Vec<DrawTarget>,
|
||||||
pub shader_stages: ShaderStages,
|
pub shader_stages: ShaderStages,
|
||||||
pub rasterization_state: Option<wgpu::RasterizationStateDescriptor>,
|
pub rasterization_state: Option<wgpu::RasterizationStateDescriptor>,
|
||||||
@ -44,7 +37,7 @@ pub struct PipelineDefinition {
|
|||||||
pub index_format: wgpu::IndexFormat,
|
pub index_format: wgpu::IndexFormat,
|
||||||
|
|
||||||
/// The format of any vertex buffers used with this pipeline.
|
/// The format of any vertex buffers used with this pipeline.
|
||||||
pub vertex_buffer_definitions: Vec<VertexBufferDefinition>,
|
pub vertex_buffer_descriptors: Vec<VertexBufferDescriptor>,
|
||||||
|
|
||||||
/// The number of samples calculated per pixel (for MSAA).
|
/// The number of samples calculated per pixel (for MSAA).
|
||||||
pub sample_count: u32,
|
pub sample_count: u32,
|
||||||
@ -60,14 +53,14 @@ pub struct PipelineDefinition {
|
|||||||
pub alpha_to_coverage_enabled: bool,
|
pub alpha_to_coverage_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineDefinition {
|
impl PipelineDescriptor {
|
||||||
fn new(vertex_shader: Shader) -> Self {
|
fn new(vertex_shader: Shader) -> Self {
|
||||||
PipelineDefinition {
|
PipelineDescriptor {
|
||||||
color_states: Vec::new(),
|
color_states: Vec::new(),
|
||||||
depth_stencil_state: None,
|
depth_stencil_state: None,
|
||||||
draw_targets: Vec::new(),
|
draw_targets: Vec::new(),
|
||||||
shader_stages: ShaderStages::new(vertex_shader),
|
shader_stages: ShaderStages::new(vertex_shader),
|
||||||
vertex_buffer_definitions: Vec::new(),
|
vertex_buffer_descriptors: Vec::new(),
|
||||||
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
cull_mode: wgpu::CullMode::Back,
|
cull_mode: wgpu::CullMode::Back,
|
||||||
@ -84,64 +77,29 @@ impl PipelineDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineDefinition {
|
impl PipelineDescriptor {
|
||||||
pub fn create_render_pipeline(&self, device: &wgpu::Device) -> wgpu::RenderPipeline {
|
|
||||||
let vertex_shader_module = self.shader_stages.vertex.create_shader_module(device);
|
|
||||||
let fragment_shader_module = match self.shader_stages.fragment {
|
|
||||||
Some(ref fragment_shader) => Some(fragment_shader.create_shader_module(device)),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
bind_group_layouts: &[],
|
|
||||||
});
|
|
||||||
let render_pipeline_descriptor = wgpu::RenderPipelineDescriptor {
|
|
||||||
layout: &pipeline_layout,
|
|
||||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
|
||||||
module: &vertex_shader_module,
|
|
||||||
entry_point: &self.shader_stages.vertex.entry_point,
|
|
||||||
},
|
|
||||||
fragment_stage: match self.shader_stages.fragment {
|
|
||||||
Some(ref fragment_shader) => Some(wgpu::ProgrammableStageDescriptor {
|
|
||||||
entry_point: &fragment_shader.entry_point,
|
|
||||||
module: fragment_shader_module.as_ref().unwrap(),
|
|
||||||
}),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
rasterization_state: self.rasterization_state.clone(),
|
|
||||||
primitive_topology: self.primitive_topology,
|
|
||||||
color_states: &self.color_states,
|
|
||||||
depth_stencil_state: self.depth_stencil_state.clone(),
|
|
||||||
index_format: self.index_format,
|
|
||||||
vertex_buffers: &self
|
|
||||||
.vertex_buffer_definitions
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.into())
|
|
||||||
.collect::<Vec<wgpu::VertexBufferDescriptor>>(),
|
|
||||||
sample_count: self.sample_count,
|
|
||||||
sample_mask: self.sample_mask,
|
|
||||||
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
device.create_render_pipeline(&render_pipeline_descriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(vertex_shader: Shader) -> PipelineBuilder {
|
pub fn build(vertex_shader: Shader) -> PipelineBuilder {
|
||||||
PipelineBuilder::new(vertex_shader)
|
PipelineBuilder::new(vertex_shader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PipelineBuilder {
|
pub struct PipelineBuilder {
|
||||||
pipeline: PipelineDefinition,
|
pipeline: PipelineDescriptor,
|
||||||
|
vertex_buffer_descriptor_offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineBuilder {
|
impl PipelineBuilder {
|
||||||
pub fn new(vertex_shader: Shader) -> Self {
|
pub fn new(vertex_shader: Shader) -> Self {
|
||||||
PipelineBuilder {
|
PipelineBuilder {
|
||||||
pipeline: PipelineDefinition::new(vertex_shader),
|
pipeline: PipelineDescriptor::new(vertex_shader),
|
||||||
|
vertex_buffer_descriptor_offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> PipelineDescriptor {
|
||||||
|
self.pipeline
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_fragment_shader(mut self, fragment_shader: Shader) -> Self {
|
pub fn with_fragment_shader(mut self, fragment_shader: Shader) -> Self {
|
||||||
self.pipeline.shader_stages.fragment = Some(fragment_shader);
|
self.pipeline.shader_stages.fragment = Some(fragment_shader);
|
||||||
self
|
self
|
||||||
@ -152,7 +110,10 @@ impl PipelineBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_depth_stencil_state(mut self, depth_stencil_state: wgpu::DepthStencilStateDescriptor) -> Self {
|
pub fn with_depth_stencil_state(
|
||||||
|
mut self,
|
||||||
|
depth_stencil_state: wgpu::DepthStencilStateDescriptor,
|
||||||
|
) -> Self {
|
||||||
if let Some(_) = self.pipeline.depth_stencil_state {
|
if let Some(_) = self.pipeline.depth_stencil_state {
|
||||||
panic!("Depth stencil state has already been set");
|
panic!("Depth stencil state has already been set");
|
||||||
}
|
}
|
||||||
@ -160,8 +121,21 @@ impl PipelineBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_vertex_buffer_definition(mut self, vertex_buffer_definition: VertexBufferDefinition) -> Self {
|
pub fn with_vertex_buffer_descriptor(
|
||||||
self.pipeline.vertex_buffer_definitions.push(vertex_buffer_definition);
|
mut self,
|
||||||
|
mut vertex_buffer_descriptor: VertexBufferDescriptor,
|
||||||
|
) -> Self {
|
||||||
|
let mut offset = 0;
|
||||||
|
for attribute in vertex_buffer_descriptor.attributes.iter_mut() {
|
||||||
|
offset += attribute.offset;
|
||||||
|
attribute.offset += self.vertex_buffer_descriptor_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vertex_buffer_descriptor_offset += offset;
|
||||||
|
|
||||||
|
self.pipeline
|
||||||
|
.vertex_buffer_descriptors
|
||||||
|
.push(vertex_buffer_descriptor);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +149,10 @@ impl PipelineBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_rasterization_state(mut self, rasterization_state: wgpu::RasterizationStateDescriptor) -> Self {
|
pub fn with_rasterization_state(
|
||||||
|
mut self,
|
||||||
|
rasterization_state: wgpu::RasterizationStateDescriptor,
|
||||||
|
) -> Self {
|
||||||
self.pipeline.rasterization_state = Some(rasterization_state);
|
self.pipeline.rasterization_state = Some(rasterization_state);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
98
src/render/render_graph_2/render_graph.rs
Normal file
98
src/render/render_graph_2/render_graph.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use crate::render::render_graph_2::{PassDescriptor, PipelineDescriptor};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
// holds on to passes, pipeline descriptions, instances
|
||||||
|
// passes: shadow, forward
|
||||||
|
pub struct RenderGraph {
|
||||||
|
pub pipeline_descriptors: HashMap<String, PipelineDescriptor>,
|
||||||
|
pub pass_descriptors: HashMap<String, PassDescriptor>,
|
||||||
|
pub pass_pipelines: HashMap<String, Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RenderGraph {
|
||||||
|
fn default() -> Self {
|
||||||
|
RenderGraph {
|
||||||
|
pipeline_descriptors: HashMap::new(),
|
||||||
|
pass_descriptors: HashMap::new(),
|
||||||
|
pass_pipelines: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderGraph {
|
||||||
|
pub fn build() -> RenderGraphBuilder {
|
||||||
|
RenderGraphBuilder {
|
||||||
|
render_graph: RenderGraph::default(),
|
||||||
|
current_pass: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderGraphBuilder {
|
||||||
|
render_graph: RenderGraph,
|
||||||
|
current_pass: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderGraphBuilder {
|
||||||
|
pub fn add_pass(mut self, name: &str, pass: PassDescriptor) -> Self {
|
||||||
|
self.current_pass = Some(name.to_string());
|
||||||
|
self.render_graph
|
||||||
|
.pass_descriptors
|
||||||
|
.insert(name.to_string(), pass);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_pipeline(mut self, name: &str, pipeline: PipelineDescriptor) -> Self {
|
||||||
|
self.render_graph
|
||||||
|
.pipeline_descriptors
|
||||||
|
.insert(name.to_string(), pipeline);
|
||||||
|
|
||||||
|
if let Some(current_pass) = self.current_pass.as_ref() {
|
||||||
|
if let None = self.render_graph.pass_pipelines.get(current_pass) {
|
||||||
|
self.render_graph.pass_pipelines.insert(current_pass.to_string(), Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let pass_pipelines = self.render_graph.pass_pipelines.get_mut(current_pass).unwrap();
|
||||||
|
pass_pipelines.push(name.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> RenderGraph {
|
||||||
|
self.render_graph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RenderGraph::build()
|
||||||
|
.AddPass("forward", Pass {
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddPipeline(Pipeline::build()
|
||||||
|
.with_vertex_shader("pbr.vert")
|
||||||
|
.with_fragment_shader("pbr.frag")
|
||||||
|
.add_vertex_layout(Vertex::get_layout()) // maybe someday reflect this using spirv-reflect
|
||||||
|
.add_uniform_binding("camera_resource", "shader_camera") // if a uniform is not bound directly, and no uniforms are set on entity, produce an error
|
||||||
|
.add_texture_binding("some_texture", "shader_texture") // if a uniform is not bound directly, and no uniforms are set on entity, produce an error
|
||||||
|
.add_draw_target(MeshDrawTarget)
|
||||||
|
.add_draw_target(InstancedMeshDrawTarget)
|
||||||
|
)
|
||||||
|
.AddPipeline(Pipeline::build()
|
||||||
|
.with_vertex_shader("ui.vert")
|
||||||
|
.with_fragment_shader("ui.frag")
|
||||||
|
.with_vertex_layout(Vertex::get_layout())
|
||||||
|
.with_draw_target(UiDrawTarget)
|
||||||
|
)
|
||||||
|
.AddPass("shadow", Pass {
|
||||||
|
render_target: Null
|
||||||
|
depth_target: DepthTexture (TextureView)
|
||||||
|
})
|
||||||
|
.AddPipeline(Pipeline::build()
|
||||||
|
.with_vertex_shader("pbr.vert")
|
||||||
|
.with_fragment_shader("pbr.frag")
|
||||||
|
.with_vertex_layout(Vertex::get_layout())
|
||||||
|
.with_draw_target(ShadowedMeshDrawTarget)
|
||||||
|
.with_draw_target(ShadowedInstancedMeshDrawTarget)
|
||||||
|
)
|
||||||
|
*/
|
||||||
212
src/render/render_graph_2/renderer.rs
Normal file
212
src/render/render_graph_2/renderer.rs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
use crate::{
|
||||||
|
asset::{AssetStorage, Handle, Mesh},
|
||||||
|
legion::prelude::*,
|
||||||
|
render::{
|
||||||
|
render_graph_2::{PipelineDescriptor, PassDescriptor, RenderGraph, ShaderMaterials},
|
||||||
|
Instanced,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
// A set of draw calls. ex: get + draw meshes, get + draw instanced meshes, draw ui meshes, etc
|
||||||
|
// Mesh target
|
||||||
|
// trait DrawTarget {
|
||||||
|
// fn draw(device: &wgpu::Device);
|
||||||
|
// }
|
||||||
|
pub type DrawTarget =
|
||||||
|
fn(world: &World, render_pass: &mut dyn RenderPass);
|
||||||
|
|
||||||
|
pub fn mesh_draw_target(
|
||||||
|
world: &World,
|
||||||
|
render_pass: &mut dyn RenderPass,
|
||||||
|
) {
|
||||||
|
let mut mesh_storage = world.resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
||||||
|
let mut last_mesh_id = None;
|
||||||
|
let mesh_query =
|
||||||
|
<(Read<ShaderMaterials>, Read<Handle<Mesh>>)>::query().filter(!component::<Instanced>());
|
||||||
|
for (material, mesh) in mesh_query.iter(world) {
|
||||||
|
let current_mesh_id = mesh.id;
|
||||||
|
|
||||||
|
let mut should_load_mesh = last_mesh_id == None;
|
||||||
|
if let Some(last) = last_mesh_id {
|
||||||
|
should_load_mesh = last != current_mesh_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_load_mesh {
|
||||||
|
if let Some(mesh_asset) = mesh_storage.get(mesh.id) {
|
||||||
|
// render_pass.load_mesh(mesh.id, mesh_asset);
|
||||||
|
// render_pass.set_index_buffer(mesh_asset.index_buffer.as_ref().unwrap(), 0);
|
||||||
|
// render_pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref mesh_asset) = mesh_storage.get(mesh.id) {
|
||||||
|
// pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]);
|
||||||
|
// pass.draw_indexed(0..mesh_asset.indices.len() as u32, 0, 0..1);
|
||||||
|
};
|
||||||
|
|
||||||
|
last_mesh_id = Some(current_mesh_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Renderer {
|
||||||
|
fn resize(&mut self, world: &mut World, width: u32, height: u32);
|
||||||
|
fn process_render_graph(&mut self, render_graph: &RenderGraph, world: &mut World);
|
||||||
|
fn load_mesh(&mut self, asset_id: usize, mesh: &Mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WgpuRenderer {
|
||||||
|
pub device: wgpu::Device,
|
||||||
|
pub surface: wgpu::Surface,
|
||||||
|
pub swap_chain_descriptor: wgpu::SwapChainDescriptor,
|
||||||
|
pub render_pipelines: HashMap<String, wgpu::RenderPipeline>,
|
||||||
|
pub buffers: HashMap<String, wgpu::Buffer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WgpuRenderer {
|
||||||
|
pub fn create_render_pipeline(
|
||||||
|
pipeline_descriptor: &PipelineDescriptor,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
|
let vertex_shader_module = pipeline_descriptor
|
||||||
|
.shader_stages
|
||||||
|
.vertex
|
||||||
|
.create_shader_module(device);
|
||||||
|
let fragment_shader_module = match pipeline_descriptor.shader_stages.fragment {
|
||||||
|
Some(ref fragment_shader) => Some(fragment_shader.create_shader_module(device)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
bind_group_layouts: &[],
|
||||||
|
});
|
||||||
|
let render_pipeline_descriptor = wgpu::RenderPipelineDescriptor {
|
||||||
|
layout: &pipeline_layout,
|
||||||
|
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||||
|
module: &vertex_shader_module,
|
||||||
|
entry_point: &pipeline_descriptor.shader_stages.vertex.entry_point,
|
||||||
|
},
|
||||||
|
fragment_stage: match pipeline_descriptor.shader_stages.fragment {
|
||||||
|
Some(ref fragment_shader) => Some(wgpu::ProgrammableStageDescriptor {
|
||||||
|
entry_point: &fragment_shader.entry_point,
|
||||||
|
module: fragment_shader_module.as_ref().unwrap(),
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
rasterization_state: pipeline_descriptor.rasterization_state.clone(),
|
||||||
|
primitive_topology: pipeline_descriptor.primitive_topology,
|
||||||
|
color_states: &pipeline_descriptor.color_states,
|
||||||
|
depth_stencil_state: pipeline_descriptor.depth_stencil_state.clone(),
|
||||||
|
index_format: pipeline_descriptor.index_format,
|
||||||
|
vertex_buffers: &pipeline_descriptor
|
||||||
|
.vertex_buffer_descriptors
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.into())
|
||||||
|
.collect::<Vec<wgpu::VertexBufferDescriptor>>(),
|
||||||
|
sample_count: pipeline_descriptor.sample_count,
|
||||||
|
sample_mask: pipeline_descriptor.sample_mask,
|
||||||
|
alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
device.create_render_pipeline(&render_pipeline_descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_render_pass<'a>(
|
||||||
|
pass_descriptor: &PassDescriptor,
|
||||||
|
encoder: &'a mut wgpu::CommandEncoder,
|
||||||
|
frame: &'a wgpu::SwapChainOutput,
|
||||||
|
) -> wgpu::RenderPass<'a> {
|
||||||
|
// TODO: fill this in
|
||||||
|
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
color_attachments: &[],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer for WgpuRenderer {
|
||||||
|
fn resize(&mut self, world: &mut World, width: u32, height: u32) {
|
||||||
|
let swap_chain = self
|
||||||
|
.device
|
||||||
|
.create_swap_chain(&self.surface, &self.swap_chain_descriptor);
|
||||||
|
self.swap_chain_descriptor.width = width;
|
||||||
|
self.swap_chain_descriptor.height = height;
|
||||||
|
|
||||||
|
// WgpuRenderer can't own swap_chain without creating lifetime ergonomics issues
|
||||||
|
world.resources.insert(swap_chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_render_graph(&mut self, render_graph: &RenderGraph, world: &mut World) {
|
||||||
|
let mut swap_chain = world.resources.get_mut::<wgpu::SwapChain>().unwrap();
|
||||||
|
let frame = swap_chain
|
||||||
|
.get_next_texture()
|
||||||
|
.expect("Timeout when acquiring next swap chain texture");
|
||||||
|
|
||||||
|
let mut encoder = self
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
|
||||||
|
|
||||||
|
for (pass_name, pass_descriptor) in render_graph.pass_descriptors.iter() {
|
||||||
|
let mut render_pass =
|
||||||
|
WgpuRenderer::create_render_pass(pass_descriptor, &mut encoder, &frame);
|
||||||
|
if let Some(pass_pipelines) = render_graph.pass_pipelines.get(pass_name) {
|
||||||
|
for pass_pipeline in pass_pipelines.iter() {
|
||||||
|
if let Some(pipeline_descriptor) =
|
||||||
|
render_graph.pipeline_descriptors.get(pass_pipeline)
|
||||||
|
{
|
||||||
|
if let None = self.render_pipelines.get(pass_pipeline) {
|
||||||
|
let render_pipeline = WgpuRenderer::create_render_pipeline(
|
||||||
|
pipeline_descriptor,
|
||||||
|
&self.device,
|
||||||
|
);
|
||||||
|
self.render_pipelines
|
||||||
|
.insert(pass_pipeline.to_string(), render_pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut render_pass = WgpuRenderPass {
|
||||||
|
render_pass: &mut render_pass,
|
||||||
|
renderer: &self,
|
||||||
|
};
|
||||||
|
for draw_target in pipeline_descriptor.draw_targets.iter() {
|
||||||
|
draw_target(world, &mut render_pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_mesh(&mut self, asset_id: usize, mesh: &Mesh) {
|
||||||
|
if let None = mesh.vertex_buffer {
|
||||||
|
self.buffers.insert(
|
||||||
|
format!("meshv{}", asset_id),
|
||||||
|
self.device
|
||||||
|
.create_buffer_with_data(mesh.vertices.as_bytes(), wgpu::BufferUsage::VERTEX),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let None = mesh.index_buffer {
|
||||||
|
self.buffers.insert(
|
||||||
|
format!("meshi{}", asset_id),
|
||||||
|
self.device
|
||||||
|
.create_buffer_with_data(mesh.indices.as_bytes(), wgpu::BufferUsage::INDEX),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RenderPass {
|
||||||
|
fn set_index_buffer(&mut self, buffer: &wgpu::Buffer, offset: wgpu::BufferAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WgpuRenderPass<'a, 'b, 'c> {
|
||||||
|
pub render_pass: &'b mut wgpu::RenderPass<'a>,
|
||||||
|
pub renderer: &'c WgpuRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'c> RenderPass for WgpuRenderPass<'a, 'b, 'c> {
|
||||||
|
fn set_index_buffer(&mut self, buffer: &wgpu::Buffer, offset: wgpu::BufferAddress) {
|
||||||
|
self.render_pass.set_index_buffer(buffer, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/render/render_graph_2/shader.rs
Normal file
66
src/render/render_graph_2/shader.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use crate::{asset::Texture, legion::{prelude::{Entity, World}, borrow::{Ref, RefMap}}, render::Albedo};
|
||||||
|
|
||||||
|
|
||||||
|
pub enum ShaderValue<'a> {
|
||||||
|
Int(u32),
|
||||||
|
Float(f32),
|
||||||
|
Vec4(Vec4),
|
||||||
|
Texture(&'a Handle<Texture>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ShaderMaterialSelector = fn(Entity, &World) -> Option<RefMap<&dyn ShaderMaterial>>;
|
||||||
|
pub struct ShaderMaterials {
|
||||||
|
// used for distinguishing
|
||||||
|
pub materials: Vec<ShaderMaterialSelector>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ShaderMaterials {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ShaderMaterials {
|
||||||
|
materials: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, selector: ShaderMaterialSelector) {
|
||||||
|
self.materials.push(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ShaderMaterial {
|
||||||
|
fn iter_properties(&self) -> std::slice::Iter<&'static str> ;
|
||||||
|
fn get_property(&self, name: &str) -> Option<ShaderValue>;
|
||||||
|
fn get_selector(&self) -> ShaderMaterialSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StandardMaterial {
|
||||||
|
pub albedo: Albedo
|
||||||
|
}
|
||||||
|
|
||||||
|
// create this from a derive macro
|
||||||
|
const STANDARD_MATERIAL_PROPERTIES: &[&str] = &["albedo"];
|
||||||
|
impl ShaderMaterial for StandardMaterial {
|
||||||
|
fn iter_properties(&self) -> std::slice::Iter<&'static str> {
|
||||||
|
STANDARD_MATERIAL_PROPERTIES.iter()
|
||||||
|
}
|
||||||
|
fn get_property(&self, name: &str) -> Option<ShaderValue> {
|
||||||
|
match name {
|
||||||
|
"albedo" => Some(match self.albedo {
|
||||||
|
Albedo::Color(color) => ShaderValue::Vec4(color),
|
||||||
|
Albedo::Texture(ref texture) => ShaderValue::Texture(texture)
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_selector(&self) -> ShaderMaterialSelector {
|
||||||
|
|entity, world| {
|
||||||
|
world.get_component::<Self>(entity).map(
|
||||||
|
|c: Ref<StandardMaterial>| {
|
||||||
|
c.map_into(|s| {
|
||||||
|
s as &dyn ShaderMaterial
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use zerocopy::{AsBytes, FromBytes};
|
use zerocopy::{AsBytes, FromBytes};
|
||||||
|
use crate::render::render_graph_2::VertexBufferDescriptor;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, AsBytes, FromBytes)]
|
#[derive(Clone, Copy, AsBytes, FromBytes)]
|
||||||
@ -9,6 +10,33 @@ pub struct Vertex {
|
|||||||
pub uv: [f32; 2],
|
pub uv: [f32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Vertex {
|
||||||
|
// TODO: generate from macro
|
||||||
|
pub fn get_vertex_buffer_descriptor() -> VertexBufferDescriptor {
|
||||||
|
VertexBufferDescriptor {
|
||||||
|
stride: std::mem::size_of::<Vertex>() as u64,
|
||||||
|
step_mode: wgpu::InputStepMode::Vertex,
|
||||||
|
attributes: vec![
|
||||||
|
wgpu::VertexAttributeDescriptor {
|
||||||
|
format: wgpu::VertexFormat::Float4,
|
||||||
|
offset: 0,
|
||||||
|
shader_location: 0,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttributeDescriptor {
|
||||||
|
format: wgpu::VertexFormat::Float4,
|
||||||
|
offset: 4 * 4,
|
||||||
|
shader_location: 1,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttributeDescriptor {
|
||||||
|
format: wgpu::VertexFormat::Float2,
|
||||||
|
offset: 8 * 4,
|
||||||
|
shader_location: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<([f32; 4], [f32; 4], [f32; 2])> for Vertex {
|
impl From<([f32; 4], [f32; 4], [f32; 2])> for Vertex {
|
||||||
fn from((position, normal, uv): ([f32; 4], [f32; 4], [f32; 2])) -> Self {
|
fn from((position, normal, uv): ([f32; 4], [f32; 4], [f32; 2])) -> Self {
|
||||||
Vertex {
|
Vertex {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user