Path: blob/main/crates/bevy_gizmos_render/src/pipeline_3d.rs
9402 views
use crate::{1init_line_gizmo_uniform_bind_group_layout, line_gizmo_vertex_buffer_layouts,2line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, DrawLineJointGizmo, GizmoRenderSystems,3GpuLineGizmo, LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup,4};5use bevy_app::{App, Plugin};6use bevy_asset::{load_embedded_asset, AssetServer, Handle};7use bevy_camera::visibility::RenderLayers;8use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};9use bevy_gizmos::config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig};1011use bevy_ecs::{12error::BevyError,13prelude::Entity,14resource::Resource,15schedule::IntoScheduleConfigs,16system::{Commands, Query, Res, ResMut},17};18use bevy_image::BevyDefault as _;19use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup, ViewKeyCache};20use bevy_render::{21render_asset::{prepare_assets, RenderAssets},22render_phase::{23AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,24ViewSortedRenderPhases,25},26render_resource::*,27view::{ExtractedView, ViewTarget},28Render, RenderApp, RenderSystems,29};30use bevy_render::{sync_world::MainEntity, RenderStartup};31use bevy_shader::Shader;32use bevy_utils::default;33use tracing::error;3435pub struct LineGizmo3dPlugin;36impl Plugin for LineGizmo3dPlugin {37fn build(&self, app: &mut App) {38let Some(render_app) = app.get_sub_app_mut(RenderApp) else {39return;40};4142render_app43.add_render_command::<Transparent3d, DrawLineGizmo3d>()44.add_render_command::<Transparent3d, DrawLineGizmo3dStrip>()45.add_render_command::<Transparent3d, DrawLineJointGizmo3d>()46.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()47.configure_sets(48Render,49GizmoRenderSystems::QueueLineGizmos3d.in_set(RenderSystems::Queue),50)51.add_systems(52RenderStartup,53init_line_gizmo_pipelines.after(init_line_gizmo_uniform_bind_group_layout),54)55.add_systems(56Render,57(queue_line_gizmos_3d, queue_line_joint_gizmos_3d)58.in_set(GizmoRenderSystems::QueueLineGizmos3d)59.after(prepare_assets::<GpuLineGizmo>),60);61}62}6364#[derive(Resource)]65struct LineGizmoPipeline {66variants: Variants<RenderPipeline, LineGizmoPipelineSpecializer>,67}6869fn init_line_gizmo_pipelines(70mut commands: Commands,71mesh_pipeline: Res<MeshPipeline>,72uniform_bind_group_layout: Res<LineGizmoUniformBindgroupLayout>,73asset_server: Res<AssetServer>,74) {75let line_shader = load_embedded_asset!(asset_server.as_ref(), "lines.wgsl");76let variants_line = Variants::new(77LineGizmoPipelineSpecializer {78mesh_pipeline: mesh_pipeline.clone(),79},80RenderPipelineDescriptor {81label: Some("LineGizmo 3d Pipeline".into()),82vertex: VertexState {83shader: line_shader.clone(),84..default()85},86fragment: Some(FragmentState {87shader: line_shader,88..default()89}),90layout: vec![91Default::default(), // placeholder92uniform_bind_group_layout.layout.clone(),93],94depth_stencil: Some(DepthStencilState {95format: CORE_3D_DEPTH_FORMAT,96depth_write_enabled: true,97depth_compare: CompareFunction::Greater,98stencil: StencilState::default(),99bias: DepthBiasState::default(),100}),101..default()102},103);104105commands.insert_resource(LineGizmoPipeline {106variants: variants_line,107});108commands.insert_resource(LineJointGizmoPipeline {109mesh_pipeline: mesh_pipeline.clone(),110uniform_layout: uniform_bind_group_layout.layout.clone(),111shader: load_embedded_asset!(asset_server.as_ref(), "line_joints.wgsl"),112});113}114115struct LineGizmoPipelineSpecializer {116mesh_pipeline: MeshPipeline,117}118119#[derive(PartialEq, Eq, Hash, Clone, SpecializerKey)]120struct LineGizmoPipelineKey {121view_key: MeshPipelineKey,122strip: bool,123perspective: bool,124line_style: GizmoLineStyle,125}126127impl Specializer<RenderPipeline> for LineGizmoPipelineSpecializer {128type Key = LineGizmoPipelineKey;129130fn specialize(131&self,132key: Self::Key,133descriptor: &mut RenderPipelineDescriptor,134) -> Result<Canonical<Self::Key>, BevyError> {135let view_layout = self136.mesh_pipeline137.get_view_layout(key.view_key.into())138.clone();139140descriptor.set_layout(0, view_layout.main_layout.clone());141descriptor.vertex.buffers = line_gizmo_vertex_buffer_layouts(key.strip);142descriptor.multisample.count = key.view_key.msaa_samples();143144let fragment = descriptor.fragment_mut()?;145146#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]147fragment.shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());148149if key.perspective {150fragment.shader_defs.push("PERSPECTIVE".into());151}152153let format = if key.view_key.contains(MeshPipelineKey::HDR) {154ViewTarget::TEXTURE_FORMAT_HDR155} else {156TextureFormat::bevy_default()157};158159let fragment_entry_point = match key.line_style {160GizmoLineStyle::Solid => "fragment_solid",161GizmoLineStyle::Dotted => "fragment_dotted",162GizmoLineStyle::Dashed { .. } => "fragment_dashed",163_ => unimplemented!(),164};165166fragment.entry_point = Some(fragment_entry_point.into());167168fragment.set_target(1690,170ColorTargetState {171format,172blend: Some(BlendState::ALPHA_BLENDING),173write_mask: ColorWrites::ALL,174},175);176177Ok(key)178}179}180181#[derive(Clone, Resource)]182struct LineJointGizmoPipeline {183mesh_pipeline: MeshPipeline,184uniform_layout: BindGroupLayoutDescriptor,185shader: Handle<Shader>,186}187188#[derive(PartialEq, Eq, Hash, Clone)]189struct LineJointGizmoPipelineKey {190view_key: MeshPipelineKey,191perspective: bool,192joints: GizmoLineJoint,193}194195impl SpecializedRenderPipeline for LineJointGizmoPipeline {196type Key = LineJointGizmoPipelineKey;197198fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {199let mut shader_defs = vec![200#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]201"SIXTEEN_BYTE_ALIGNMENT".into(),202];203204if key.perspective {205shader_defs.push("PERSPECTIVE".into());206}207208let format = if key.view_key.contains(MeshPipelineKey::HDR) {209ViewTarget::TEXTURE_FORMAT_HDR210} else {211TextureFormat::bevy_default()212};213214let view_layout = self215.mesh_pipeline216.get_view_layout(key.view_key.into())217.clone();218let layout = vec![view_layout.main_layout.clone(), self.uniform_layout.clone()];219220if key.joints == GizmoLineJoint::None {221error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");222};223224let entry_point = match key.joints {225GizmoLineJoint::Miter => "vertex_miter",226GizmoLineJoint::Round(_) => "vertex_round",227GizmoLineJoint::None | GizmoLineJoint::Bevel => "vertex_bevel",228};229230RenderPipelineDescriptor {231vertex: VertexState {232shader: self.shader.clone(),233entry_point: Some(entry_point.into()),234shader_defs: shader_defs.clone(),235buffers: line_joint_gizmo_vertex_buffer_layouts(),236},237fragment: Some(FragmentState {238shader: self.shader.clone(),239shader_defs,240targets: vec![Some(ColorTargetState {241format,242blend: Some(BlendState::ALPHA_BLENDING),243write_mask: ColorWrites::ALL,244})],245..default()246}),247layout,248depth_stencil: Some(DepthStencilState {249format: CORE_3D_DEPTH_FORMAT,250depth_write_enabled: true,251depth_compare: CompareFunction::Greater,252stencil: StencilState::default(),253bias: DepthBiasState::default(),254}),255multisample: MultisampleState {256count: key.view_key.msaa_samples(),257mask: !0,258alpha_to_coverage_enabled: false,259},260label: Some("LineJointGizmo 3d Pipeline".into()),261..default()262}263}264}265266type DrawLineGizmo3d = (267SetItemPipeline,268SetMeshViewBindGroup<0>,269SetLineGizmoBindGroup<1>,270DrawLineGizmo<false>,271);272type DrawLineGizmo3dStrip = (273SetItemPipeline,274SetMeshViewBindGroup<0>,275SetLineGizmoBindGroup<1>,276DrawLineGizmo<true>,277);278type DrawLineJointGizmo3d = (279SetItemPipeline,280SetMeshViewBindGroup<0>,281SetLineGizmoBindGroup<1>,282DrawLineJointGizmo,283);284285fn queue_line_gizmos_3d(286draw_functions: Res<DrawFunctions<Transparent3d>>,287mut pipeline: ResMut<LineGizmoPipeline>,288pipeline_cache: Res<PipelineCache>,289line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,290line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,291mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,292views: Query<(&ExtractedView, Option<&RenderLayers>)>,293view_key_cache: Res<ViewKeyCache>,294) -> Result<(), BevyError> {295let draw_function = draw_functions.read().get_id::<DrawLineGizmo3d>().unwrap();296let draw_function_strip = draw_functions297.read()298.get_id::<DrawLineGizmo3dStrip>()299.unwrap();300301for (view, render_layers) in &views {302let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)303else {304continue;305};306307let render_layers = render_layers.unwrap_or_default();308309let Some(&view_key) = view_key_cache.get(&view.retained_view_entity) else {310continue;311};312313for (entity, main_entity, config) in &line_gizmos {314if !config.render_layers.intersects(render_layers) {315continue;316}317318let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {319continue;320};321322if line_gizmo.list_vertex_count > 0 {323let pipeline = pipeline.variants.specialize(324&pipeline_cache,325LineGizmoPipelineKey {326view_key,327strip: false,328perspective: config.line_perspective,329line_style: config.line_style,330},331)?;332transparent_phase.add(Transparent3d {333entity: (entity, *main_entity),334draw_function,335pipeline,336distance: 0.,337batch_range: 0..1,338extra_index: PhaseItemExtraIndex::None,339indexed: true,340});341}342343if line_gizmo.strip_vertex_count >= 2 {344let pipeline = pipeline.variants.specialize(345&pipeline_cache,346LineGizmoPipelineKey {347view_key,348strip: true,349perspective: config.line_perspective,350line_style: config.line_style,351},352)?;353transparent_phase.add(Transparent3d {354entity: (entity, *main_entity),355draw_function: draw_function_strip,356pipeline,357distance: 0.,358batch_range: 0..1,359extra_index: PhaseItemExtraIndex::None,360indexed: true,361});362}363}364}365366Ok(())367}368369fn queue_line_joint_gizmos_3d(370draw_functions: Res<DrawFunctions<Transparent3d>>,371pipeline: Res<LineJointGizmoPipeline>,372mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,373pipeline_cache: Res<PipelineCache>,374line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,375line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,376mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,377views: Query<(&ExtractedView, Option<&RenderLayers>)>,378view_key_cache: Res<ViewKeyCache>,379) {380let draw_function = draw_functions381.read()382.get_id::<DrawLineJointGizmo3d>()383.unwrap();384385for (view, render_layers) in &views {386let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)387else {388continue;389};390391let render_layers = render_layers.unwrap_or_default();392393let Some(&view_key) = view_key_cache.get(&view.retained_view_entity) else {394continue;395};396397for (entity, main_entity, config) in &line_gizmos {398if !config.render_layers.intersects(render_layers) {399continue;400}401402let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {403continue;404};405406if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None {407continue;408}409410let pipeline = pipelines.specialize(411&pipeline_cache,412&pipeline,413LineJointGizmoPipelineKey {414view_key,415perspective: config.line_perspective,416joints: config.line_joints,417},418);419420transparent_phase.add(Transparent3d {421entity: (entity, *main_entity),422draw_function,423pipeline,424distance: 0.,425batch_range: 0..1,426extra_index: PhaseItemExtraIndex::None,427indexed: true,428});429}430}431}432433434