wgpu_3dgs_editor/
selection_modifier.rs

1use crate::{
2    BasicModifier, Modifier, SelectionBuffer, SelectionBundle, SelectionExpr, WithSelection,
3    core::{
4        ComputeBundle, GaussianPod, GaussianTransformBuffer, GaussiansBuffer, ModelTransformBuffer,
5    },
6};
7
8/// A struct to handle custom selection and custom modifier together.
9///
10/// ## Overview
11///
12/// This modifier holdes a [`SelectionBundle`] and a [`Modifier`] along with necessary
13/// buffers, and applies the selection followed by the basic modifier in order.
14///
15/// The [`Modifier`] can use the [`SelectionModifier::selection_buffer`] to determine which
16/// Gaussians to modify.
17///
18/// ## Usage
19///
20/// You can supply your own selection bundles and modifier when creating a
21/// [`SelectionModifier`].
22///
23/// The creation expects a modifier factory function instead of a modifier,
24/// so that the modifier can be created with a reference to the selection buffer.
25///
26/// ```rust
27/// // Create your selection bundles
28/// let selection_bundles = vec![
29///   SelectionBundle::<GaussianPod>::create_sphere_bundle(&device), // The built-in sphere selection bundle as example
30/// ];
31///
32/// struct MyCustomModifier(ComputeBundle);
33///
34/// impl MyCustomModifier {
35///     pub fn new(device: &wgpu::Device, /* Your buffers */, selection: &SelectionBuffer) -> Self {
36///         // Build your compute bundle here,
37///         // and include the selection buffer to only modify selected Gaussians
38///         let compute_bundle = ComputeBundleBuilder::new().build(&device, /* Your buffers */);
39///         Self(compute_bundle)
40///     }
41/// }
42///
43/// impl Modifier<GaussianPod> for MyCustomModifier {
44///     fn apply(
45///         &self,
46///         device: &wgpu::Device,
47///         encoder: &mut wgpu::CommandEncoder,
48///         gaussians: &GaussiansBuffer<GaussianPod>,
49///         model_transform: &ModelTransformBuffer,
50///         gaussian_transform: &GaussianTransformBuffer,
51///     ) {
52///         self.0.dispatch(encoder, gaussians.len() as u32);
53///     }
54/// }
55///
56/// let selection_modifier = SelectionModifier::new(
57///     &device,
58///     &gaussians_buffer,
59///     selection_bundles,
60///     |selection_buffer| { // The factory closure
61///         BasicModifier::new_with_selection(
62///             device,
63///             // Your buffers,
64///             selection_buffer,
65///         )
66///     },
67/// );
68/// ```
69///
70/// Alternatively, you can use a modifier closure instead of a struct (but be reminded this could
71/// harm readability of your code).
72///
73/// ```rust
74/// let selection_modifier = SelectionModifier::<GaussianPod>::new(
75///     &device,
76///     &gaussians_buffer,
77///     selection_bundles,
78///     |selection_buffer| { // The factory closure
79///         // Build your compute bundle here,
80///         // and include the selection buffer to only modify selected Gaussians
81///         let modifier_bundle = ComputeBundleBuilder::new().build(&device, /* Your buffers */);
82///
83///         // This function signature has blanket impl of the modifier trait
84///         move |_device: &wgpu::Device,
85///               encoder: &mut wgpu::CommandEncoder,
86///               gaussians: &gs::core::GaussiansBuffer<GaussianPod>,
87///               _model_transform: &gs::core::ModelTransformBuffer,
88///               _gaussian_transform: &gs::core::GaussianTransformBuffer| {
89///             modifier_bundle.dispatch(encoder, gaussians.len() as u32);
90///         }
91///     },
92/// );
93/// ``````
94#[derive(Debug)]
95pub struct SelectionModifier<G: GaussianPod, M: Modifier<G>> {
96    pub selection_expr: SelectionExpr,
97    pub selection_buffer: SelectionBuffer,
98    pub selection: SelectionBundle<G>,
99    pub modifier: M,
100}
101
102impl<G: GaussianPod, M: Modifier<G>> SelectionModifier<G, M> {
103    /// Create a new selection modifier.
104    ///
105    /// `bundles` are used for [`SelectionExpr::Unary`], [`SelectionExpr::Binary`], or
106    /// [`SelectionExpr::Selection`], they must have the same bind group 0 as the
107    /// [`SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`], see documentation of
108    /// [`SelectionBundle`] for more details.
109    pub fn new(
110        device: &wgpu::Device,
111        gaussians_buffer: &GaussiansBuffer<G>,
112        selection_bundles: Vec<ComputeBundle<()>>,
113        modifier: impl FnOnce(&SelectionBuffer) -> M,
114    ) -> Self {
115        log::debug!("Creating selection buffer");
116        let selection_buffer = SelectionBuffer::new(device, gaussians_buffer.len() as u32);
117
118        log::debug!("Creating selection bundle");
119        let selection = SelectionBundle::<G>::new(device, selection_bundles);
120
121        log::debug!("Creating modifier");
122        let modifier = modifier(&selection_buffer);
123
124        log::debug!("Creating selection modifier");
125
126        Self {
127            selection_expr: SelectionExpr::default(),
128            selection_buffer,
129            selection,
130            modifier,
131        }
132    }
133
134    /// Apply the selection and the modifier with the given expression and buffer.
135    ///
136    /// [`SelectionModifier::apply`] is the equivalent of applying this function with
137    /// [`SelectionModifier::selection_expr`] and [`SelectionModifier::selection_buffer`].
138    pub fn apply_with(
139        &self,
140        device: &wgpu::Device,
141        encoder: &mut wgpu::CommandEncoder,
142        gaussians: &GaussiansBuffer<G>,
143        model_transform: &ModelTransformBuffer,
144        gaussian_transform: &GaussianTransformBuffer,
145        selection_expr: &SelectionExpr,
146        selection_buffer: &SelectionBuffer,
147    ) {
148        self.selection.evaluate(
149            device,
150            encoder,
151            selection_expr,
152            selection_buffer,
153            model_transform,
154            gaussian_transform,
155            gaussians,
156        );
157
158        self.modifier.apply(
159            device,
160            encoder,
161            gaussians,
162            model_transform,
163            gaussian_transform,
164        );
165    }
166}
167
168impl<G: GaussianPod, M: Modifier<G>> Modifier<G> for SelectionModifier<G, M> {
169    fn apply(
170        &self,
171        device: &wgpu::Device,
172        encoder: &mut wgpu::CommandEncoder,
173        gaussians: &GaussiansBuffer<G>,
174        model_transform: &ModelTransformBuffer,
175        gaussian_transform: &GaussianTransformBuffer,
176    ) {
177        self.apply_with(
178            device,
179            encoder,
180            gaussians,
181            model_transform,
182            gaussian_transform,
183            &self.selection_expr,
184            &self.selection_buffer,
185        );
186    }
187}
188
189/// A type alias of [`SelectionModifier`] with [`BasicModifier`].
190pub type BasicSelectionModifier<G> = SelectionModifier<G, BasicModifier<G, WithSelection>>;
191
192impl<G: GaussianPod> BasicSelectionModifier<G> {
193    /// Create a new selection modifier with [`BasicModifier`].
194    ///
195    /// `bundles` are used for [`SelectionExpr::Unary`], [`SelectionExpr::Binary`], or
196    /// [`SelectionExpr::Selection`], they must have the same bind group 0 as the
197    /// [`SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`], see documentation of
198    /// [`SelectionBundle`] for more details.
199    pub fn new_with_basic_modifier(
200        device: &wgpu::Device,
201        gaussians_buffer: &GaussiansBuffer<G>,
202        model_transform: &ModelTransformBuffer,
203        gaussian_transform: &GaussianTransformBuffer,
204        selection_bundles: Vec<ComputeBundle<()>>,
205    ) -> Self {
206        Self::new(
207            device,
208            gaussians_buffer,
209            selection_bundles,
210            |selection_buffer| {
211                BasicModifier::new_with_selection(
212                    device,
213                    gaussians_buffer,
214                    model_transform,
215                    gaussian_transform,
216                    selection_buffer,
217                )
218            },
219        )
220    }
221}