mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
Reflect Vertex Buffer Attributes
Must follow VertexBufferDescriptorName_AttributeName format I_VertexBufferDescriptorName_AttributeName indicates that an attribute is instanced Currently all attributes must be defined in shaders or offsets will be incorrect.
This commit is contained in:
parent
62d1e710a5
commit
28fb0fdfc8
15 changed files with 597 additions and 249 deletions
|
@ -25,7 +25,9 @@ fn main() {
|
|||
ShaderStage::Vertex,
|
||||
r#"
|
||||
#version 450
|
||||
layout(location = 0) in vec4 a_Pos;
|
||||
layout(location = 0) in vec4 Vertex_Position;
|
||||
layout(location = 1) in vec4 Vertex_Normal;
|
||||
layout(location = 2) in vec2 Vertex_Uv;
|
||||
layout(location = 0) out vec4 v_Position;
|
||||
layout(set = 0, binding = 0) uniform Camera {
|
||||
mat4 ViewProj;
|
||||
|
@ -34,7 +36,7 @@ fn main() {
|
|||
mat4 Model;
|
||||
};
|
||||
void main() {
|
||||
v_Position = Model * a_Pos;
|
||||
v_Position = Model * Vertex_Position;
|
||||
gl_Position = ViewProj * v_Position;
|
||||
}
|
||||
"#,
|
||||
|
|
|
@ -1,172 +1,107 @@
|
|||
use bevy::prelude::*;
|
||||
use rand::{random, rngs::StdRng, Rng, SeedableRng};
|
||||
|
||||
struct Person;
|
||||
|
||||
struct Velocity {
|
||||
pub value: math::Vec3,
|
||||
}
|
||||
|
||||
struct NavigationPoint {
|
||||
pub target: math::Vec3,
|
||||
}
|
||||
|
||||
struct Wander {
|
||||
pub duration_bounds: math::Vec2,
|
||||
pub distance_bounds: math::Vec2,
|
||||
pub duration: f32,
|
||||
pub elapsed: f32,
|
||||
}
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
|
||||
fn main() {
|
||||
AppBuilder::new()
|
||||
.setup_world(setup)
|
||||
.add_system(build_wander_system())
|
||||
.add_system(build_navigate_system())
|
||||
.add_defaults()
|
||||
.add_system(build_move_system())
|
||||
.add_system(bevy::diagnostics::build_fps_printer_system())
|
||||
.setup_world(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(world: &mut World, resources: &mut Resources) {
|
||||
let mut mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
||||
let cube_handle = mesh_storage.add(Mesh::load(MeshType::Cube));
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![
|
||||
// lights
|
||||
(
|
||||
Light::default(),
|
||||
LocalToWorld::identity(),
|
||||
Translation::new(4.0, -4.0, 5.0),
|
||||
Rotation::from_euler_angles(0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![
|
||||
// camera
|
||||
(
|
||||
Camera::new(CameraType::Projection {
|
||||
fov: std::f32::consts::PI / 4.0,
|
||||
near: 1.0,
|
||||
far: 1000.0,
|
||||
aspect_ratio: 1.0,
|
||||
}),
|
||||
ActiveCamera,
|
||||
LocalToWorld(Mat4::look_at_rh(
|
||||
Vec3::new(6.0, -40.0, 20.0),
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(0.0, 0.0, 1.0),
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
let mut rng = StdRng::from_entropy();
|
||||
for _ in 0..70000 {
|
||||
create_person(
|
||||
world,
|
||||
cube_handle,
|
||||
Translation::new(rng.gen_range(-50.0, 50.0), 0.0, rng.gen_range(-50.0, 50.0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_wander_system() -> Box<dyn Schedulable> {
|
||||
let mut rng = StdRng::from_entropy();
|
||||
|
||||
SystemBuilder::new("Wander")
|
||||
.read_resource::<Time>()
|
||||
.with_query(<(
|
||||
Read<Person>,
|
||||
Read<Translation>,
|
||||
Write<Wander>,
|
||||
Write<NavigationPoint>,
|
||||
)>::query())
|
||||
.build(move |_, world, time, person_query| {
|
||||
for (_, translation, mut wander, mut navigation_point) in person_query.iter_mut(world) {
|
||||
wander.elapsed += time.delta_seconds;
|
||||
if wander.elapsed >= wander.duration {
|
||||
let direction = math::vec3(
|
||||
rng.gen_range(-1.0, 1.0),
|
||||
rng.gen_range(-1.0, 1.0),
|
||||
rng.gen_range(0.0, 0.001),
|
||||
)
|
||||
.normalize();
|
||||
let distance =
|
||||
rng.gen_range(wander.distance_bounds.x(), wander.distance_bounds.y());
|
||||
navigation_point.target = translation.0 + direction * distance;
|
||||
wander.elapsed = 0.0;
|
||||
wander.duration =
|
||||
rng.gen_range(wander.duration_bounds.x(), wander.duration_bounds.y());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_navigate_system() -> Box<dyn Schedulable> {
|
||||
SystemBuilder::new("Navigate")
|
||||
.with_query(<(
|
||||
Read<Person>,
|
||||
Write<Translation>,
|
||||
Write<Velocity>,
|
||||
Write<NavigationPoint>,
|
||||
)>::query())
|
||||
.build(move |_, world, _, person_query| {
|
||||
for (_, translation, mut velocity, navigation_point) in person_query.iter_mut(world) {
|
||||
let distance = navigation_point.target - translation.0;
|
||||
if distance.length() > 0.01 {
|
||||
let direction = distance.normalize();
|
||||
velocity.value = direction * 2.0;
|
||||
} else {
|
||||
velocity.value = math::vec3(0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_move_system() -> Box<dyn Schedulable> {
|
||||
SystemBuilder::new("Move")
|
||||
.read_resource::<Time>()
|
||||
.with_query(<(Write<Translation>, Read<Velocity>)>::query())
|
||||
.build(move |_, world, time, person_query| {
|
||||
for (mut translation, velocity) in person_query.iter_mut(world) {
|
||||
translation.0 += velocity.value * time.delta_seconds;
|
||||
.write_resource::<AssetStorage<StandardMaterial>>()
|
||||
.with_query(<(Write<Translation>, Read<Handle<StandardMaterial>>)>::query())
|
||||
.build(move |_, world, (time, material_storage), person_query| {
|
||||
for (mut translation, material_handle) in person_query.iter_mut(world) {
|
||||
let material = material_storage.get_mut(&material_handle).unwrap();
|
||||
translation.0 += math::vec3(1.0, 0.0, 0.0) * time.delta_seconds;
|
||||
if let ColorSource::Color(ref mut color) = material.albedo {
|
||||
*color = *color
|
||||
+ Color::rgb(-time.delta_seconds, -time.delta_seconds, time.delta_seconds);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_person(world: &mut World, mesh_handle: Handle<Mesh>, translation: Translation) {
|
||||
world.insert(
|
||||
(),
|
||||
vec![(
|
||||
Person {},
|
||||
Wander {
|
||||
duration_bounds: math::vec2(3.0, 10.0),
|
||||
distance_bounds: math::vec2(-50.0, 50.0),
|
||||
elapsed: 0.0,
|
||||
duration: 0.0,
|
||||
},
|
||||
NavigationPoint {
|
||||
target: math::vec3(0.0, 0.0, 0.0),
|
||||
},
|
||||
Velocity {
|
||||
value: math::vec3(0.0, 0.0, 0.0),
|
||||
},
|
||||
StandardMaterial {
|
||||
albedo: (math::vec4(0.5, 0.3, 0.3, 1.0) * random::<f32>()).into(),
|
||||
},
|
||||
Renderable {
|
||||
fn setup(world: &mut World, resources: &mut Resources) {
|
||||
let mut mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
||||
let mut material_storage = resources
|
||||
.get_mut::<AssetStorage<StandardMaterial>>()
|
||||
.unwrap();
|
||||
let cube_handle = mesh_storage.add(Mesh::load(MeshType::Cube));
|
||||
let plane_handle = mesh_storage.add(Mesh::load(MeshType::Plane { size: 10.0 }));
|
||||
let cube_material_handle = material_storage.add(StandardMaterial {
|
||||
albedo: Color::rgb(0.5, 0.4, 0.3).into(),
|
||||
});
|
||||
let plane_material_handle = material_storage.add(StandardMaterial {
|
||||
albedo: Color::rgb(0.1, 0.2, 0.1).into(),
|
||||
});
|
||||
|
||||
let mut builder = world
|
||||
.build()
|
||||
// plane
|
||||
.add_entity(MeshEntity {
|
||||
mesh: plane_handle,
|
||||
material: plane_material_handle,
|
||||
..Default::default()
|
||||
})
|
||||
// cube
|
||||
.add_entity(MeshEntity {
|
||||
mesh: cube_handle,
|
||||
material: cube_material_handle,
|
||||
translation: Translation::new(0.0, 0.0, 1.0),
|
||||
..Default::default()
|
||||
})
|
||||
// light
|
||||
.add_entity(LightEntity {
|
||||
translation: Translation::new(4.0, -4.0, 5.0),
|
||||
..Default::default()
|
||||
})
|
||||
// camera
|
||||
.add_entity(CameraEntity {
|
||||
camera: Camera::new(CameraType::Projection {
|
||||
fov: std::f32::consts::PI / 4.0,
|
||||
near: 1.0,
|
||||
far: 1000.0,
|
||||
aspect_ratio: 1.0,
|
||||
}),
|
||||
active_camera: ActiveCamera,
|
||||
local_to_world: LocalToWorld(Mat4::look_at_rh(
|
||||
Vec3::new(3.0, 8.0, 5.0),
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(0.0, 0.0, 1.0),
|
||||
)),
|
||||
});
|
||||
|
||||
let mut rng = StdRng::from_entropy();
|
||||
for _ in 0..500 {
|
||||
let spawned_material_handle = material_storage.add(StandardMaterial {
|
||||
albedo: Color::rgb(
|
||||
rng.gen_range(0.0, 1.0),
|
||||
rng.gen_range(0.0, 1.0),
|
||||
rng.gen_range(0.0, 1.0),
|
||||
)
|
||||
.into(),
|
||||
});
|
||||
builder = builder.add_entity(MeshEntity {
|
||||
mesh: cube_handle,
|
||||
material: spawned_material_handle,
|
||||
translation: Translation::new(
|
||||
rng.gen_range(-50.0, 50.0),
|
||||
rng.gen_range(-50.0, 50.0),
|
||||
0.0,
|
||||
),
|
||||
renderable: Renderable {
|
||||
instanced: true,
|
||||
..Default::default()
|
||||
},
|
||||
mesh_handle,
|
||||
LocalToWorld::identity(),
|
||||
translation,
|
||||
)],
|
||||
);
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
builder.build();
|
||||
}
|
||||
|
|
172
examples/instancing_old.rs
Normal file
172
examples/instancing_old.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
use bevy::prelude::*;
|
||||
use rand::{random, rngs::StdRng, Rng, SeedableRng};
|
||||
|
||||
struct Person;
|
||||
|
||||
struct Velocity {
|
||||
pub value: math::Vec3,
|
||||
}
|
||||
|
||||
struct NavigationPoint {
|
||||
pub target: math::Vec3,
|
||||
}
|
||||
|
||||
struct Wander {
|
||||
pub duration_bounds: math::Vec2,
|
||||
pub distance_bounds: math::Vec2,
|
||||
pub duration: f32,
|
||||
pub elapsed: f32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
AppBuilder::new()
|
||||
.setup_world(setup)
|
||||
.add_system(build_wander_system())
|
||||
.add_system(build_navigate_system())
|
||||
.add_system(build_move_system())
|
||||
.add_system(bevy::diagnostics::build_fps_printer_system())
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(world: &mut World, resources: &mut Resources) {
|
||||
let mut mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
||||
let cube_handle = mesh_storage.add(Mesh::load(MeshType::Cube));
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![
|
||||
// lights
|
||||
(
|
||||
Light::default(),
|
||||
LocalToWorld::identity(),
|
||||
Translation::new(4.0, -4.0, 5.0),
|
||||
Rotation::from_euler_angles(0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![
|
||||
// camera
|
||||
(
|
||||
Camera::new(CameraType::Projection {
|
||||
fov: std::f32::consts::PI / 4.0,
|
||||
near: 1.0,
|
||||
far: 1000.0,
|
||||
aspect_ratio: 1.0,
|
||||
}),
|
||||
ActiveCamera,
|
||||
LocalToWorld(Mat4::look_at_rh(
|
||||
Vec3::new(6.0, -40.0, 20.0),
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(0.0, 0.0, 1.0),
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
let mut rng = StdRng::from_entropy();
|
||||
for _ in 0..70000 {
|
||||
create_person(
|
||||
world,
|
||||
cube_handle,
|
||||
Translation::new(rng.gen_range(-50.0, 50.0), 0.0, rng.gen_range(-50.0, 50.0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_wander_system() -> Box<dyn Schedulable> {
|
||||
let mut rng = StdRng::from_entropy();
|
||||
|
||||
SystemBuilder::new("Wander")
|
||||
.read_resource::<Time>()
|
||||
.with_query(<(
|
||||
Read<Person>,
|
||||
Read<Translation>,
|
||||
Write<Wander>,
|
||||
Write<NavigationPoint>,
|
||||
)>::query())
|
||||
.build(move |_, world, time, person_query| {
|
||||
for (_, translation, mut wander, mut navigation_point) in person_query.iter_mut(world) {
|
||||
wander.elapsed += time.delta_seconds;
|
||||
if wander.elapsed >= wander.duration {
|
||||
let direction = math::vec3(
|
||||
rng.gen_range(-1.0, 1.0),
|
||||
rng.gen_range(-1.0, 1.0),
|
||||
rng.gen_range(0.0, 0.001),
|
||||
)
|
||||
.normalize();
|
||||
let distance =
|
||||
rng.gen_range(wander.distance_bounds.x(), wander.distance_bounds.y());
|
||||
navigation_point.target = translation.0 + direction * distance;
|
||||
wander.elapsed = 0.0;
|
||||
wander.duration =
|
||||
rng.gen_range(wander.duration_bounds.x(), wander.duration_bounds.y());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_navigate_system() -> Box<dyn Schedulable> {
|
||||
SystemBuilder::new("Navigate")
|
||||
.with_query(<(
|
||||
Read<Person>,
|
||||
Write<Translation>,
|
||||
Write<Velocity>,
|
||||
Write<NavigationPoint>,
|
||||
)>::query())
|
||||
.build(move |_, world, _, person_query| {
|
||||
for (_, translation, mut velocity, navigation_point) in person_query.iter_mut(world) {
|
||||
let distance = navigation_point.target - translation.0;
|
||||
if distance.length() > 0.01 {
|
||||
let direction = distance.normalize();
|
||||
velocity.value = direction * 2.0;
|
||||
} else {
|
||||
velocity.value = math::vec3(0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_move_system() -> Box<dyn Schedulable> {
|
||||
SystemBuilder::new("Move")
|
||||
.read_resource::<Time>()
|
||||
.with_query(<(Write<Translation>, Read<Velocity>)>::query())
|
||||
.build(move |_, world, time, person_query| {
|
||||
for (mut translation, velocity) in person_query.iter_mut(world) {
|
||||
translation.0 += velocity.value * time.delta_seconds;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_person(world: &mut World, mesh_handle: Handle<Mesh>, translation: Translation) {
|
||||
world.insert(
|
||||
(),
|
||||
vec![(
|
||||
Person {},
|
||||
Wander {
|
||||
duration_bounds: math::vec2(3.0, 10.0),
|
||||
distance_bounds: math::vec2(-50.0, 50.0),
|
||||
elapsed: 0.0,
|
||||
duration: 0.0,
|
||||
},
|
||||
NavigationPoint {
|
||||
target: math::vec3(0.0, 0.0, 0.0),
|
||||
},
|
||||
Velocity {
|
||||
value: math::vec3(0.0, 0.0, 0.0),
|
||||
},
|
||||
StandardMaterial {
|
||||
albedo: (math::vec4(0.5, 0.3, 0.3, 1.0) * random::<f32>()).into(),
|
||||
},
|
||||
Renderable {
|
||||
instanced: true,
|
||||
..Default::default()
|
||||
},
|
||||
mesh_handle,
|
||||
LocalToWorld::identity(),
|
||||
translation,
|
||||
)],
|
||||
);
|
||||
}
|
|
@ -12,7 +12,6 @@ use crate::{
|
|||
render_resource::resource_name,
|
||||
shader::{Shader, ShaderStages},
|
||||
texture::TextureFormat,
|
||||
Vertex,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -22,12 +21,19 @@ pub enum PipelineLayoutType {
|
|||
Reflected(Option<PipelineLayout>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DescriptorType<T> {
|
||||
Manual(T),
|
||||
Reflected(Option<T>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PipelineDescriptor {
|
||||
pub name: Option<String>,
|
||||
pub draw_targets: Vec<String>,
|
||||
pub layout: PipelineLayoutType,
|
||||
pub shader_stages: ShaderStages,
|
||||
pub reflect_vertex_buffer_descriptors: bool,
|
||||
pub rasterization_state: Option<RasterizationStateDescriptor>,
|
||||
|
||||
/// The primitive topology used to interpret vertices.
|
||||
|
@ -76,6 +82,7 @@ impl PipelineDescriptor {
|
|||
depth_bias_slope_scale: 0.0,
|
||||
depth_bias_clamp: 0.0,
|
||||
}),
|
||||
reflect_vertex_buffer_descriptors: true,
|
||||
primitive_topology: PrimitiveTopology::TriangleList,
|
||||
index_format: IndexFormat::Uint16,
|
||||
sample_count: 1,
|
||||
|
@ -169,6 +176,7 @@ impl<'a> PipelineBuilder<'a> {
|
|||
mut self,
|
||||
vertex_buffer_descriptor: VertexBufferDescriptor,
|
||||
) -> Self {
|
||||
self.pipeline.reflect_vertex_buffer_descriptors = false;
|
||||
self.pipeline
|
||||
.vertex_buffer_descriptors
|
||||
.push(vertex_buffer_descriptor);
|
||||
|
@ -229,7 +237,6 @@ impl<'a> PipelineBuilder<'a> {
|
|||
alpha_blend: BlendDescriptor::REPLACE,
|
||||
write_mask: ColorWrite::ALL,
|
||||
})
|
||||
.add_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor())
|
||||
.add_draw_target(resource_name::draw_target::ASSIGNED_MESHES)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) in vec4 a_Pos;
|
||||
layout(location = 1) in vec4 a_Normal;
|
||||
layout(location = 2) in vec2 a_Uv;
|
||||
layout(location = 0) in vec4 Vertex_Position;
|
||||
layout(location = 1) in vec4 Vertex_Normal;
|
||||
layout(location = 2) in vec2 Vertex_Uv;
|
||||
|
||||
# ifdef INSTANCING
|
||||
layout(location = 3) in vec4 I_Object_Model_0;
|
||||
layout(location = 4) in vec4 I_Object_Model_1;
|
||||
layout(location = 5) in vec4 I_Object_Model_2;
|
||||
layout(location = 6) in vec4 I_Object_Model_3;
|
||||
# endif
|
||||
|
||||
layout(location = 0) out vec4 v_Position;
|
||||
layout(location = 1) out vec3 v_Normal;
|
||||
|
@ -12,13 +19,24 @@ layout(set = 0, binding = 0) uniform Camera {
|
|||
mat4 ViewProj;
|
||||
};
|
||||
|
||||
# ifndef INSTANCING
|
||||
layout(set = 1, binding = 0) uniform Object {
|
||||
mat4 Model;
|
||||
};
|
||||
# endif
|
||||
|
||||
void main() {
|
||||
v_Normal = mat3(Model) * vec3(a_Normal.xyz);
|
||||
v_Position = Model * a_Pos;
|
||||
v_Uv = a_Uv;
|
||||
# ifdef INSTANCING
|
||||
mat4 Model = mat4(
|
||||
I_Object_Model_0,
|
||||
I_Object_Model_1,
|
||||
I_Object_Model_2,
|
||||
I_Object_Model_3
|
||||
);
|
||||
# endif
|
||||
|
||||
v_Normal = mat3(Model) * vec3(Vertex_Normal.xyz);
|
||||
v_Position = Model * Vertex_Position;
|
||||
v_Uv = Vertex_Uv;
|
||||
gl_Position = ViewProj * v_Position;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use crate::{
|
|||
render_resource::resource_name,
|
||||
shader::{Shader, ShaderStage},
|
||||
texture::TextureFormat,
|
||||
Vertex,
|
||||
},
|
||||
};
|
||||
pub trait ForwardPipelineBuilder {
|
||||
|
@ -71,7 +70,6 @@ impl ForwardPipelineBuilder for RenderGraphBuilder {
|
|||
},
|
||||
write_mask: ColorWrite::ALL,
|
||||
})
|
||||
.add_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor())
|
||||
.add_draw_target(resource_name::draw_target::ASSIGNED_MESHES)
|
||||
.finish(),
|
||||
)
|
||||
|
|
|
@ -13,7 +13,6 @@ use crate::{
|
|||
render_resource::resource_name,
|
||||
shader::{Shader, ShaderStage},
|
||||
texture::TextureFormat,
|
||||
Vertex,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -64,7 +63,6 @@ impl ForwardFlatPipelineBuilder for RenderGraphBuilder {
|
|||
alpha_blend: BlendDescriptor::REPLACE,
|
||||
write_mask: ColorWrite::ALL,
|
||||
})
|
||||
.add_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor())
|
||||
.add_draw_target(resource_name::draw_target::MESHES)
|
||||
.finish(),
|
||||
)
|
||||
|
|
|
@ -6,15 +6,12 @@ use crate::{
|
|||
BlendDescriptor, BlendFactor, BlendOperation, ColorStateDescriptor, ColorWrite,
|
||||
CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace,
|
||||
RasterizationStateDescriptor, StencilStateFaceDescriptor,
|
||||
},
|
||||
InputStepMode, PipelineDescriptor, VertexAttributeDescriptor, VertexBufferDescriptor,
|
||||
VertexFormat,
|
||||
}, PipelineDescriptor,
|
||||
},
|
||||
render_graph::RenderGraphBuilder,
|
||||
render_resource::{resource_name, resource_providers::RectData},
|
||||
render_resource::{resource_name},
|
||||
shader::{Shader, ShaderStage},
|
||||
texture::TextureFormat,
|
||||
Vertex,
|
||||
},
|
||||
};
|
||||
pub trait UiPipelineBuilder {
|
||||
|
@ -72,33 +69,6 @@ impl UiPipelineBuilder for RenderGraphBuilder {
|
|||
},
|
||||
write_mask: ColorWrite::ALL,
|
||||
})
|
||||
.add_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor())
|
||||
.add_vertex_buffer_descriptor(VertexBufferDescriptor {
|
||||
stride: std::mem::size_of::<RectData>() as u64,
|
||||
step_mode: InputStepMode::Instance,
|
||||
attributes: vec![
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float2,
|
||||
offset: 0,
|
||||
shader_location: 3,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float2,
|
||||
offset: 2 * 4,
|
||||
shader_location: 4,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float4,
|
||||
offset: 4 * 4,
|
||||
shader_location: 5,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float,
|
||||
offset: 8 * 4,
|
||||
shader_location: 6,
|
||||
},
|
||||
],
|
||||
})
|
||||
.add_draw_target(resource_name::draw_target::UI)
|
||||
.finish(),
|
||||
)
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#version 450
|
||||
|
||||
// vertex attributes
|
||||
layout(location = 0) in vec4 a_Pos;
|
||||
layout(location = 1) in vec4 a_Normal;
|
||||
layout(location = 2) in vec2 a_Uv;
|
||||
layout(location = 0) in vec4 Vertex_Position;
|
||||
layout(location = 1) in vec4 Vertex_Normal;
|
||||
layout(location = 2) in vec2 Vertex_Uv;
|
||||
|
||||
// instanced attributes (RectData)
|
||||
layout (location = 3) in vec2 a_RectPosition;
|
||||
layout (location = 4) in vec2 a_RectSize;
|
||||
layout (location = 5) in vec4 a_RectColor;
|
||||
layout (location = 6) in float a_RectZIndex;
|
||||
layout (location = 3) in vec2 I_Rect_Position;
|
||||
layout (location = 4) in vec2 I_Rect_Size;
|
||||
layout (location = 5) in vec4 I_Rect_Color;
|
||||
layout (location = 6) in float I_Rect_ZIndex;
|
||||
|
||||
layout(location = 0) out vec4 v_Color;
|
||||
|
||||
|
@ -18,8 +16,8 @@ layout(set = 0, binding = 0) uniform Camera2d {
|
|||
};
|
||||
|
||||
void main() {
|
||||
v_Color = a_RectColor;
|
||||
vec4 position = a_Pos * vec4(a_RectSize, 0.0, 1.0);
|
||||
position = position + vec4(a_RectPosition + a_RectSize / 2.0, -a_RectZIndex, 0.0);
|
||||
v_Color = I_Rect_Color;
|
||||
vec4 position = Vertex_Position * vec4(I_Rect_Size, 0.0, 1.0);
|
||||
position = position + vec4(I_Rect_Position + I_Rect_Size / 2.0, -I_Rect_ZIndex, 0.0);
|
||||
gl_Position = ViewProj * position;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VertexBufferDescriptor {
|
||||
pub name: String,
|
||||
pub stride: u64,
|
||||
pub step_mode: InputStepMode,
|
||||
pub attributes: Vec<VertexAttributeDescriptor>,
|
||||
|
@ -39,14 +40,52 @@ pub enum VertexFormat {
|
|||
Int4 = 48,
|
||||
}
|
||||
|
||||
impl VertexFormat {
|
||||
pub fn get_size(&self) -> u64 {
|
||||
match *self {
|
||||
VertexFormat::Uchar2 => 2,
|
||||
VertexFormat::Uchar4 => 4,
|
||||
VertexFormat::Char2 => 2,
|
||||
VertexFormat::Char4 => 4,
|
||||
VertexFormat::Uchar2Norm => 2,
|
||||
VertexFormat::Uchar4Norm => 4,
|
||||
VertexFormat::Char2Norm => 2,
|
||||
VertexFormat::Char4Norm => 4,
|
||||
VertexFormat::Ushort2 => 2 * 2,
|
||||
VertexFormat::Ushort4 => 2 * 4,
|
||||
VertexFormat::Short2 => 2 * 2,
|
||||
VertexFormat::Short4 => 2 * 4,
|
||||
VertexFormat::Ushort2Norm => 2 * 2,
|
||||
VertexFormat::Ushort4Norm => 2 * 4,
|
||||
VertexFormat::Short2Norm => 2 * 2,
|
||||
VertexFormat::Short4Norm => 2 * 4,
|
||||
VertexFormat::Half2 => 2 * 2,
|
||||
VertexFormat::Half4 => 2 * 4,
|
||||
VertexFormat::Float => 4,
|
||||
VertexFormat::Float2 => 4 * 2,
|
||||
VertexFormat::Float3 => 4 * 3,
|
||||
VertexFormat::Float4 => 4 * 4,
|
||||
VertexFormat::Uint => 4,
|
||||
VertexFormat::Uint2 => 4 * 2,
|
||||
VertexFormat::Uint3 => 4 * 3,
|
||||
VertexFormat::Uint4 => 4 * 4,
|
||||
VertexFormat::Int => 4,
|
||||
VertexFormat::Int2 => 4 * 2,
|
||||
VertexFormat::Int3 => 4 * 3,
|
||||
VertexFormat::Int4 => 4 * 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum InputStepMode {
|
||||
Vertex = 0,
|
||||
Instance = 1,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct VertexAttributeDescriptor {
|
||||
pub name: String,
|
||||
pub offset: u64,
|
||||
pub format: VertexFormat,
|
||||
pub shader_location: u32,
|
||||
|
|
|
@ -16,6 +16,15 @@ pub struct Renderable {
|
|||
pub instanced: bool,
|
||||
}
|
||||
|
||||
impl Renderable {
|
||||
pub fn instanced() -> Self {
|
||||
Renderable {
|
||||
instanced: false,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Renderable {
|
||||
fn default() -> Self {
|
||||
Renderable {
|
||||
|
@ -116,7 +125,12 @@ pub fn update_shader_assignments(
|
|||
// reset assignments so they are updated every frame
|
||||
shader_pipeline_assignments.assignments = HashMap::new();
|
||||
|
||||
for (entity, renderable) in <Read<Renderable>>::query().iter_entities(world) {
|
||||
for (entity, mut renderable) in <Write<Renderable>>::query().iter_entities_mut(world) {
|
||||
// if instancing is enabled, set the def here
|
||||
if renderable.instanced {
|
||||
renderable.shader_defs.insert("INSTANCING".to_string());
|
||||
}
|
||||
|
||||
for pipeline_handle in renderable.pipelines.iter() {
|
||||
if let None = compiled_shader_map
|
||||
.pipeline_to_macro_pipelines
|
||||
|
|
|
@ -7,7 +7,9 @@ use crate::{
|
|||
PassDescriptor, RenderPassColorAttachmentDescriptor,
|
||||
RenderPassDepthStencilAttachmentDescriptor,
|
||||
},
|
||||
pipeline::{BindType, PipelineDescriptor, PipelineLayout, PipelineLayoutType},
|
||||
pipeline::{
|
||||
BindType, PipelineDescriptor, PipelineLayout, PipelineLayoutType,
|
||||
},
|
||||
render_graph::RenderGraph,
|
||||
render_resource::{
|
||||
resource_name, BufferUsage, RenderResource, RenderResources, ResourceInfo,
|
||||
|
@ -161,8 +163,19 @@ impl WgpuRenderer {
|
|||
bind_group_layouts: bind_group_layouts.as_slice(),
|
||||
});
|
||||
|
||||
let owned_vertex_buffer_descriptors = pipeline_descriptor
|
||||
.vertex_buffer_descriptors
|
||||
let reflected_vertex_layout = if pipeline_descriptor.reflect_vertex_buffer_descriptors {
|
||||
Some(vertex_spirv.reflect_layout().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vertex_buffer_descriptors = if let Some(ref layout) = reflected_vertex_layout {
|
||||
&layout.vertex_buffer_descriptors
|
||||
} else {
|
||||
&pipeline_descriptor.vertex_buffer_descriptors
|
||||
};
|
||||
|
||||
let owned_vertex_buffer_descriptors = vertex_buffer_descriptors
|
||||
.iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<Vec<OwnedWgpuVertexBufferDescriptor>>();
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
use crate::render::{
|
||||
pipeline::{BindGroup, BindType, Binding, UniformProperty, UniformPropertyType},
|
||||
pipeline::{
|
||||
BindGroup, BindType, Binding, InputStepMode, UniformProperty, UniformPropertyType,
|
||||
VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
|
||||
},
|
||||
texture::TextureViewDimension,
|
||||
};
|
||||
use spirv_reflect::{
|
||||
types::{
|
||||
ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType, ReflectDimension,
|
||||
ReflectTypeDescription, ReflectTypeFlags,
|
||||
ReflectInterfaceVariable, ReflectTypeDescription, ReflectTypeFlags,
|
||||
},
|
||||
ShaderModule,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use zerocopy::AsBytes;
|
||||
// use rspirv::{binary::Parser, dr::Loader, lift::LiftContext};
|
||||
|
||||
|
@ -27,6 +31,7 @@ use zerocopy::AsBytes;
|
|||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ShaderLayout {
|
||||
pub bind_groups: Vec<BindGroup>,
|
||||
pub vertex_buffer_descriptors: Vec<VertexBufferDescriptor>,
|
||||
pub entry_point: String,
|
||||
}
|
||||
|
||||
|
@ -41,8 +46,80 @@ impl ShaderLayout {
|
|||
bind_groups.push(bind_group);
|
||||
}
|
||||
|
||||
let mut vertex_attribute_descriptors = Vec::new();
|
||||
for input_variable in module.enumerate_input_variables(None).unwrap() {
|
||||
let vertex_attribute_descriptor =
|
||||
reflect_vertex_attribute_descriptor(&input_variable);
|
||||
vertex_attribute_descriptors.push(vertex_attribute_descriptor);
|
||||
}
|
||||
|
||||
vertex_attribute_descriptors
|
||||
.sort_by(|a, b| a.shader_location.cmp(&b.shader_location));
|
||||
|
||||
let mut visited_buffer_descriptors = HashSet::new();
|
||||
let mut vertex_buffer_descriptors = Vec::new();
|
||||
let mut current_descriptor: Option<VertexBufferDescriptor> = None;
|
||||
for vertex_attribute_descriptor in vertex_attribute_descriptors.drain(..) {
|
||||
let mut instance = false;
|
||||
let current_buffer_name = {
|
||||
let parts = vertex_attribute_descriptor
|
||||
.name
|
||||
.splitn(3, "_")
|
||||
.collect::<Vec<&str>>();
|
||||
if parts.len() == 3 {
|
||||
if parts[0] == "I" {
|
||||
instance = true;
|
||||
parts[1].to_string()
|
||||
} else {
|
||||
parts[0].to_string()
|
||||
}
|
||||
} else if parts.len() == 2 {
|
||||
parts[0].to_string()
|
||||
} else {
|
||||
panic!("Vertex attributes must follow the form BUFFERNAME_PROPERTYNAME. For example: Vertex_Position");
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(current) = current_descriptor.as_mut() {
|
||||
if ¤t.name == ¤t_buffer_name {
|
||||
current.attributes.push(vertex_attribute_descriptor);
|
||||
continue;
|
||||
} else {
|
||||
if visited_buffer_descriptors.contains(¤t_buffer_name) {
|
||||
panic!("Vertex attribute buffer names must be consecutive.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(current) = current_descriptor.take() {
|
||||
visited_buffer_descriptors.insert(current.name.to_string());
|
||||
vertex_buffer_descriptors.push(current);
|
||||
}
|
||||
|
||||
current_descriptor = Some(VertexBufferDescriptor {
|
||||
attributes: vec![vertex_attribute_descriptor],
|
||||
name: current_buffer_name,
|
||||
step_mode: if instance {
|
||||
InputStepMode::Instance
|
||||
} else {
|
||||
InputStepMode::Vertex
|
||||
},
|
||||
stride: 0,
|
||||
})
|
||||
}
|
||||
|
||||
if let Some(current) = current_descriptor.take() {
|
||||
visited_buffer_descriptors.insert(current.name.to_string());
|
||||
vertex_buffer_descriptors.push(current);
|
||||
}
|
||||
|
||||
for vertex_buffer_descriptor in vertex_buffer_descriptors.iter_mut() {
|
||||
calculate_offsets(vertex_buffer_descriptor);
|
||||
}
|
||||
|
||||
ShaderLayout {
|
||||
bind_groups,
|
||||
vertex_buffer_descriptors,
|
||||
entry_point: entry_point_name,
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +128,27 @@ impl ShaderLayout {
|
|||
}
|
||||
}
|
||||
|
||||
fn calculate_offsets(vertex_buffer_descriptor: &mut VertexBufferDescriptor) {
|
||||
let mut offset = 0;
|
||||
for attribute in vertex_buffer_descriptor.attributes.iter_mut() {
|
||||
attribute.offset = offset;
|
||||
offset += attribute.format.get_size();
|
||||
}
|
||||
|
||||
vertex_buffer_descriptor.stride = offset;
|
||||
}
|
||||
|
||||
fn reflect_vertex_attribute_descriptor(
|
||||
input_variable: &ReflectInterfaceVariable,
|
||||
) -> VertexAttributeDescriptor {
|
||||
VertexAttributeDescriptor {
|
||||
name: input_variable.name.clone(),
|
||||
format: reflect_vertex_format(input_variable.type_description.as_ref().unwrap()),
|
||||
offset: 0,
|
||||
shader_location: input_variable.location,
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect_bind_group(descriptor_set: &ReflectDescriptorSet) -> BindGroup {
|
||||
let mut bindings = Vec::new();
|
||||
for descriptor_binding in descriptor_set.bindings.iter() {
|
||||
|
@ -180,6 +278,55 @@ fn reflect_uniform_numeric(type_description: &ReflectTypeDescription) -> Uniform
|
|||
}
|
||||
}
|
||||
|
||||
fn reflect_vertex_format(type_description: &ReflectTypeDescription) -> VertexFormat {
|
||||
let traits = &type_description.traits;
|
||||
let number_type = if type_description.type_flags.contains(ReflectTypeFlags::INT) {
|
||||
match traits.numeric.scalar.signedness {
|
||||
0 => NumberType::UInt,
|
||||
1 => NumberType::Int,
|
||||
signedness => panic!("unexpected signedness {}", signedness),
|
||||
}
|
||||
} else if type_description
|
||||
.type_flags
|
||||
.contains(ReflectTypeFlags::FLOAT)
|
||||
{
|
||||
NumberType::Float
|
||||
} else {
|
||||
panic!("unexpected type flag {:?}", type_description.type_flags);
|
||||
};
|
||||
|
||||
let width = traits.numeric.scalar.width;
|
||||
|
||||
match (number_type, traits.numeric.vector.component_count, width) {
|
||||
(NumberType::UInt, 2, 8) => VertexFormat::Uchar2,
|
||||
(NumberType::UInt, 4, 8) => VertexFormat::Uchar4,
|
||||
(NumberType::Int, 2, 8) => VertexFormat::Char2,
|
||||
(NumberType::Int, 4, 8) => VertexFormat::Char4,
|
||||
(NumberType::UInt, 2, 16) => VertexFormat::Ushort2,
|
||||
(NumberType::UInt, 4, 16) => VertexFormat::Ushort4,
|
||||
(NumberType::Int, 2, 16) => VertexFormat::Short2,
|
||||
(NumberType::Int, 8, 16) => VertexFormat::Short4,
|
||||
(NumberType::Float, 2, 16) => VertexFormat::Half2,
|
||||
(NumberType::Float, 4, 16) => VertexFormat::Half4,
|
||||
(NumberType::Float, 0, 32) => VertexFormat::Float,
|
||||
(NumberType::Float, 2, 32) => VertexFormat::Float2,
|
||||
(NumberType::Float, 3, 32) => VertexFormat::Float3,
|
||||
(NumberType::Float, 4, 32) => VertexFormat::Float4,
|
||||
(NumberType::UInt, 0, 32) => VertexFormat::Uint,
|
||||
(NumberType::UInt, 2, 32) => VertexFormat::Uint2,
|
||||
(NumberType::UInt, 3, 32) => VertexFormat::Uint3,
|
||||
(NumberType::UInt, 4, 32) => VertexFormat::Uint4,
|
||||
(NumberType::Int, 0, 32) => VertexFormat::Int,
|
||||
(NumberType::Int, 2, 32) => VertexFormat::Int2,
|
||||
(NumberType::Int, 3, 32) => VertexFormat::Int3,
|
||||
(NumberType::Int, 4, 32) => VertexFormat::Int4,
|
||||
(number_type, component_count, width) => panic!(
|
||||
"unexpected uniform property format {:?} {} {}",
|
||||
number_type, component_count, width
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -191,7 +338,10 @@ mod tests {
|
|||
ShaderStage::Vertex,
|
||||
r#"
|
||||
#version 450
|
||||
layout(location = 0) in vec4 a_Pos;
|
||||
layout(location = 0) in vec4 Vertex_Position;
|
||||
layout(location = 1) in uvec4 Vertex_Normal;
|
||||
layout(location = 2) in uvec4 I_TestInstancing_Property;
|
||||
|
||||
layout(location = 0) out vec4 v_Position;
|
||||
layout(set = 0, binding = 0) uniform Camera {
|
||||
mat4 ViewProj;
|
||||
|
@ -199,7 +349,7 @@ mod tests {
|
|||
layout(set = 1, binding = 0) uniform texture2D Texture;
|
||||
|
||||
void main() {
|
||||
v_Position = a_Pos;
|
||||
v_Position = Vertex_Position;
|
||||
gl_Position = ViewProj * v_Position;
|
||||
}
|
||||
"#,
|
||||
|
@ -211,6 +361,40 @@ mod tests {
|
|||
layout,
|
||||
ShaderLayout {
|
||||
entry_point: "main".to_string(),
|
||||
vertex_buffer_descriptors: vec![
|
||||
VertexBufferDescriptor {
|
||||
name: "Vertex".to_string(),
|
||||
attributes: vec![
|
||||
VertexAttributeDescriptor {
|
||||
name: "Vertex_Position".to_string(),
|
||||
format: VertexFormat::Float4,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
name: "Vertex_Normal".to_string(),
|
||||
format: VertexFormat::Uint4,
|
||||
offset: 16,
|
||||
shader_location: 1,
|
||||
}
|
||||
],
|
||||
step_mode: InputStepMode::Vertex,
|
||||
stride: 32,
|
||||
},
|
||||
VertexBufferDescriptor {
|
||||
name: "TestInstancing".to_string(),
|
||||
attributes: vec![
|
||||
VertexAttributeDescriptor {
|
||||
name: "I_TestInstancing_Property".to_string(),
|
||||
format: VertexFormat::Uint4,
|
||||
offset: 0,
|
||||
shader_location: 2,
|
||||
},
|
||||
],
|
||||
step_mode: InputStepMode::Instance,
|
||||
stride: 16,
|
||||
}
|
||||
],
|
||||
bind_groups: vec![
|
||||
BindGroup::new(
|
||||
0,
|
||||
|
@ -246,4 +430,32 @@ mod tests {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Vertex attribute buffer names must be consecutive.")]
|
||||
fn test_reflection_consecutive_buffer_validation() {
|
||||
let vertex_shader = Shader::from_glsl(
|
||||
ShaderStage::Vertex,
|
||||
r#"
|
||||
#version 450
|
||||
layout(location = 0) in vec4 Vertex_Position;
|
||||
layout(location = 1) in uvec4 Other_Property;
|
||||
layout(location = 2) in uvec4 Vertex_Normal;
|
||||
|
||||
layout(location = 0) out vec4 v_Position;
|
||||
layout(set = 0, binding = 0) uniform Camera {
|
||||
mat4 ViewProj;
|
||||
};
|
||||
layout(set = 1, binding = 0) uniform texture2D Texture;
|
||||
|
||||
void main() {
|
||||
v_Position = Vertex_Position;
|
||||
gl_Position = ViewProj * v_Position;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.get_spirv_shader(None);
|
||||
|
||||
let _layout = vertex_shader.reflect_layout().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ const LOCAL_TO_WORLD_FIELD_INFOS: &[FieldInfo] = &[FieldInfo {
|
|||
uniform_name: "Object",
|
||||
texture_name: "",
|
||||
sampler_name: "",
|
||||
is_instanceable: false,
|
||||
is_instanceable: true,
|
||||
}];
|
||||
|
||||
impl AsUniforms for bevy_transform::prelude::LocalToWorld {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use super::pipeline::{InputStepMode, VertexBufferDescriptor, VertexFormat};
|
||||
use crate::render::pipeline::VertexAttributeDescriptor;
|
||||
use std::convert::From;
|
||||
use zerocopy::{AsBytes, FromBytes};
|
||||
|
||||
|
@ -11,32 +9,6 @@ pub struct Vertex {
|
|||
pub uv: [f32; 2],
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn get_vertex_buffer_descriptor() -> VertexBufferDescriptor {
|
||||
VertexBufferDescriptor {
|
||||
stride: std::mem::size_of::<Vertex>() as u64,
|
||||
step_mode: InputStepMode::Vertex,
|
||||
attributes: vec![
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float4,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float4,
|
||||
offset: 4 * 4,
|
||||
shader_location: 1,
|
||||
},
|
||||
VertexAttributeDescriptor {
|
||||
format: VertexFormat::Float2,
|
||||
offset: 8 * 4,
|
||||
shader_location: 2,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<([f32; 4], [f32; 4], [f32; 2])> for Vertex {
|
||||
fn from((position, normal, uv): ([f32; 4], [f32; 4], [f32; 2])) -> Self {
|
||||
Vertex {
|
||||
|
|
Loading…
Add table
Reference in a new issue