Skip to main content

wgpu_3dgs_editor/
modifier.rs

1use crate::{
2    BasicColorModifiersBuffer, RotScaleBuffer, SelectionBuffer, TransformFlagsBuffer,
3    core::{
4        self, BufferWrapper, ComputeBundle, ComputeBundleBuilder, GaussianPod,
5        GaussianTransformBuffer, GaussiansBuffer, ModelTransformBuffer,
6    },
7    shader,
8};
9
10/// A trait to apply modifier to Gaussians.
11///
12/// ## Overview
13///
14/// This trait simply defines a method to apply modifications to a set of Gaussians stored in a
15/// [`GaussiansBuffer`]. It makes it convenient for users to apply a sequence of modifications.
16///
17/// The trait is also blanket implemented for closures with the same signature, allowing users to
18/// easily create modifier closures instead of having to define a modifier struct.
19///
20/// [`Editor`](crate::Editor) also provides an `apply` method which takes a slice of
21/// [`Modifier`]s to apply them in sequence to the stored Gaussians.
22///
23/// ## Usage
24///
25/// There are many ways to use this but the recommended way is to implement this trait for a closure
26/// which dispatch a [`ComputeBundle`].
27///
28/// ```rust no_run
29/// # pollster::block_on(async {
30/// # use wgpu_3dgs_editor::{
31/// #     Editor, MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR, Modifier,
32/// #     core::{
33/// #         self, BufferWrapper, GaussianPod as _, GaussianTransformBuffer,
34/// #         GaussiansBuffer, ModelTransformBuffer, glam::*,
35/// #     },
36/// #     shader,
37/// # };
38/// #
39/// # type GaussianPod = core::GaussianPodWithShSingleCov3dSingleConfigs;
40/// #
41/// # let instance = wgpu::Instance::new(
42/// #     wgpu::InstanceDescriptor::new_without_display_handle_from_env()
43/// # );
44/// #
45/// # let adapter = instance
46/// #     .request_adapter(&wgpu::RequestAdapterOptions::default())
47/// #     .await
48/// #     .expect("adapter");
49/// #
50/// # let (device, _queue) = adapter
51/// #     .request_device(&wgpu::DeviceDescriptor {
52/// #         label: Some("Device"),
53/// #         required_limits: adapter.limits(),
54/// #         ..Default::default()
55/// #     })
56/// #     .await
57/// #     .expect("device");
58/// #
59/// # const MY_CUSTOM_BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor =
60/// #     wgpu::BindGroupLayoutDescriptor {
61/// #         label: Some("My Custom Bind Group Layout"),
62/// #         entries: &[wgpu::BindGroupLayoutEntry {
63/// #             binding: 0,
64/// #             visibility: wgpu::ShaderStages::COMPUTE,
65/// #             ty: wgpu::BindingType::Buffer {
66/// #                 ty: wgpu::BufferBindingType::Uniform,
67/// #                 has_dynamic_offset: false,
68/// #                 min_binding_size: None,
69/// #             },
70/// #             count: None,
71/// #         }],
72/// #     };
73/// #
74/// # let my_buffer = device.create_buffer(&wgpu::BufferDescriptor {
75/// #     label: Some("My Buffer"),
76/// #     size: 4,
77/// #     usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
78/// #     mapped_at_creation: false,
79/// # });
80/// #
81/// // Create an editor that holds the buffers for the Gaussians and will apply the modifier
82/// let editor = Editor::new(
83///     &device,
84///     &vec![core::Gaussian {
85///         rot: Quat::IDENTITY,
86///         pos: Vec3::ZERO,
87///         color: U8Vec4::ZERO,
88///         sh: [Vec3::ZERO; 15],
89///         scale: Vec3::ONE,
90///     }],
91/// );
92///
93/// // Create the modifier compute bundle
94/// let my_modifier_bundle = core::ComputeBundleBuilder::new()
95///     .label("My Modifier")
96///     .bind_group_layouts([
97///         // For accessing Gaussians and transforms
98///         &MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
99///         // Your custom bind group layout here
100///         &MY_CUSTOM_BIND_GROUP_LAYOUT_DESCRIPTOR,
101///     ])
102///     .resolver({
103///         let mut resolver =
104///             wesl::StandardResolver::new("path/to/my/folder/containing/wesl");
105///         // Required for using core buffer structs.
106///         resolver.add_package(&core::shader::PACKAGE);
107///         // Optionally add this for some utility functions.
108///         resolver.add_package(&shader::PACKAGE);
109///         resolver
110///     })
111///     .main_shader("package::my_wesl_filename".parse().unwrap())
112///     .entry_point("main")
113///     .wesl_compile_options(wesl::CompileOptions {
114///         // Required for enabling the correct features for core struct.
115///         features: GaussianPod::wesl_features(),
116///         ..Default::default()
117///     })
118///     .build(
119///         &device,
120///         [
121///             vec![
122///                 editor.gaussians_buffer.buffer().as_entire_binding(),
123///                 editor.model_transform_buffer.buffer().as_entire_binding(),
124///                 editor.gaussian_transform_buffer.buffer().as_entire_binding(),
125///             ],
126///             vec![my_buffer.buffer().as_entire_binding()],
127///         ],
128///     )
129///     .map_err(|e| log::error!("{e}"))
130///     .expect("my modifier bundle");
131///
132/// // Create the modifier closure
133/// // This function signature implements Modifier by default
134/// let my_modifier =
135///     |_device: &wgpu::Device,
136///         encoder: &mut wgpu::CommandEncoder,
137///         gaussians: &GaussiansBuffer<GaussianPod>,
138///         _model_transform: &ModelTransformBuffer,
139///         _gaussian_transform: &GaussianTransformBuffer| {
140///         my_modifier_bundle.dispatch(encoder, gaussians.len() as u32);
141///     };
142///
143/// # let mut encoder =
144/// #     device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
145///
146/// // Apply the modifier using the editor
147/// editor.apply(
148///     &device,
149///     &mut encoder,
150///     [&my_modifier as &dyn Modifier<GaussianPod>],
151/// );
152/// # });
153/// ```
154///
155/// ## Shader Format
156///
157/// You may copy and paste the following shader bindings for
158/// [`MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`] into your custom selection operation
159/// shader to ensure that the bindings are correct, then add your own bindings after that.
160///
161/// ```wgsl
162/// import wgpu_3dgs_core::{
163///     gaussian::Gaussian,
164///     gaussian_transform::GaussianTransform,
165///     model_transform::ModelTransform,
166/// };
167///
168/// @group(0) @binding(0)
169/// var<storage, read_write> gaussians: array<Gaussian>;
170///
171/// @group(0) @binding(1)
172/// var<uniform> model_transform: ModelTransform;
173///
174/// @group(0) @binding(2)
175/// var<uniform> gaussian_transform: GaussianTransform;
176///
177/// // Your custom bindings here...
178/// //
179/// // You may also apply modifier to selected gaussians only by adding:
180/// // @group(1) @binding(N)
181/// // var<storage, read> selection: array<u32>;
182///
183/// override workgroup_size: u32;
184///
185/// @compute @workgroup_size(workgroup_size)
186/// fn main(@builtin(global_invocation_id) id: vec3<u32>) {
187///     let index = id.x;
188///
189///     if index >= arrayLength(&gaussians) {
190///         return;
191///     }
192///
193///     @if(/* using selection buffer */) {
194///         let word_index = index / 32u;
195///         let bit_index = index % 32u;
196///         let bit_mask = 1u << bit_index;
197///         if (selection[word_index] & bit_mask) == 0 {
198///             return;
199///         }
200///     }
201///
202///     var gaussian = gaussians[index];
203///
204///     // Your custom modifier operation code here...
205///
206///     gaussians[index] = gaussian;
207/// }
208/// ```
209pub trait Modifier<G: GaussianPod> {
210    /// Apply the modifier to the Gaussians.
211    fn apply(
212        &self,
213        device: &wgpu::Device,
214        encoder: &mut wgpu::CommandEncoder,
215        gaussians: &GaussiansBuffer<G>,
216        model_transform: &ModelTransformBuffer,
217        gaussian_transform: &GaussianTransformBuffer,
218    );
219}
220
221impl<
222    G: GaussianPod,
223    F: Fn(
224        &wgpu::Device,
225        &mut wgpu::CommandEncoder,
226        &GaussiansBuffer<G>,
227        &ModelTransformBuffer,
228        &GaussianTransformBuffer,
229    ),
230> Modifier<G> for F
231{
232    fn apply(
233        &self,
234        device: &wgpu::Device,
235        encoder: &mut wgpu::CommandEncoder,
236        gaussians: &GaussiansBuffer<G>,
237        model_transform: &ModelTransformBuffer,
238        gaussian_transform: &GaussianTransformBuffer,
239    ) {
240        self(
241            device,
242            encoder,
243            gaussians,
244            model_transform,
245            gaussian_transform,
246        );
247    }
248}
249
250/// The bind group layout descriptor for the Gaussians buffer, with the
251/// model transform and Gaussian transform.
252///
253/// This bind group layout takes the following buffers:
254/// - [`GaussiansBuffer`]
255/// - [`ModelTransformBuffer`]
256/// - [`GaussianTransformBuffer`]
257///
258/// This bind group is usually at group 0 for [`Modifier`]s.
259pub const MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor =
260    wgpu::BindGroupLayoutDescriptor {
261        label: Some("Modifier Gaussians Bind Group Layout"),
262        entries: &[
263            // Gaussians storage buffer
264            wgpu::BindGroupLayoutEntry {
265                binding: 0,
266                visibility: wgpu::ShaderStages::COMPUTE,
267                ty: wgpu::BindingType::Buffer {
268                    ty: wgpu::BufferBindingType::Storage { read_only: false },
269                    has_dynamic_offset: false,
270                    min_binding_size: None,
271                },
272                count: None,
273            },
274            // Model transform uniform buffer
275            wgpu::BindGroupLayoutEntry {
276                binding: 1,
277                visibility: wgpu::ShaderStages::COMPUTE,
278                ty: wgpu::BindingType::Buffer {
279                    ty: wgpu::BufferBindingType::Uniform,
280                    has_dynamic_offset: false,
281                    min_binding_size: None,
282                },
283                count: None,
284            },
285            // Gaussian transform uniform buffer
286            wgpu::BindGroupLayoutEntry {
287                binding: 2,
288                visibility: wgpu::ShaderStages::COMPUTE,
289                ty: wgpu::BindingType::Buffer {
290                    ty: wgpu::BufferBindingType::Uniform,
291                    has_dynamic_offset: false,
292                    min_binding_size: None,
293                },
294                count: None,
295            },
296        ],
297    };
298
299/// A marker struct to indicate that a modifier takes a selection buffer.
300#[derive(Debug)]
301pub struct WithSelection;
302
303/// A marker struct to indicate that a modifier does not take a selection buffer.
304#[derive(Debug)]
305pub struct NoSelection;
306
307/// A specialized [`ComputeBundle`] for some built-in basic modifier.
308///
309/// This bundle includes the modifiers for [`BasicColorModifiersBuffer`],
310/// [`RotScaleBuffer`], and [`TransformFlagsBuffer`] (which provides flags for applying
311/// [`core::ModelTransformBuffer`] and [`core::GaussianTransformBuffer`]).
312#[derive(Debug)]
313pub struct BasicModifierBundle<G: GaussianPod, S = NoSelection, B = wgpu::BindGroup> {
314    bundle: ComputeBundle<B>,
315    gaussian_pod_marker: std::marker::PhantomData<G>,
316    selection_marker: std::marker::PhantomData<S>,
317}
318
319impl<G: GaussianPod, S, B> BasicModifierBundle<G, S, B> {
320    /// Gets the inner [`ComputeBundle`].
321    pub fn bundle(&self) -> &ComputeBundle<B> {
322        &self.bundle
323    }
324}
325
326impl<G: GaussianPod> BasicModifierBundle<G> {
327    /// The bind group layout descriptor for the [`BasicModifierBundle`].
328    ///
329    /// This bind group layout takes the following buffers:
330    /// - [`TransformFlagsBuffer`]
331    /// - [`BasicColorModifiersBuffer`]
332    /// - [`RotScaleBuffer`]
333    ///
334    /// This is at group 1, because group 0 is the [`MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`].
335    pub const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> =
336        wgpu::BindGroupLayoutDescriptor {
337            label: Some("Basic Modifier Bind Group Layout"),
338            entries: BasicModifierBundle::<G, WithSelection>::BIND_GROUP_LAYOUT_DESCRIPTOR
339                .entries
340                .split_at(3)
341                .0,
342        };
343
344    /// Creates a new [`BasicModifierBundle`] bundle.
345    pub fn new(
346        device: &wgpu::Device,
347        gaussians_buffer: &GaussiansBuffer<G>,
348        model_transform_buffer: &ModelTransformBuffer,
349        gaussian_transform_buffer: &GaussianTransformBuffer,
350        transform_flags_buffer: &TransformFlagsBuffer,
351        basic_color_modifiers_buffer: &BasicColorModifiersBuffer,
352        rot_scale_buffer: &RotScaleBuffer,
353    ) -> Self {
354        Self::create_bundle_builder(false)
355            .build(
356                device,
357                [
358                    [
359                        gaussians_buffer.buffer().as_entire_binding(),
360                        model_transform_buffer.buffer().as_entire_binding(),
361                        gaussian_transform_buffer.buffer().as_entire_binding(),
362                    ],
363                    [
364                        transform_flags_buffer.buffer().as_entire_binding(),
365                        basic_color_modifiers_buffer.buffer().as_entire_binding(),
366                        rot_scale_buffer.buffer().as_entire_binding(),
367                    ],
368                ],
369            )
370            .map(|bundle| Self {
371                bundle,
372                gaussian_pod_marker: std::marker::PhantomData,
373                selection_marker: std::marker::PhantomData,
374            })
375            .map_err(|e| log::error!("{e}"))
376            .expect("basic modifier bundle")
377    }
378
379    /// Creates a new [`ComputeBundleBuilder`] for the basic modifier.
380    fn create_bundle_builder<'a>(
381        has_selection: bool,
382    ) -> ComputeBundleBuilder<'a, wesl::PkgResolver> {
383        ComputeBundleBuilder::new()
384            .label("Basic Modifier")
385            .bind_group_layouts([
386                &MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
387                match has_selection {
388                    true => &BasicModifierBundle::<G, WithSelection>::BIND_GROUP_LAYOUT_DESCRIPTOR,
389                    false => &BasicModifierBundle::<G>::BIND_GROUP_LAYOUT_DESCRIPTOR,
390                },
391            ])
392            .resolver({
393                let mut resolver = wesl::PkgResolver::new();
394                resolver.add_package(&core::shader::PACKAGE);
395                resolver.add_package(&shader::PACKAGE);
396                resolver
397            })
398            .main_shader(
399                "wgpu_3dgs_editor::modifier::basic"
400                    .parse()
401                    .expect("modifier::basic module path"),
402            )
403            .entry_point("main")
404            .wesl_compile_options(wesl::CompileOptions {
405                features: wesl::Features {
406                    flags: G::features()
407                        .into_iter()
408                        .chain(std::iter::once(("selection_buffer", has_selection)))
409                        .map(|(k, v)| (k.to_string(), v.into()))
410                        .collect(),
411                    ..Default::default()
412                },
413                ..Default::default()
414            })
415    }
416}
417
418impl<G: GaussianPod> BasicModifierBundle<G, WithSelection> {
419    /// The bind group layout descriptor for the [`BasicModifierBundle`] with a [`SelectionBuffer`].
420    ///
421    /// Thie bind group layout takes the following buffers:
422    /// - [`TransformFlagsBuffer`]
423    /// - [`BasicColorModifiersBuffer`]
424    /// - [`RotScaleBuffer`]
425    /// - [`SelectionBuffer`]
426    ///
427    /// This is at group 1, because group 0 is the [`MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`].
428    pub const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor<'static> =
429        wgpu::BindGroupLayoutDescriptor {
430            label: Some("Basic Modifier Bind Group Layout"),
431            entries: &[
432                // Transform flags uniform buffer
433                wgpu::BindGroupLayoutEntry {
434                    binding: 0,
435                    visibility: wgpu::ShaderStages::COMPUTE,
436                    ty: wgpu::BindingType::Buffer {
437                        ty: wgpu::BufferBindingType::Uniform,
438                        has_dynamic_offset: false,
439                        min_binding_size: None,
440                    },
441                    count: None,
442                },
443                // Basic color modifiers uniform buffer
444                wgpu::BindGroupLayoutEntry {
445                    binding: 1,
446                    visibility: wgpu::ShaderStages::COMPUTE,
447                    ty: wgpu::BindingType::Buffer {
448                        ty: wgpu::BufferBindingType::Uniform,
449                        has_dynamic_offset: false,
450                        min_binding_size: None,
451                    },
452                    count: None,
453                },
454                // Scale rotation uniform buffer
455                wgpu::BindGroupLayoutEntry {
456                    binding: 2,
457                    visibility: wgpu::ShaderStages::COMPUTE,
458                    ty: wgpu::BindingType::Buffer {
459                        ty: wgpu::BufferBindingType::Uniform,
460                        has_dynamic_offset: false,
461                        min_binding_size: None,
462                    },
463                    count: None,
464                },
465                // Selection buffer
466                wgpu::BindGroupLayoutEntry {
467                    binding: 3,
468                    visibility: wgpu::ShaderStages::COMPUTE,
469                    ty: wgpu::BindingType::Buffer {
470                        ty: wgpu::BufferBindingType::Storage { read_only: true },
471                        has_dynamic_offset: false,
472                        min_binding_size: None,
473                    },
474                    count: None,
475                },
476            ],
477        };
478
479    /// Creates a new [`BasicModifierBundle`] bundle with [`SelectionBuffer`].
480    #[allow(clippy::too_many_arguments)]
481    pub fn new_with_selection(
482        device: &wgpu::Device,
483        gaussians_buffer: &GaussiansBuffer<G>,
484        model_transform_buffer: &ModelTransformBuffer,
485        gaussian_transform_buffer: &GaussianTransformBuffer,
486        transform_flags_buffer: &TransformFlagsBuffer,
487        basic_color_modifiers_buffer: &BasicColorModifiersBuffer,
488        rot_scale_buffer: &RotScaleBuffer,
489        selection_buffer: &SelectionBuffer,
490    ) -> Self {
491        BasicModifierBundle::<G>::create_bundle_builder(true)
492            .build(
493                device,
494                [
495                    vec![
496                        gaussians_buffer.buffer().as_entire_binding(),
497                        model_transform_buffer.buffer().as_entire_binding(),
498                        gaussian_transform_buffer.buffer().as_entire_binding(),
499                    ],
500                    vec![
501                        transform_flags_buffer.buffer().as_entire_binding(),
502                        basic_color_modifiers_buffer.buffer().as_entire_binding(),
503                        rot_scale_buffer.buffer().as_entire_binding(),
504                        selection_buffer.buffer().as_entire_binding(),
505                    ],
506                ],
507            )
508            .map(|bundle| Self {
509                bundle,
510                gaussian_pod_marker: std::marker::PhantomData,
511                selection_marker: std::marker::PhantomData,
512            })
513            .map_err(|e| log::error!("{e}"))
514            .expect("basic modifier bundle")
515    }
516}
517
518impl<G: GaussianPod, S> BasicModifierBundle<G, S> {
519    /// Apply the basic modifier to the Gaussians.
520    pub fn apply_with_count(&self, encoder: &mut wgpu::CommandEncoder, gaussian_count: u32) {
521        self.bundle().dispatch(encoder, gaussian_count);
522    }
523}
524
525impl<G: GaussianPod, S> Modifier<G> for BasicModifierBundle<G, S> {
526    fn apply(
527        &self,
528        _device: &wgpu::Device,
529        encoder: &mut wgpu::CommandEncoder,
530        gaussians: &GaussiansBuffer<G>,
531        _model_transform: &ModelTransformBuffer,
532        _gaussian_transform: &GaussianTransformBuffer,
533    ) {
534        self.apply_with_count(encoder, gaussians.len() as u32);
535    }
536}
537
538impl<G: GaussianPod> BasicModifierBundle<G, NoSelection, ()> {
539    /// Creates a new [`BasicModifierBundle`] bundle without a bind group.
540    pub fn new_without_bind_group(device: &wgpu::Device) -> Self {
541        BasicModifierBundle::<G>::create_bundle_builder(false)
542            .build_without_bind_groups(device)
543            .map(|bundle| Self {
544                bundle,
545                gaussian_pod_marker: std::marker::PhantomData,
546                selection_marker: std::marker::PhantomData,
547            })
548            .expect("basic modifier bundle")
549    }
550}
551
552impl<G: GaussianPod> BasicModifierBundle<G, WithSelection, ()> {
553    /// Creates a new [`BasicModifierBundle`] bundle without a bind group with selection buffer.
554    pub fn new_without_bind_group_with_selection(device: &wgpu::Device) -> Self {
555        BasicModifierBundle::<G>::create_bundle_builder(true)
556            .build_without_bind_groups(device)
557            .map(|bundle| Self {
558                bundle,
559                gaussian_pod_marker: std::marker::PhantomData,
560                selection_marker: std::marker::PhantomData,
561            })
562            .expect("basic modifier bundle")
563    }
564}
565
566impl<G: GaussianPod, S> BasicModifierBundle<G, S, ()> {
567    /// Apply the basic modifier to the Gaussians.
568    ///
569    /// - `gaussians_bind_group` is the bind group created from [`MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR`].
570    /// - `bind_group` is the bind group created from [`BasicModifierBundle::BIND_GROUP_LAYOUT_DESCRIPTOR`].
571    pub fn apply_with_count(
572        &self,
573        encoder: &mut wgpu::CommandEncoder,
574        gaussians_bind_group: &wgpu::BindGroup,
575        bind_group: &wgpu::BindGroup,
576        gaussian_count: u32,
577    ) {
578        self.bundle()
579            .dispatch(encoder, gaussian_count, [gaussians_bind_group, bind_group]);
580    }
581}
582
583/// A struct to handle basic modifier.
584///
585/// This modifier holds a [`BasicModifierBundle`] along with necessary buffers, and applies the
586/// basic modifier.
587#[derive(Debug)]
588pub struct BasicModifier<G: GaussianPod, S = NoSelection> {
589    pub transform_flags_buffer: TransformFlagsBuffer,
590    pub basic_color_modifiers_buffer: BasicColorModifiersBuffer,
591    pub rot_scale_buffer: RotScaleBuffer,
592    pub modifier: BasicModifierBundle<G, S>,
593}
594
595impl<G: GaussianPod> BasicModifier<G> {
596    /// Create a new basic modifier.
597    pub fn new(
598        device: &wgpu::Device,
599        gaussians_buffer: &GaussiansBuffer<G>,
600        model_transform_buffer: &ModelTransformBuffer,
601        gaussian_transform_buffer: &GaussianTransformBuffer,
602    ) -> Self {
603        log::debug!("Creating transform flags buffer");
604        let transform_flags_buffer = TransformFlagsBuffer::new(device);
605
606        log::debug!("Creating basic color modifiers buffer");
607        let basic_color_modifiers_buffer = BasicColorModifiersBuffer::new(device);
608
609        log::debug!("Creating rotation scale buffer");
610        let rot_scale_buffer = RotScaleBuffer::new(device);
611
612        log::debug!("Creating basic modifier bundle");
613        let modifier = BasicModifierBundle::new(
614            device,
615            gaussians_buffer,
616            model_transform_buffer,
617            gaussian_transform_buffer,
618            &transform_flags_buffer,
619            &basic_color_modifiers_buffer,
620            &rot_scale_buffer,
621        );
622
623        log::debug!("Basic modifier created");
624
625        Self {
626            transform_flags_buffer,
627            basic_color_modifiers_buffer,
628            rot_scale_buffer,
629
630            modifier,
631        }
632    }
633}
634
635impl<G: GaussianPod> BasicModifier<G, WithSelection> {
636    /// Create a new basic modifier with selection.
637    pub fn new_with_selection(
638        device: &wgpu::Device,
639        gaussians_buffer: &GaussiansBuffer<G>,
640        model_transform_buffer: &ModelTransformBuffer,
641        gaussian_transform_buffer: &GaussianTransformBuffer,
642        selection_buffer: &SelectionBuffer,
643    ) -> Self {
644        log::debug!("Creating transform flags buffer");
645        let transform_flags_buffer = TransformFlagsBuffer::new(device);
646
647        log::debug!("Creating basic color modifiers buffer");
648        let basic_color_modifiers_buffer = BasicColorModifiersBuffer::new(device);
649
650        log::debug!("Creating rotation scale buffer");
651        let rot_scale_buffer = RotScaleBuffer::new(device);
652
653        log::debug!("Creating basic modifier bundle");
654        let modifier = BasicModifierBundle::new_with_selection(
655            device,
656            gaussians_buffer,
657            model_transform_buffer,
658            gaussian_transform_buffer,
659            &transform_flags_buffer,
660            &basic_color_modifiers_buffer,
661            &rot_scale_buffer,
662            selection_buffer,
663        );
664
665        log::debug!("Basic modifier created");
666
667        Self {
668            transform_flags_buffer,
669            basic_color_modifiers_buffer,
670            rot_scale_buffer,
671
672            modifier,
673        }
674    }
675}
676
677impl<G: GaussianPod, S> Modifier<G> for BasicModifier<G, S> {
678    fn apply(
679        &self,
680        device: &wgpu::Device,
681        encoder: &mut wgpu::CommandEncoder,
682        gaussians: &GaussiansBuffer<G>,
683        model_transform: &ModelTransformBuffer,
684        gaussian_transform: &GaussianTransformBuffer,
685    ) {
686        self.modifier.apply(
687            device,
688            encoder,
689            gaussians,
690            model_transform,
691            gaussian_transform,
692        );
693    }
694}