bevy_vector_shapes/render/
render_3d.rs1use bevy::{
2 core_pipeline::core_3d::*,
3 ecs::entity::hash_map::EntityHashMap,
4 platform::collections::HashMap,
5 prelude::*,
6 render::{
7 render_phase::DrawFunctions,
8 render_resource::*,
9 sync_world::{MainEntity, RenderEntity, TemporaryRenderEntity},
10 view::ExtractedView,
11 Extract,
12 },
13};
14
15use crate::{painter::ShapeStorage, render::*, shapes::Shape3d};
16
17#[derive(Resource, Deref, DerefMut)]
18pub struct Shape3dInstances<T: ShapeData>(EntityHashMap<ShapeInstance<T>>);
19
20impl<T: ShapeData> Default for Shape3dInstances<T> {
21 fn default() -> Self {
22 Self(Default::default())
23 }
24}
25
26#[derive(Resource, Deref, DerefMut)]
27pub struct Shape3dMaterials<T: ShapeData>(
28 #[deref] HashMap<ShapePipelineMaterial, Vec<Entity>>,
29 PhantomData<T>,
30);
31
32impl<T: ShapeData> Default for Shape3dMaterials<T> {
33 fn default() -> Self {
34 Self(Default::default(), Default::default())
35 }
36}
37
38pub fn extract_shapes_3d<T: ShapeData>(
39 mut commands: Commands,
40 entities: Extract<
41 Query<
42 (
43 Entity,
44 &T::Component,
45 &ShapeFill,
46 &GlobalTransform,
47 &InheritedVisibility,
48 Option<&ShapeMaterial>,
49 Option<&RenderLayers>,
50 Option<&ShapeOrigin>,
51 ),
52 With<Shape3d>,
53 >,
54 >,
55 storage: Extract<Res<ShapeStorage>>,
56 mut instance_data: ResMut<Shape3dInstances<T>>,
57 mut materials: ResMut<Shape3dMaterials<T>>,
58 render_entities: Extract<Query<&RenderEntity>>,
59 mut canvases: Local<EntityHashMap<Entity>>,
60) {
61 instance_data.clear();
62 materials.clear();
63 canvases.clear();
64
65 entities
66 .iter()
67 .filter_map(|(e, cp, fill, tf, vis, flags, rl, or)| {
68 if vis.get() {
69 let local_origin = or.map(|or| or.0).unwrap_or(Vec3::ZERO);
71 let origin = tf.transform_point(local_origin);
72
73 Some((
74 e,
75 ShapeInstance {
76 material: ShapePipelineMaterial::new(flags, rl),
77 origin,
78 data: cp.get_data(tf, fill),
79 },
80 ))
81 } else {
82 None
83 }
84 })
85 .for_each(|(entity, instance)| {
86 materials
87 .entry(instance.material.clone())
88 .or_default()
89 .push(entity);
90 instance_data.insert(entity, instance);
91 });
92
93 if let Some(iter) = storage.get::<T>(ShapePipelineType::Shape3d) {
94 iter.cloned().for_each(|mut instance| {
95 let entity = commands.spawn(TemporaryRenderEntity).id();
96 if let Some(canvas) = &mut instance.material.canvas {
97 *canvas = *canvases.entry(*canvas).or_insert_with(|| {
98 render_entities
99 .get(*canvas)
100 .map(|e| e.id())
101 .unwrap_or(Entity::PLACEHOLDER)
102 });
103 }
104 materials
105 .entry(instance.material.clone())
106 .or_default()
107 .push(entity);
108 instance_data.insert(entity, instance);
109 });
110 }
111}
112
113#[allow(clippy::too_many_arguments)]
114pub fn queue_shapes_3d<T: ShapeData>(
115 transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
118 pipeline: Res<Shape3dPipeline<T>>,
119 pipeline_cache: Res<PipelineCache>,
120 materials: Res<Shape3dMaterials<T>>,
121 instance_data: Res<Shape3dInstances<T>>,
122 mut shape_pipelines: ResMut<ShapePipelines>,
123 mut trans_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
126 mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
127) {
128 let draw_transparent = transparent_draw_functions
133 .read()
134 .id::<DrawShape3dCommand<T>>();
135 let view_count = views.iter().count();
136
137 for (material, entities) in materials.iter() {
138 let mut key = ShapePipelineKey::from_material(material);
139 if !material.disable_laa {
140 key |= ShapePipelineKey::LOCAL_AA;
141 }
142
143 let mut visible_views = Vec::with_capacity(view_count);
144 if let Some(canvas) = material.canvas {
145 if let Ok(view) = views.get_mut(canvas) {
146 visible_views.push(view);
147 }
148 } else {
149 views
150 .iter_mut()
151 .filter(|(_, _, layers)| {
152 let render_layers = layers.cloned().unwrap_or_default();
153 render_layers.intersects(&material.render_layers.0)
154 })
155 .for_each(|view| visible_views.push(view))
156 };
157
158 for (view, msaa, _) in visible_views.into_iter() {
159 let Some(transparent_phase) = trans_phases.get_mut(&view.retained_view_entity) else {
167 continue;
168 };
169 let mut view_key = key;
170 view_key |= ShapePipelineKey::from_msaa_samples(msaa.samples());
171 view_key |= ShapePipelineKey::from_hdr(view.hdr);
172 let pipeline = shape_pipelines.specialize(&pipeline_cache, pipeline.as_ref(), view_key);
173
174 let rangefinder = view.rangefinder3d();
178 for &entity in entities {
179 let instance = unsafe { instance_data.get(&entity).unwrap_unchecked() };
181 let distance = rangefinder.distance_translation(&instance.origin);
182 transparent_phase.add(Transparent3d {
183 entity: (entity, MainEntity::from(Entity::PLACEHOLDER)),
184 draw_function: draw_transparent,
185 pipeline,
186 distance,
187 batch_range: 0..1,
188 extra_index: PhaseItemExtraIndex::None,
189 indexed: false,
190 });
191 }
192 }
193 }
194}
195
196#[derive(Resource)]
197pub struct Shape3dBindGroup<T: ShapeData> {
198 pub value: BindGroup,
199 _marker: PhantomData<T>,
200}
201
202pub fn prepare_shape_3d_bind_group<T: ShapeData + 'static>(
203 mut commands: Commands,
204 pipeline: Res<Shape3dPipeline<T>>,
205 render_device: Res<RenderDevice>,
206 shape_buffer: Res<BatchedInstanceBuffer<T>>,
207) {
208 if let Some(binding) = shape_buffer.binding() {
209 commands.insert_resource(Shape3dBindGroup {
210 value: render_device.create_bind_group(
211 "shape_bind_group",
212 &pipeline.layout,
213 &BindGroupEntries::single(binding),
214 ),
215 _marker: PhantomData::<T>,
216 });
217 }
218}