Skip to main content

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