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}