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