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 no_run
27/// # pollster::block_on(async {
28/// # use wgpu_3dgs_editor::{
29/// #     Editor, Modifier, SelectionBuffer, SelectionBundle, SelectionModifier,
30/// #     core::{
31/// #         self, BufferWrapper, glam::*,
32/// #     },
33/// # };
34/// #
35/// # type GaussianPod = core::GaussianPodWithShSingleCov3dSingleConfigs;
36/// #
37/// # let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
38/// #
39/// # let adapter = instance
40/// #     .request_adapter(&wgpu::RequestAdapterOptions::default())
41/// #     .await
42/// #     .expect("adapter");
43/// #
44/// # let (device, _queue) = adapter
45/// #     .request_device(&wgpu::DeviceDescriptor {
46/// #         label: Some("Device"),
47/// #         required_limits: adapter.limits(),
48/// #         ..Default::default()
49/// #     })
50/// #     .await
51/// #     .expect("device");
52/// #
53/// // Create an editor that holds the buffers for the Gaussians and will apply the modifier
54/// let editor = Editor::new(
55///     &device,
56///     &vec![core::Gaussian {
57///         rot: Quat::IDENTITY,
58///         pos: Vec3::ZERO,
59///         color: U8Vec4::ZERO,
60///         sh: [Vec3::ZERO; 15],
61///         scale: Vec3::ONE,
62///     }],
63/// );
64///
65/// // Create your selection bundles
66/// let selection_bundles = vec![
67///     // The built-in sphere selection bundle as example
68///     SelectionBundle::<GaussianPod>::create_sphere_bundle(&device),
69/// ];
70///
71/// struct MyCustomModifier(core::ComputeBundle);
72///
73/// impl MyCustomModifier {
74///     pub fn new(
75///         device: &wgpu::Device,
76///         // My buffers,
77///         selection: &SelectionBuffer,
78///     ) -> Self {
79///         // Build your compute bundle here,
80///         // and include the selection buffer to only modify selected Gaussians
81///         let compute_bundle = core::ComputeBundleBuilder::new()
82///             // Configure the modifier compute bundle here
83///             .build(
84///                 &device,
85///                 [
86///                     [selection.buffer().as_entire_binding()].to_vec(),
87///                     [/* Your buffers */].to_vec(),
88///                 ],
89///             )
90///             .unwrap();
91///         Self(compute_bundle)
92///     }
93/// }
94///
95/// impl Modifier<GaussianPod> for MyCustomModifier {
96///     fn apply(
97///         &self,
98///         _device: &wgpu::Device,
99///         encoder: &mut wgpu::CommandEncoder,
100///         gaussians: &core::GaussiansBuffer<GaussianPod>,
101///         _model_transform: &core::ModelTransformBuffer,
102///         _gaussian_transform: &core::GaussianTransformBuffer,
103///     ) {
104///         self.0.dispatch(encoder, gaussians.len() as u32);
105///     }
106/// }
107///
108/// let selection_modifier = SelectionModifier::new(
109///     &device,
110///     &editor.gaussians_buffer,
111///     selection_bundles,
112///     |selection_buffer| {
113///         // The factory closure
114///         MyCustomModifier::new(
115///             &device,
116///             // Your buffers,
117///             selection_buffer,
118///         )
119///     },
120/// );
121/// # });
122/// ```
123///
124/// Alternatively, you can use a modifier closure instead of a struct (but be reminded this could
125/// harm readability of your code).
126///
127/// ```rust no_run
128/// # pollster::block_on(async {
129/// # use wgpu_3dgs_editor::{
130/// #     Editor, Modifier, SelectionBuffer, SelectionBundle, SelectionModifier,
131/// #     core::{
132/// #         self, BufferWrapper, glam::*,
133/// #     },
134/// # };
135/// #
136/// # type GaussianPod = core::GaussianPodWithShSingleCov3dSingleConfigs;
137/// #
138/// # let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
139/// #
140/// # let adapter = instance
141/// #     .request_adapter(&wgpu::RequestAdapterOptions::default())
142/// #     .await
143/// #     .expect("adapter");
144/// #
145/// # let (device, _queue) = adapter
146/// #     .request_device(&wgpu::DeviceDescriptor {
147/// #         label: Some("Device"),
148/// #         required_limits: adapter.limits(),
149/// #         ..Default::default()
150/// #     })
151/// #     .await
152/// #     .expect("device");
153/// #
154/// # let editor = Editor::new(
155/// #     &device,
156/// #     &vec![core::Gaussian {
157/// #         rot: Quat::IDENTITY,
158/// #         pos: Vec3::ZERO,
159/// #         color: U8Vec4::ZERO,
160/// #         sh: [Vec3::ZERO; 15],
161/// #         scale: Vec3::ONE,
162/// #     }],
163/// # );
164/// #
165/// # let selection_bundles = vec![
166/// #     // The built-in sphere selection bundle as example
167/// #     SelectionBundle::<GaussianPod>::create_sphere_bundle(&device),
168/// # ];
169/// #
170/// let selection_modifier = SelectionModifier::new(
171///     &device,
172///     &editor.gaussians_buffer,
173///     selection_bundles,
174///     |selection_buffer| {
175///         // The factory closure
176///         // Build your compute bundle here,
177///         // and include the selection buffer to only modify selected Gaussians
178///         let modifier_bundle = core::ComputeBundleBuilder::new()
179///             .build(
180///                 &device,
181///                 [
182///                     [selection_buffer.buffer().as_entire_binding()].to_vec(),
183///                     [/* Your buffers */].to_vec(),
184///                 ],
185///             )
186///             .unwrap();
187///
188///         // This function signature has blanket impl of the modifier trait
189///         move |_device: &wgpu::Device,
190///                 encoder: &mut wgpu::CommandEncoder,
191///                 gaussians: &core::GaussiansBuffer<GaussianPod>,
192///                 _model_transform: &core::ModelTransformBuffer,
193///                 _gaussian_transform: &core::GaussianTransformBuffer| {
194///             modifier_bundle.dispatch(encoder, gaussians.len() as u32);
195///         }
196///     },
197/// );
198/// # });
199/// ```
200#[derive(Debug)]
201pub struct SelectionModifier<G: GaussianPod, M: Modifier<G>> {
202    pub selection_expr: SelectionExpr,
203    pub selection_buffer: SelectionBuffer,
204    pub selection: SelectionBundle<G>,
205    pub modifier: M,
206}
207
208impl<G: GaussianPod, M: Modifier<G>> SelectionModifier<G, M> {
209    /// Create a new selection modifier.
210    ///
211    /// `bundles` are used for [`SelectionExpr::Unary`], [`SelectionExpr::Binary`], or
212    /// [`SelectionExpr::Selection`], they must have the same bind group 0 as the
213    /// [`SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`], see documentation of
214    /// [`SelectionBundle`] for more details.
215    pub fn new(
216        device: &wgpu::Device,
217        gaussians_buffer: &GaussiansBuffer<G>,
218        selection_bundles: Vec<ComputeBundle<()>>,
219        modifier: impl FnOnce(&SelectionBuffer) -> M,
220    ) -> Self {
221        log::debug!("Creating selection buffer");
222        let selection_buffer = SelectionBuffer::new(device, gaussians_buffer.len() as u32);
223
224        log::debug!("Creating selection bundle");
225        let selection = SelectionBundle::<G>::new(device, selection_bundles);
226
227        log::debug!("Creating modifier");
228        let modifier = modifier(&selection_buffer);
229
230        log::debug!("Creating selection modifier");
231
232        Self {
233            selection_expr: SelectionExpr::default(),
234            selection_buffer,
235            selection,
236            modifier,
237        }
238    }
239}
240
241impl<G: GaussianPod, M: Modifier<G>> Modifier<G> for SelectionModifier<G, M> {
242    fn apply(
243        &self,
244        device: &wgpu::Device,
245        encoder: &mut wgpu::CommandEncoder,
246        gaussians: &GaussiansBuffer<G>,
247        model_transform: &ModelTransformBuffer,
248        gaussian_transform: &GaussianTransformBuffer,
249    ) {
250        self.selection.evaluate(
251            device,
252            encoder,
253            &self.selection_expr,
254            &self.selection_buffer,
255            model_transform,
256            gaussian_transform,
257            gaussians,
258        );
259
260        self.modifier.apply(
261            device,
262            encoder,
263            gaussians,
264            model_transform,
265            gaussian_transform,
266        );
267    }
268}
269
270/// A type alias of [`SelectionModifier`] with [`BasicModifier`].
271pub type BasicSelectionModifier<G> = SelectionModifier<G, BasicModifier<G, WithSelection>>;
272
273impl<G: GaussianPod> BasicSelectionModifier<G> {
274    /// Create a new selection modifier with [`BasicModifier`].
275    ///
276    /// `bundles` are used for [`SelectionExpr::Unary`], [`SelectionExpr::Binary`], or
277    /// [`SelectionExpr::Selection`], they must have the same bind group 0 as the
278    /// [`SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`], see documentation of
279    /// [`SelectionBundle`] for more details.
280    pub fn new_with_basic_modifier(
281        device: &wgpu::Device,
282        gaussians_buffer: &GaussiansBuffer<G>,
283        model_transform: &ModelTransformBuffer,
284        gaussian_transform: &GaussianTransformBuffer,
285        selection_bundles: Vec<ComputeBundle<()>>,
286    ) -> Self {
287        Self::new(
288            device,
289            gaussians_buffer,
290            selection_bundles,
291            |selection_buffer| {
292                BasicModifier::new_with_selection(
293                    device,
294                    gaussians_buffer,
295                    model_transform,
296                    gaussian_transform,
297                    selection_buffer,
298                )
299            },
300        )
301    }
302}