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}