Port to Bevy 0.11 (#22)

---------

Co-authored-by: mramirez <ramirezmike2@gmail.com>
Co-authored-by: Zain Azam <zainyusufazam@gmail.com>
This commit is contained in:
Robin KAY 2023-08-14 01:51:43 +01:00 committed by GitHub
parent 71add9ecb5
commit ae5c331450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1396 additions and 136 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_mod_outline"
version = "0.4.2"
version = "0.5.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A mesh outlining plugin for Bevy."
@ -11,27 +11,30 @@ keywords = ["gamedev", "bevy", "outline"]
categories = ["game-engines", "rendering"]
[dependencies]
bevy = { version = "0.10", default-features = false, features = [
bevy = { version = "0.11", default-features = false, features = [
"bevy_asset",
"bevy_render",
"bevy_pbr",
"ktx2",
"tonemapping_luts",
"zstd",
"bevy_core_pipeline",
] }
bitfield = "0.14"
interpolation = "0.2"
thiserror = "1.0"
wgpu-types = "0.15"
wgpu-types = "0.16.1"
[dev-dependencies]
bevy = { version = "0.10", default-features = false, features = [
bevy = { version = "0.11", default-features = false, features = [
"animation",
"bevy_gltf",
"bevy_pbr",
"bevy_scene",
"bevy_winit",
"png",
"x11",
] }
bevy_mod_gltf_patched = "0.2"
[features]
default = ["bevy_ui"]
@ -56,3 +59,7 @@ path = "examples/animated_fox.rs"
[[example]]
name = "hollow"
path = "examples/hollow.rs"
[[example]]
name = "morph_targets"
path = "examples/morph_targets.rs"

View File

@ -12,7 +12,7 @@ vertex extrusion method.
```toml
[dependencies]
bevy_mod_outline = "0.4"
bevy_mod_outline = "0.5"
```
## Examples
@ -49,14 +49,21 @@ A glTF model with pre-baked outline normals.
cargo run --example hollow
```
An animated morphing glTF model with an outline.
```shell
cargo run --example morph_targets
```
## Versions
| This Version | Bevy version |
|--------------|--------------|
| 0.1.x | 0.7.x |
| 0.2.x | 0.8.x |
| 0.3.x | 0.9.x |
| 0.5.x | 0.11.x |
| 0.4.x | 0.10.x |
| 0.3.x | 0.9.x |
| 0.2.x | 0.8.x |
| 0.1.x | 0.7.x |
## Features

1055
assets/MorphStressTest.gltf Normal file

File diff suppressed because one or more lines are too long

View File

@ -14,16 +14,17 @@ struct Fox(Handle<AnimationClip>);
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
.add_plugin(AutoGenerateOutlineNormalsPlugin)
.add_plugins((
DefaultPlugins,
OutlinePlugin,
AutoGenerateOutlineNormalsPlugin,
))
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
})
.add_startup_system(setup)
.add_system(setup_scene_once_loaded)
.add_system(close_on_esc)
.add_systems(Startup, setup)
.add_systems(Update, (setup_scene_once_loaded, close_on_esc))
.run();
}

View File

@ -1,28 +1,27 @@
use std::f32::consts::{PI, TAU};
use bevy::{prelude::*, scene::SceneInstance, window::close_on_esc};
use bevy_mod_gltf_patched::GltfPlugin;
use bevy::{gltf::GltfPlugin, prelude::*, scene::SceneInstance, window::close_on_esc};
use bevy_mod_outline::*;
fn main() {
App::new()
// Disable built-in glTF plugin
.add_plugins(DefaultPlugins.build().disable::<bevy::gltf::GltfPlugin>())
// Register outline normal vertex attribute with bevy_mod_gltf_patched
.add_plugin(
GltfPlugin::default()
.add_custom_vertex_attribute("_OUTLINE_NORMAL", ATTRIBUTE_OUTLINE_NORMAL),
// Register outline normal vertex attribute with glTF plugin
.add_plugins(
DefaultPlugins.build().set(
GltfPlugin::default()
.add_custom_vertex_attribute("_OUTLINE_NORMAL", ATTRIBUTE_OUTLINE_NORMAL),
),
)
.add_plugin(OutlinePlugin)
.add_plugins(OutlinePlugin)
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
})
.add_startup_system(setup)
.add_system(setup_scene_once_loaded)
.add_system(rotates)
.add_system(rotates_hue)
.add_system(close_on_esc)
.add_systems(Startup, setup)
.add_systems(
Update,
(setup_scene_once_loaded, rotates, rotates_hue, close_on_esc),
)
.run();
}

126
examples/morph_targets.rs Normal file
View File

@ -0,0 +1,126 @@
//! Controls morph targets in a loaded scene.
//!
//! Illustrates:
//!
//! - How to access and modify individual morph target weights.
//! See the [`update_weights`] system for details.
//! - How to read morph target names in [`name_morphs`].
//! - How to play morph target animations in [`setup_animations`].
use bevy::prelude::*;
use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
};
use std::f32::consts::PI;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "morph targets".to_string(),
..default()
}),
..default()
}),
OutlinePlugin,
AutoGenerateOutlineNormalsPlugin,
))
.insert_resource(AmbientLight {
brightness: 1.0,
..default()
})
.add_systems(Startup, setup)
.add_systems(Update, (name_morphs, setup_outlines, setup_animations))
.run();
}
#[derive(Resource)]
struct MorphData {
the_wave: Handle<AnimationClip>,
mesh: Handle<Mesh>,
}
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
commands.insert_resource(MorphData {
the_wave: asset_server.load("MorphStressTest.gltf#Animation2"),
mesh: asset_server.load("MorphStressTest.gltf#Mesh0/Primitive0"),
});
commands.spawn(SceneBundle {
scene: asset_server.load("MorphStressTest.gltf#Scene0"),
..default()
});
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
color: Color::WHITE,
illuminance: 19350.0,
..default()
},
transform: Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)),
..default()
});
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(3.0, 2.1, 10.2).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
/// Adds outlines to the meshes.
fn setup_outlines(
mut commands: Commands,
mut has_setup: Local<bool>,
meshes: Query<Entity, With<Handle<Mesh>>>,
) {
if *has_setup {
return;
}
for entity in &meshes {
commands.entity(entity).insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 3.0,
colour: Color::RED,
},
..default()
});
*has_setup = true;
}
}
/// Plays an [`AnimationClip`] from the loaded [`Gltf`] on the [`AnimationPlayer`] created by the spawned scene.
fn setup_animations(
mut has_setup: Local<bool>,
mut players: Query<(&Name, &mut AnimationPlayer)>,
morph_data: Res<MorphData>,
) {
if *has_setup {
return;
}
for (name, mut player) in &mut players {
// The name of the entity in the GLTF scene containing the AnimationPlayer for our morph targets is "Main"
if name.as_str() != "Main" {
continue;
}
player.play(morph_data.the_wave.clone()).repeat();
*has_setup = true;
}
}
/// You can get the target names in their corresponding [`Mesh`].
/// They are in the order of the weights.
fn name_morphs(
mut has_printed: Local<bool>,
morph_data: Res<MorphData>,
meshes: Res<Assets<Mesh>>,
) {
if *has_printed {
return;
}
let Some(mesh) = meshes.get(&morph_data.mesh) else { return };
let Some(names) = mesh.morph_target_names() else { return };
for name in names {
println!(" {name}");
}
*has_printed = true;
}

View File

@ -15,11 +15,9 @@ fn main() {
App::new()
.insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
.add_startup_system(setup)
.add_system(close_on_esc)
.add_system(rotates)
.add_plugins((DefaultPlugins, OutlinePlugin))
.add_systems(Startup, setup)
.add_systems(Update, (close_on_esc, rotates))
.run();
}

View File

@ -16,11 +16,9 @@ fn main() {
App::new()
.insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
.add_startup_system(setup)
.add_system(close_on_esc)
.add_system(set_camera_viewports)
.add_plugins((DefaultPlugins, OutlinePlugin))
.add_systems(Startup, setup)
.add_systems(Update, (close_on_esc, set_camera_viewports))
.run();
}

View File

@ -15,12 +15,9 @@ fn main() {
App::new()
.insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
.add_startup_system(setup)
.add_system(close_on_esc)
.add_system(wobble)
.add_system(orbit)
.add_plugins((DefaultPlugins, OutlinePlugin))
.add_systems(Startup, setup)
.add_systems(Update, (close_on_esc, wobble, orbit))
.run();
}

View File

@ -74,7 +74,8 @@ pub(crate) fn queue_outline_stencil_mesh(
let key = base_key
.with_primitive_topology(mesh.primitive_topology)
.with_depth_mode(stencil_flags.depth_mode)
.with_offset_zero(stencil_uniform.offset == 0.0);
.with_offset_zero(stencil_uniform.offset == 0.0)
.with_morph_targets(mesh.morph_targets.is_some());
let pipeline = pipelines
.specialize(&pipeline_cache, &stencil_pipeline, key, &mesh.layout)
.unwrap();
@ -161,7 +162,8 @@ pub(crate) fn queue_outline_volume_mesh(
})
.with_depth_mode(volume_flags.depth_mode)
.with_offset_zero(volume_uniform.offset == 0.0)
.with_hdr_format(view.hdr);
.with_hdr_format(view.hdr)
.with_morph_targets(mesh.morph_targets.is_some());
let pipeline = pipelines
.specialize(&pipeline_cache, &outline_pipeline, key, &mesh.layout)
.unwrap();

View File

@ -168,6 +168,6 @@ pub struct AutoGenerateOutlineNormalsPlugin;
impl Plugin for AutoGenerateOutlineNormalsPlugin {
fn build(&self, app: &mut App) {
app.add_system(auto_generate_outline_normals);
app.add_systems(Update, auto_generate_outline_normals);
}
}

View File

@ -31,7 +31,7 @@ use bevy::render::render_graph::RenderGraph;
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
use bevy::render::view::RenderLayers;
use bevy::render::{RenderApp, RenderSet};
use bevy::render::{Render, RenderApp, RenderSet};
use bevy::transform::TransformSystem;
use interpolation::Lerp;
@ -207,37 +207,55 @@ impl Plugin for OutlinePlugin {
Shader::from_wgsl
);
app.add_plugin(ExtractComponentPlugin::<OutlineStencil>::extract_visible())
.add_plugin(ExtractComponentPlugin::<OutlineRenderLayers>::default())
.add_plugin(UniformComponentPlugin::<OutlineStencilUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineVolumeUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineFragmentUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineViewUniform>::default())
.add_system(
compute_outline_depth
.in_base_set(CoreSet::PostUpdate)
.after(TransformSystem::TransformPropagate),
)
.sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>()
.init_resource::<DrawFunctions<OpaqueOutline>>()
.init_resource::<DrawFunctions<TransparentOutline>>()
.init_resource::<OutlinePipeline>()
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
.add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>()
.add_system(extract_outline_view_uniforms.in_schedule(ExtractSchedule))
.add_system(extract_outline_stencil_uniforms.in_schedule(ExtractSchedule))
.add_system(extract_outline_volume_uniforms.in_schedule(ExtractSchedule))
.add_system(sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort))
.add_system(sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort))
.add_system(sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort))
.add_system(queue_outline_view_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_stencil_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_volume_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_stencil_mesh.in_set(RenderSet::Queue))
.add_system(queue_outline_volume_mesh.in_set(RenderSet::Queue));
app.add_plugins((
ExtractComponentPlugin::<OutlineStencil>::extract_visible(),
ExtractComponentPlugin::<OutlineRenderLayers>::default(),
UniformComponentPlugin::<OutlineStencilUniform>::default(),
UniformComponentPlugin::<OutlineVolumeUniform>::default(),
UniformComponentPlugin::<OutlineFragmentUniform>::default(),
UniformComponentPlugin::<OutlineViewUniform>::default(),
))
.add_systems(
PostUpdate,
compute_outline_depth.after(TransformSystem::TransformPropagate),
)
.sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>()
.init_resource::<DrawFunctions<OpaqueOutline>>()
.init_resource::<DrawFunctions<TransparentOutline>>()
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
.add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>()
.add_systems(ExtractSchedule, extract_outline_view_uniforms)
.add_systems(ExtractSchedule, extract_outline_stencil_uniforms)
.add_systems(ExtractSchedule, extract_outline_volume_uniforms)
.add_systems(
Render,
sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort),
)
.add_systems(
Render,
sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort),
)
.add_systems(
Render,
sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort),
)
.add_systems(
Render,
queue_outline_view_bind_group.in_set(RenderSet::Queue),
)
.add_systems(
Render,
queue_outline_stencil_bind_group.in_set(RenderSet::Queue),
)
.add_systems(
Render,
queue_outline_volume_bind_group.in_set(RenderSet::Queue),
)
.add_systems(Render, queue_outline_stencil_mesh.in_set(RenderSet::Queue))
.add_systems(Render, queue_outline_volume_mesh.in_set(RenderSet::Queue));
let world = &mut app.sub_app_mut(RenderApp).world;
let node = OutlineNode::new(world);
@ -248,16 +266,10 @@ impl Plugin for OutlinePlugin {
.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME)
.unwrap();
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
draw_3d_graph.add_slot_edge(
draw_3d_graph.input_node().id,
bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
OUTLINE_PASS_NODE_NAME,
OutlineNode::IN_VIEW,
);
// Run after main 3D pass, but before UI psss
draw_3d_graph.add_node_edge(
bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
bevy::core_pipeline::core_3d::graph::node::END_MAIN_PASS,
OUTLINE_PASS_NODE_NAME,
);
#[cfg(feature = "bevy_ui")]
@ -266,4 +278,9 @@ impl Plugin for OutlinePlugin {
bevy::ui::draw_ui_graph::node::UI_PASS,
);
}
fn finish(&self, app: &mut App) {
app.sub_app_mut(RenderApp)
.init_resource::<OutlinePipeline>();
}
}

View File

@ -3,7 +3,7 @@ use std::cmp::Reverse;
use bevy::ecs::system::lifetimeless::Read;
use bevy::prelude::*;
use bevy::render::camera::ExtractedCamera;
use bevy::render::render_graph::{NodeRunError, SlotInfo, SlotType};
use bevy::render::render_graph::NodeRunError;
use bevy::render::render_phase::{
CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, RenderPhase,
};
@ -128,8 +128,6 @@ pub(crate) struct OutlineNode {
}
impl OutlineNode {
pub(crate) const IN_VIEW: &'static str = "view";
pub(crate) fn new(world: &mut World) -> Self {
Self {
query: world.query_filtered(),
@ -138,10 +136,6 @@ impl OutlineNode {
}
impl Node for OutlineNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -152,7 +146,7 @@ impl Node for OutlineNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let (camera, stencil_phase, opaque_phase, transparent_phase, camera_3d, target, depth) =
match self.query.get_manual(world, view_entity) {
Ok(query) => query,

View File

@ -1,14 +1,47 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_types
#import bevy_render::view View
#import bevy_pbr::mesh_types Mesh
#import bevy_pbr::mesh_types SkinnedMesh
struct VertexInput {
#ifdef MORPH_TARGETS
fn morph_vertex(vertex_in: Vertex) -> Vertex {
var vertex = vertex_in;
let weight_count = bevy_pbr::morph::layer_count();
for (var i: u32 = 0u; i < weight_count; i ++) {
let weight = bevy_pbr::morph::weight_at(i);
if weight == 0.0 {
continue;
}
vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i);
#ifdef VERTEX_NORMALS
vertex.normal += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::normal_offset, i);
#endif
#ifdef VERTEX_TANGENTS
vertex.tangent += vec4(weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::tangent_offset, i), 0.0);
#endif
}
return vertex;
}
#endif
struct Vertex {
#ifdef VERTEX_POSITIONS
@location(0) position: vec3<f32>,
#endif
#ifndef OFFSET_ZERO
@location(1) normal: vec3<f32>,
@location(1) outline_normal: vec3<f32>,
#endif
#ifdef VERTEX_NORMALS
@location(2) normal: vec3<f32>,
#endif
#ifdef VERTEX_TANGENTS
@location(3) tangent: vec4<f32>,
#endif
#ifdef SKINNED
@location(2) joint_indexes: vec4<u32>,
@location(3) joint_weights: vec4<f32>,
@location(5) joint_indices: vec4<u32>,
@location(6) joint_weights: vec4<f32>,
#endif
#ifdef MORPH_TARGETS
@builtin(vertex_index) index: u32,
#endif
};
@ -30,14 +63,14 @@ struct OutlineVertexUniform {
offset: f32,
};
@group(0) @binding(0)
var<uniform> view: View;
@group(1) @binding(0)
var<uniform> mesh: Mesh;
#ifdef SKINNED
@group(1) @binding(1)
var<uniform> joint_matrices: SkinnedMesh;
#import bevy_pbr::skinning
#endif
#import bevy_pbr::morph
@group(2) @binding(0)
var<uniform> view_uniform: OutlineViewUniform;
@ -60,9 +93,14 @@ fn model_origin_z(plane: vec3<f32>, view_proj: mat4x4<f32>) -> f32 {
}
@vertex
fn vertex(vertex: VertexInput) -> VertexOutput {
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef MORPH_TARGETS
var vertex = morph_vertex(vertex_no_morph);
#else
var vertex = vertex_no_morph;
#endif
#ifdef SKINNED
let model = skin_model(vertex.joint_indexes, vertex.joint_weights);
let model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else
let model = mesh.model;
#endif
@ -75,7 +113,7 @@ fn vertex(vertex: VertexInput) -> VertexOutput {
#ifdef OFFSET_ZERO
let out_xy = clip_pos.xy;
#else
let clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.normal);
let clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.outline_normal);
let ndc_delta = vstage.offset * normalize(clip_norm.xy) * view_uniform.scale * clip_pos.w;
let out_xy = clip_pos.xy + ndc_delta;
#endif

View File

@ -1,6 +1,6 @@
use std::borrow::Cow;
use bevy::pbr::{MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS};
use bevy::pbr::{setup_morph_and_skinning_defs, MeshPipelineKey};
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use bevy::render::render_resource::{
@ -57,6 +57,7 @@ impl PipelineKey {
pub offset_zero, set_offset_zero: 13;
pub hdr_format, set_hdr_format: 14;
pub opengl_workaround, set_opengl_workaround: 15;
pub morph_targets, set_morph_targets: 16;
}
pub(crate) fn new() -> Self {
@ -135,6 +136,21 @@ impl PipelineKey {
self.set_opengl_workaround(opengl_workaround);
self
}
pub(crate) fn with_morph_targets(mut self, morph_targets: bool) -> Self {
self.set_morph_targets(morph_targets);
self
}
}
impl From<PipelineKey> for MeshPipelineKey {
fn from(key: PipelineKey) -> Self {
if key.morph_targets() {
MeshPipelineKey::empty() | MeshPipelineKey::MORPH_TARGETS
} else {
MeshPipelineKey::empty()
}
}
}
#[derive(Resource)]
@ -223,38 +239,43 @@ impl SpecializedMeshPipeline for OutlinePipeline {
fn specialize(
&self,
key: Self::Key,
mesh_layout: &MeshVertexBufferLayout,
layout: &MeshVertexBufferLayout,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut targets = vec![];
let mut vertex_defs = vec!["MESH_BINDGROUP_1".into()];
let mut fragment_defs = vec![];
let mut buffer_attrs = Vec::new();
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
vertex_defs.push("VERTEX_POSITIONS".into());
buffer_attrs.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
}
if layout.contains(Mesh::ATTRIBUTE_NORMAL) {
vertex_defs.push("VERTEX_NORMALS".into());
buffer_attrs.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(2));
}
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
vertex_defs.push("VERTEX_TANGENTS".into());
buffer_attrs.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
}
let mut bind_layouts = vec![if key.msaa() == Msaa::Off {
self.mesh_pipeline.view_layout.clone()
} else {
self.mesh_pipeline.view_layout_multisampled.clone()
}];
let mut buffer_attrs = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)];
let mut vertex_defs = vec![
ShaderDefVal::Int(
"MAX_CASCADES_PER_LIGHT".to_string(),
MAX_CASCADES_PER_LIGHT as i32,
),
ShaderDefVal::Int(
"MAX_DIRECTIONAL_LIGHTS".to_string(),
MAX_DIRECTIONAL_LIGHTS as i32,
),
];
let mut fragment_defs = vec![];
bind_layouts.push(
if mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
&& mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
{
vertex_defs.push(ShaderDefVal::from("SKINNED"));
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(2));
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(3));
self.mesh_pipeline.skinned_mesh_layout.clone()
} else {
self.mesh_pipeline.mesh_layout.clone()
},
);
bind_layouts.push(setup_morph_and_skinning_defs(
&self.mesh_pipeline.mesh_layouts,
layout,
5,
&key.into(),
&mut vertex_defs,
&mut buffer_attrs,
));
bind_layouts.push(self.outline_view_bind_group_layout.clone());
let cull_mode;
if key.depth_mode() == DepthMode::Flat {
@ -269,7 +290,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
vertex_defs.push(ShaderDefVal::from("OFFSET_ZERO"));
} else {
buffer_attrs.push(
if mesh_layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
if layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
ATTRIBUTE_OUTLINE_NORMAL
} else {
Mesh::ATTRIBUTE_NORMAL
@ -305,7 +326,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
vertex_defs.push(val.clone());
fragment_defs.push(val);
}
let buffers = vec![mesh_layout.get_layout(&buffer_attrs)?];
let buffers = vec![layout.get_layout(&buffer_attrs)?];
Ok(RenderPipelineDescriptor {
vertex: VertexState {
shader: OUTLINE_SHADER_HANDLE.typed::<Shader>(),