Skip to main content

Editor

Struct Editor 

Source
pub struct Editor<G: GaussianPod> {
    pub model_transform_buffer: ModelTransformBuffer,
    pub gaussian_transform_buffer: GaussianTransformBuffer,
    pub gaussians_buffer: GaussiansBuffer<G>,
}
Expand description

An editor for Gaussians.

This enables the application of a sequence of Modifiers to the Gaussians by storing the necessary buffers, including:

If you wish to manage these buffers yourself, you do not need to use this struct.

Note that some GaussianPod configurations may not be able to be downloaded after modification, see documentations of core::GaussianShConfig and core::GaussianCov3dConfig for details.

Fields§

§model_transform_buffer: ModelTransformBuffer§gaussian_transform_buffer: GaussianTransformBuffer§gaussians_buffer: GaussiansBuffer<G>

Implementations§

Source§

impl<G: GaussianPod> Editor<G>

Source

pub fn new(device: &Device, gaussians: &impl IterGaussian) -> Self

Create a new editor.

Examples found in repository?
examples/modify.rs (line 112)
76async fn main() {
77    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
78
79    let args = Args::parse();
80    let model_path = &args.model;
81
82    log::debug!("Creating wgpu instance");
83    let instance =
84        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
85
86    log::debug!("Requesting adapter");
87    let adapter = instance
88        .request_adapter(&wgpu::RequestAdapterOptions::default())
89        .await
90        .expect("adapter");
91
92    log::debug!("Requesting device");
93    let (device, queue) = adapter
94        .request_device(&wgpu::DeviceDescriptor {
95            label: Some("Device"),
96            required_limits: adapter.limits(),
97            ..Default::default()
98        })
99        .await
100        .expect("device");
101
102    log::debug!("Creating gaussians");
103    let gaussians = [
104        gs::core::GaussiansSource::Ply,
105        gs::core::GaussiansSource::Spz,
106    ]
107    .into_iter()
108    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
109    .expect("gaussians");
110
111    log::debug!("Creating editor");
112    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
113
114    log::debug!("Creating basic modifier");
115    let basic_modifier = gs::BasicModifier::<GaussianPod>::new(
116        &device,
117        &editor.gaussians_buffer,
118        &editor.model_transform_buffer,
119        &editor.gaussian_transform_buffer,
120    );
121
122    log::debug!("Configuring modifiers");
123    basic_modifier.basic_color_modifiers_buffer.update(
124        &queue,
125        match args.override_rgb {
126            true => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_rgb_override,
127            false => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_hsv_modifiers,
128        }(Vec3::from_slice(&args.rgb_or_hsv)),
129        args.alpha,
130        args.contrast,
131        args.exposure,
132        args.gamma,
133    );
134
135    log::info!("Starting editing process");
136    let time = std::time::Instant::now();
137
138    log::debug!("Editing Gaussians");
139    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
140        label: Some("Edit Encoder"),
141    });
142
143    editor.apply(
144        &device,
145        &mut encoder,
146        [&basic_modifier as &dyn gs::Modifier<GaussianPod>],
147    );
148
149    queue.submit(Some(encoder.finish()));
150
151    device
152        .poll(wgpu::PollType::wait_indefinitely())
153        .expect("poll");
154
155    log::info!("Editing process completed in {:?}", time.elapsed());
156
157    log::debug!("Downloading Gaussians");
158    let modified_gaussians = editor
159        .gaussians_buffer
160        .download_gaussians(&device, &queue)
161        .await
162        .map(|gs| {
163            match &args.output[args.output.len().saturating_sub(4)..] {
164                ".ply" => {
165                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
166                }
167                ".spz" => {
168                    gs::core::Gaussians::Spz(
169                        gs::core::SpzGaussians::from_gaussians_with_options(
170                            gs,
171                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
172                                version: 2, // Version 2 is more widely supported as of now
173                                ..Default::default()
174                            },
175                        )
176                        .expect("SpzGaussians from gaussians"),
177                    )
178                }
179                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
180            }
181        })
182        .expect("gaussians download");
183
184    log::debug!("Writing modified Gaussians to output file");
185    modified_gaussians
186        .write_to_file(&args.output)
187        .expect("write modified Gaussians to output file");
188
189    log::info!("Modified Gaussians written to {}", args.output);
190}
More examples
Hide additional examples
examples/modify_selection.rs (line 182)
137async fn main() {
138    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
139
140    let args = Args::parse();
141    let model_path = &args.model;
142    let pos = Vec3::from_slice(&args.pos);
143    let rot = Quat::from_slice(&args.rot);
144    let scale = Vec3::from_slice(&args.scale);
145    let shape = match args.shape {
146        Shape::Sphere => gs::SelectionBundle::<GaussianPod>::create_sphere_bundle,
147        Shape::Box => gs::SelectionBundle::<GaussianPod>::create_box_bundle,
148    };
149    let repeat = args.repeat;
150    let offset = Vec3::from_slice(&args.offset);
151
152    log::debug!("Creating wgpu instance");
153    let instance =
154        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
155
156    log::debug!("Requesting adapter");
157    let adapter = instance
158        .request_adapter(&wgpu::RequestAdapterOptions::default())
159        .await
160        .expect("adapter");
161
162    log::debug!("Requesting device");
163    let (device, queue) = adapter
164        .request_device(&wgpu::DeviceDescriptor {
165            label: Some("Device"),
166            required_limits: adapter.limits(),
167            ..Default::default()
168        })
169        .await
170        .expect("device");
171
172    log::debug!("Creating gaussians");
173    let gaussians = [
174        gs::core::GaussiansSource::Ply,
175        gs::core::GaussiansSource::Spz,
176    ]
177    .into_iter()
178    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
179    .expect("gaussians");
180
181    log::debug!("Creating editor");
182    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
183
184    log::debug!("Creating shape selection compute bundle");
185    let shape_selection = shape(&device);
186
187    log::debug!("Creating basic selection modifier");
188    let mut basic_selection_modifier = gs::SelectionModifier::new_with_basic_modifier(
189        &device,
190        &editor.gaussians_buffer,
191        &editor.model_transform_buffer,
192        &editor.gaussian_transform_buffer,
193        vec![shape_selection],
194    );
195
196    log::debug!("Configuring modifiers");
197    basic_selection_modifier
198        .modifier
199        .basic_color_modifiers_buffer
200        .update(
201            &queue,
202            match args.override_rgb {
203                true => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_rgb_override,
204                false => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_hsv_modifiers,
205            }(Vec3::from_slice(&args.rgb_or_hsv)),
206            args.alpha,
207            args.contrast,
208            args.exposure,
209            args.gamma,
210        );
211
212    log::debug!("Creating shape selection buffers");
213    let shape_selection_buffers = (0..repeat)
214        .map(|i| {
215            let offset_pos = pos + offset * i as f32;
216            let buffer = gs::InvTransformBuffer::new(&device);
217            buffer.update_with_scale_rot_pos(&queue, scale, rot, offset_pos);
218            buffer
219        })
220        .collect::<Vec<_>>();
221
222    log::debug!("Creating shape selection bind groups");
223    let shape_selection_bind_groups = shape_selection_buffers
224        .iter()
225        .map(|buffer| {
226            basic_selection_modifier.selection.bundles[0]
227                .create_bind_group(
228                    &device,
229                    // index 0 is the Gaussians buffer, so we use 1,
230                    // see docs of create_sphere_bundle or create_box_bundle
231                    1,
232                    [buffer.buffer().as_entire_binding()],
233                )
234                .expect("bind group")
235        })
236        .collect::<Vec<_>>();
237
238    log::debug!("Creating selection expression");
239    basic_selection_modifier.selection_expr = shape_selection_bind_groups.into_iter().fold(
240        gs::SelectionExpr::Identity,
241        |acc, bind_group| {
242            acc.union(gs::SelectionExpr::selection(
243                0, // the 0 here is the bundle index in the selection bundle
244                vec![bind_group],
245            ))
246        },
247    );
248
249    log::info!("Starting editing process");
250    let time = std::time::Instant::now();
251
252    log::debug!("Editing Gaussians");
253    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
254        label: Some("Edit Encoder"),
255    });
256
257    editor.apply(
258        &device,
259        &mut encoder,
260        [&basic_selection_modifier as &dyn gs::Modifier<GaussianPod>],
261    );
262
263    queue.submit(Some(encoder.finish()));
264
265    device
266        .poll(wgpu::PollType::wait_indefinitely())
267        .expect("poll");
268
269    log::info!("Editing process completed in {:?}", time.elapsed());
270
271    log::debug!("Downloading Gaussians");
272    let modified_gaussians = editor
273        .gaussians_buffer
274        .download_gaussians(&device, &queue)
275        .await
276        .map(|gs| {
277            match &args.output[args.output.len().saturating_sub(4)..] {
278                ".ply" => {
279                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
280                }
281                ".spz" => {
282                    gs::core::Gaussians::Spz(
283                        gs::core::SpzGaussians::from_gaussians_with_options(
284                            gs,
285                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
286                                version: 2, // Version 2 is more widely supported as of now
287                                ..Default::default()
288                            },
289                        )
290                        .expect("SpzGaussians from gaussians"),
291                    )
292                }
293                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
294            }
295        })
296        .expect("gaussians download");
297
298    log::debug!("Writing modified Gaussians to output file");
299    modified_gaussians
300        .write_to_file(&args.output)
301        .expect("write modified Gaussians to output file");
302
303    log::info!("Modified Gaussians written to {}", args.output);
304}
examples/custom_modify_selection.rs (line 106)
68async fn main() {
69    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
70
71    let args = Args::parse();
72    let model_path = &args.model;
73    let pos = Vec3::from_slice(&args.pos);
74    let radius = args.radius;
75
76    log::debug!("Creating wgpu instance");
77    let instance =
78        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
79
80    log::debug!("Requesting adapter");
81    let adapter = instance
82        .request_adapter(&wgpu::RequestAdapterOptions::default())
83        .await
84        .expect("adapter");
85
86    log::debug!("Requesting device");
87    let (device, queue) = adapter
88        .request_device(&wgpu::DeviceDescriptor {
89            label: Some("Device"),
90            required_limits: adapter.limits(),
91            ..Default::default()
92        })
93        .await
94        .expect("device");
95
96    log::debug!("Creating gaussians");
97    let gaussians = [
98        gs::core::GaussiansSource::Ply,
99        gs::core::GaussiansSource::Spz,
100    ]
101    .into_iter()
102    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
103    .expect("gaussians");
104
105    log::debug!("Creating editor");
106    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
107
108    log::debug!("Creating buffers");
109    let pos_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
110        label: Some("Position Buffer"),
111        contents: bytemuck::bytes_of(&pos),
112        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
113    });
114
115    let radius_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
116        label: Some("Radius Buffer"),
117        contents: bytemuck::bytes_of(&radius),
118        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
119    });
120
121    const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor =
122        wgpu::BindGroupLayoutDescriptor {
123            label: Some("Bind Group Layout"),
124            entries: &[
125                // Position uniform buffer
126                wgpu::BindGroupLayoutEntry {
127                    binding: 0,
128                    visibility: wgpu::ShaderStages::COMPUTE,
129                    ty: wgpu::BindingType::Buffer {
130                        ty: wgpu::BufferBindingType::Uniform,
131                        has_dynamic_offset: false,
132                        min_binding_size: None,
133                    },
134                    count: None,
135                },
136                // Radius uniform buffer
137                wgpu::BindGroupLayoutEntry {
138                    binding: 1,
139                    visibility: wgpu::ShaderStages::COMPUTE,
140                    ty: wgpu::BindingType::Buffer {
141                        ty: wgpu::BufferBindingType::Uniform,
142                        has_dynamic_offset: false,
143                        min_binding_size: None,
144                    },
145                    count: None,
146                },
147                // Selection buffer (only in modifier pipeline)
148                wgpu::BindGroupLayoutEntry {
149                    binding: 2,
150                    visibility: wgpu::ShaderStages::COMPUTE,
151                    ty: wgpu::BindingType::Buffer {
152                        ty: wgpu::BufferBindingType::Storage { read_only: true },
153                        has_dynamic_offset: false,
154                        min_binding_size: None,
155                    },
156                    count: None,
157                },
158            ],
159        };
160
161    log::debug!("Creating cylinder selection compute bundle");
162    let cylinder_selection_bundle = gs::core::ComputeBundleBuilder::new()
163        .label("Selection")
164        .bind_group_layouts([
165            &gs::SelectionBundle::<GaussianPod>::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
166            &wgpu::BindGroupLayoutDescriptor {
167                entries: &BIND_GROUP_LAYOUT_DESCRIPTOR.entries[..2],
168                ..BIND_GROUP_LAYOUT_DESCRIPTOR
169            },
170        ])
171        .main_shader("package::selection".parse().unwrap())
172        .entry_point("main")
173        .wesl_compile_options(wesl::CompileOptions {
174            features: GaussianPod::wesl_features(),
175            ..Default::default()
176        })
177        .resolver({
178            let mut resolver =
179                wesl::StandardResolver::new("examples/shader/custom_modify_selection");
180            resolver.add_package(&gs::core::shader::PACKAGE);
181            resolver
182        })
183        .build_without_bind_groups(&device)
184        .map_err(|e| log::error!("{e}"))
185        .expect("selection bundle");
186
187    log::debug!("Creating custom modifier");
188    #[allow(dead_code)]
189    let modifier_factory = |selection_buffer: &gs::SelectionBuffer| /* -> impl gs::Modifier<GaussianPod> */ {
190            log::debug!("Creating custom modifier compute bundle");
191            let modifier_bundle = gs::core::ComputeBundleBuilder::new()
192                .label("Modifier")
193                .bind_group_layouts([
194                    &gs::MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
195                    &BIND_GROUP_LAYOUT_DESCRIPTOR,
196                ])
197                .resolver({
198                    let mut resolver =
199                        wesl::StandardResolver::new("examples/shader/custom_modify_selection");
200                    resolver.add_package(&gs::core::shader::PACKAGE);
201                    resolver.add_package(&gs::shader::PACKAGE);
202                    resolver
203                })
204                .main_shader("package::modifier".parse().unwrap())
205                .entry_point("main")
206                .wesl_compile_options(wesl::CompileOptions {
207                    features: GaussianPod::wesl_features(),
208                    ..Default::default()
209                })
210                .build(
211                    &device,
212                    [
213                        vec![
214                            editor.gaussians_buffer.buffer().as_entire_binding(),
215                            editor.model_transform_buffer.buffer().as_entire_binding(),
216                            editor
217                                .gaussian_transform_buffer
218                                .buffer()
219                                .as_entire_binding(),
220                        ],
221                        vec![
222                            pos_buffer.as_entire_binding(),
223                            radius_buffer.as_entire_binding(),
224                            selection_buffer.buffer().as_entire_binding(),
225                        ],
226                    ],
227                )
228                .map_err(|e| log::error!("{e}"))
229                .expect("modifier bundle");
230
231            // This is a modifier closure because this function signature has blanket impl of the modifier trait
232            move |_device: &wgpu::Device,
233                  encoder: &mut wgpu::CommandEncoder,
234                  gaussians: &gs::core::GaussiansBuffer<GaussianPod>,
235                  _model_transform: &gs::core::ModelTransformBuffer,
236                  _gaussian_transform: &gs::core::GaussianTransformBuffer| {
237                  modifier_bundle.dispatch(encoder, gaussians.len() as u32);
238            }
239        };
240
241    #[allow(dead_code)]
242    struct Modifier<G: gs::core::GaussianPod>(gs::core::ComputeBundle, std::marker::PhantomData<G>);
243
244    impl<G: gs::core::GaussianPod> Modifier<G> {
245        #[allow(dead_code)]
246        fn new(
247            device: &wgpu::Device,
248            editor: &gs::Editor<G>,
249            pos_buffer: &wgpu::Buffer,
250            radius_buffer: &wgpu::Buffer,
251            selection_buffer: &gs::SelectionBuffer,
252        ) -> Self {
253            log::debug!("Creating custom modifier compute bundle");
254            let modifier_bundle = gs::core::ComputeBundleBuilder::new()
255                .label("Modifier")
256                .bind_group_layouts([
257                    &gs::MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
258                    &BIND_GROUP_LAYOUT_DESCRIPTOR,
259                ])
260                .resolver({
261                    let mut resolver =
262                        wesl::StandardResolver::new("examples/shader/custom_modify_selection");
263                    resolver.add_package(&gs::core::shader::PACKAGE);
264                    resolver.add_package(&gs::shader::PACKAGE);
265                    resolver
266                })
267                .main_shader("package::modifier".parse().unwrap())
268                .entry_point("main")
269                .wesl_compile_options(wesl::CompileOptions {
270                    features: GaussianPod::wesl_features(),
271                    ..Default::default()
272                })
273                .build(
274                    device,
275                    [
276                        vec![
277                            editor.gaussians_buffer.buffer().as_entire_binding(),
278                            editor.model_transform_buffer.buffer().as_entire_binding(),
279                            editor
280                                .gaussian_transform_buffer
281                                .buffer()
282                                .as_entire_binding(),
283                        ],
284                        vec![
285                            pos_buffer.as_entire_binding(),
286                            radius_buffer.as_entire_binding(),
287                            selection_buffer.buffer().as_entire_binding(),
288                        ],
289                    ],
290                )
291                .map_err(|e| log::error!("{e}"))
292                .expect("modifier bundle");
293
294            Self(modifier_bundle, std::marker::PhantomData)
295        }
296    }
297
298    impl<G: gs::core::GaussianPod> gs::Modifier<G> for Modifier<G> {
299        fn apply(
300            &self,
301            _device: &wgpu::Device,
302            encoder: &mut wgpu::CommandEncoder,
303            gaussians: &gs::core::GaussiansBuffer<G>,
304            _model_transform: &gs::core::ModelTransformBuffer,
305            _gaussian_transform: &gs::core::GaussianTransformBuffer,
306        ) {
307            self.0.dispatch(encoder, gaussians.len() as u32);
308        }
309    }
310
311    log::debug!("Creating selection modifier");
312    let mut selection_modifier = gs::SelectionModifier::<GaussianPod, _>::new(
313        &device,
314        &editor.gaussians_buffer,
315        vec![cylinder_selection_bundle],
316        modifier_factory,
317        // Uncomment the following line to use modifier struct instead of closure
318        // |selection_buffer| {
319        //     Modifier::new(
320        //         &device,
321        //         &editor,
322        //         &pos_buffer,
323        //         &radius_buffer,
324        //         selection_buffer,
325        //     )
326        // },
327    );
328
329    log::debug!("Creating selection expression");
330    selection_modifier.selection_expr = gs::SelectionExpr::selection(
331        0,
332        vec![
333            selection_modifier.selection.bundles[0]
334                .create_bind_group(
335                    &device,
336                    1, // index 0 is the Gaussians buffer, so we use 1,
337                    [
338                        pos_buffer.as_entire_binding(),
339                        radius_buffer.as_entire_binding(),
340                    ],
341                )
342                .expect("selection expr bind group"),
343        ],
344    );
345
346    log::info!("Starting editing process");
347    let time = std::time::Instant::now();
348
349    log::debug!("Editing Gaussians");
350    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
351        label: Some("Edit Encoder"),
352    });
353
354    editor.apply(
355        &device,
356        &mut encoder,
357        [&selection_modifier as &dyn gs::Modifier<GaussianPod>],
358    );
359
360    queue.submit(Some(encoder.finish()));
361
362    device
363        .poll(wgpu::PollType::wait_indefinitely())
364        .expect("poll");
365
366    log::info!("Editing process completed in {:?}", time.elapsed());
367
368    log::debug!("Downloading Gaussians");
369    let modified_gaussians = editor
370        .gaussians_buffer
371        .download_gaussians(&device, &queue)
372        .await
373        .map(|gs| {
374            match &args.output[args.output.len().saturating_sub(4)..] {
375                ".ply" => {
376                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
377                }
378                ".spz" => {
379                    gs::core::Gaussians::Spz(
380                        gs::core::SpzGaussians::from_gaussians_with_options(
381                            gs,
382                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
383                                version: 2, // Version 2 is more widely supported as of now
384                                ..Default::default()
385                            },
386                        )
387                        .expect("SpzGaussians from gaussians"),
388                    )
389                }
390                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
391            }
392        })
393        .expect("gaussians download");
394
395    log::debug!("Writing modified Gaussians to output file");
396    modified_gaussians
397        .write_to_file(&args.output)
398        .expect("write modified Gaussians to output file");
399
400    log::info!("Modified Gaussians written to {}", args.output);
401}
Source

pub fn apply<'a>( &self, device: &Device, encoder: &mut CommandEncoder, modifiers: impl IntoIterator<Item = &'a dyn Modifier<G>>, )

Apply the modifiers to the Gaussians.

Examples found in repository?
examples/modify.rs (lines 143-147)
76async fn main() {
77    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
78
79    let args = Args::parse();
80    let model_path = &args.model;
81
82    log::debug!("Creating wgpu instance");
83    let instance =
84        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
85
86    log::debug!("Requesting adapter");
87    let adapter = instance
88        .request_adapter(&wgpu::RequestAdapterOptions::default())
89        .await
90        .expect("adapter");
91
92    log::debug!("Requesting device");
93    let (device, queue) = adapter
94        .request_device(&wgpu::DeviceDescriptor {
95            label: Some("Device"),
96            required_limits: adapter.limits(),
97            ..Default::default()
98        })
99        .await
100        .expect("device");
101
102    log::debug!("Creating gaussians");
103    let gaussians = [
104        gs::core::GaussiansSource::Ply,
105        gs::core::GaussiansSource::Spz,
106    ]
107    .into_iter()
108    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
109    .expect("gaussians");
110
111    log::debug!("Creating editor");
112    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
113
114    log::debug!("Creating basic modifier");
115    let basic_modifier = gs::BasicModifier::<GaussianPod>::new(
116        &device,
117        &editor.gaussians_buffer,
118        &editor.model_transform_buffer,
119        &editor.gaussian_transform_buffer,
120    );
121
122    log::debug!("Configuring modifiers");
123    basic_modifier.basic_color_modifiers_buffer.update(
124        &queue,
125        match args.override_rgb {
126            true => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_rgb_override,
127            false => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_hsv_modifiers,
128        }(Vec3::from_slice(&args.rgb_or_hsv)),
129        args.alpha,
130        args.contrast,
131        args.exposure,
132        args.gamma,
133    );
134
135    log::info!("Starting editing process");
136    let time = std::time::Instant::now();
137
138    log::debug!("Editing Gaussians");
139    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
140        label: Some("Edit Encoder"),
141    });
142
143    editor.apply(
144        &device,
145        &mut encoder,
146        [&basic_modifier as &dyn gs::Modifier<GaussianPod>],
147    );
148
149    queue.submit(Some(encoder.finish()));
150
151    device
152        .poll(wgpu::PollType::wait_indefinitely())
153        .expect("poll");
154
155    log::info!("Editing process completed in {:?}", time.elapsed());
156
157    log::debug!("Downloading Gaussians");
158    let modified_gaussians = editor
159        .gaussians_buffer
160        .download_gaussians(&device, &queue)
161        .await
162        .map(|gs| {
163            match &args.output[args.output.len().saturating_sub(4)..] {
164                ".ply" => {
165                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
166                }
167                ".spz" => {
168                    gs::core::Gaussians::Spz(
169                        gs::core::SpzGaussians::from_gaussians_with_options(
170                            gs,
171                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
172                                version: 2, // Version 2 is more widely supported as of now
173                                ..Default::default()
174                            },
175                        )
176                        .expect("SpzGaussians from gaussians"),
177                    )
178                }
179                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
180            }
181        })
182        .expect("gaussians download");
183
184    log::debug!("Writing modified Gaussians to output file");
185    modified_gaussians
186        .write_to_file(&args.output)
187        .expect("write modified Gaussians to output file");
188
189    log::info!("Modified Gaussians written to {}", args.output);
190}
More examples
Hide additional examples
examples/modify_selection.rs (lines 257-261)
137async fn main() {
138    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
139
140    let args = Args::parse();
141    let model_path = &args.model;
142    let pos = Vec3::from_slice(&args.pos);
143    let rot = Quat::from_slice(&args.rot);
144    let scale = Vec3::from_slice(&args.scale);
145    let shape = match args.shape {
146        Shape::Sphere => gs::SelectionBundle::<GaussianPod>::create_sphere_bundle,
147        Shape::Box => gs::SelectionBundle::<GaussianPod>::create_box_bundle,
148    };
149    let repeat = args.repeat;
150    let offset = Vec3::from_slice(&args.offset);
151
152    log::debug!("Creating wgpu instance");
153    let instance =
154        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
155
156    log::debug!("Requesting adapter");
157    let adapter = instance
158        .request_adapter(&wgpu::RequestAdapterOptions::default())
159        .await
160        .expect("adapter");
161
162    log::debug!("Requesting device");
163    let (device, queue) = adapter
164        .request_device(&wgpu::DeviceDescriptor {
165            label: Some("Device"),
166            required_limits: adapter.limits(),
167            ..Default::default()
168        })
169        .await
170        .expect("device");
171
172    log::debug!("Creating gaussians");
173    let gaussians = [
174        gs::core::GaussiansSource::Ply,
175        gs::core::GaussiansSource::Spz,
176    ]
177    .into_iter()
178    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
179    .expect("gaussians");
180
181    log::debug!("Creating editor");
182    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
183
184    log::debug!("Creating shape selection compute bundle");
185    let shape_selection = shape(&device);
186
187    log::debug!("Creating basic selection modifier");
188    let mut basic_selection_modifier = gs::SelectionModifier::new_with_basic_modifier(
189        &device,
190        &editor.gaussians_buffer,
191        &editor.model_transform_buffer,
192        &editor.gaussian_transform_buffer,
193        vec![shape_selection],
194    );
195
196    log::debug!("Configuring modifiers");
197    basic_selection_modifier
198        .modifier
199        .basic_color_modifiers_buffer
200        .update(
201            &queue,
202            match args.override_rgb {
203                true => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_rgb_override,
204                false => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_hsv_modifiers,
205            }(Vec3::from_slice(&args.rgb_or_hsv)),
206            args.alpha,
207            args.contrast,
208            args.exposure,
209            args.gamma,
210        );
211
212    log::debug!("Creating shape selection buffers");
213    let shape_selection_buffers = (0..repeat)
214        .map(|i| {
215            let offset_pos = pos + offset * i as f32;
216            let buffer = gs::InvTransformBuffer::new(&device);
217            buffer.update_with_scale_rot_pos(&queue, scale, rot, offset_pos);
218            buffer
219        })
220        .collect::<Vec<_>>();
221
222    log::debug!("Creating shape selection bind groups");
223    let shape_selection_bind_groups = shape_selection_buffers
224        .iter()
225        .map(|buffer| {
226            basic_selection_modifier.selection.bundles[0]
227                .create_bind_group(
228                    &device,
229                    // index 0 is the Gaussians buffer, so we use 1,
230                    // see docs of create_sphere_bundle or create_box_bundle
231                    1,
232                    [buffer.buffer().as_entire_binding()],
233                )
234                .expect("bind group")
235        })
236        .collect::<Vec<_>>();
237
238    log::debug!("Creating selection expression");
239    basic_selection_modifier.selection_expr = shape_selection_bind_groups.into_iter().fold(
240        gs::SelectionExpr::Identity,
241        |acc, bind_group| {
242            acc.union(gs::SelectionExpr::selection(
243                0, // the 0 here is the bundle index in the selection bundle
244                vec![bind_group],
245            ))
246        },
247    );
248
249    log::info!("Starting editing process");
250    let time = std::time::Instant::now();
251
252    log::debug!("Editing Gaussians");
253    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
254        label: Some("Edit Encoder"),
255    });
256
257    editor.apply(
258        &device,
259        &mut encoder,
260        [&basic_selection_modifier as &dyn gs::Modifier<GaussianPod>],
261    );
262
263    queue.submit(Some(encoder.finish()));
264
265    device
266        .poll(wgpu::PollType::wait_indefinitely())
267        .expect("poll");
268
269    log::info!("Editing process completed in {:?}", time.elapsed());
270
271    log::debug!("Downloading Gaussians");
272    let modified_gaussians = editor
273        .gaussians_buffer
274        .download_gaussians(&device, &queue)
275        .await
276        .map(|gs| {
277            match &args.output[args.output.len().saturating_sub(4)..] {
278                ".ply" => {
279                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
280                }
281                ".spz" => {
282                    gs::core::Gaussians::Spz(
283                        gs::core::SpzGaussians::from_gaussians_with_options(
284                            gs,
285                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
286                                version: 2, // Version 2 is more widely supported as of now
287                                ..Default::default()
288                            },
289                        )
290                        .expect("SpzGaussians from gaussians"),
291                    )
292                }
293                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
294            }
295        })
296        .expect("gaussians download");
297
298    log::debug!("Writing modified Gaussians to output file");
299    modified_gaussians
300        .write_to_file(&args.output)
301        .expect("write modified Gaussians to output file");
302
303    log::info!("Modified Gaussians written to {}", args.output);
304}
examples/custom_modify_selection.rs (lines 354-358)
68async fn main() {
69    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
70
71    let args = Args::parse();
72    let model_path = &args.model;
73    let pos = Vec3::from_slice(&args.pos);
74    let radius = args.radius;
75
76    log::debug!("Creating wgpu instance");
77    let instance =
78        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
79
80    log::debug!("Requesting adapter");
81    let adapter = instance
82        .request_adapter(&wgpu::RequestAdapterOptions::default())
83        .await
84        .expect("adapter");
85
86    log::debug!("Requesting device");
87    let (device, queue) = adapter
88        .request_device(&wgpu::DeviceDescriptor {
89            label: Some("Device"),
90            required_limits: adapter.limits(),
91            ..Default::default()
92        })
93        .await
94        .expect("device");
95
96    log::debug!("Creating gaussians");
97    let gaussians = [
98        gs::core::GaussiansSource::Ply,
99        gs::core::GaussiansSource::Spz,
100    ]
101    .into_iter()
102    .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
103    .expect("gaussians");
104
105    log::debug!("Creating editor");
106    let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
107
108    log::debug!("Creating buffers");
109    let pos_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
110        label: Some("Position Buffer"),
111        contents: bytemuck::bytes_of(&pos),
112        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
113    });
114
115    let radius_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
116        label: Some("Radius Buffer"),
117        contents: bytemuck::bytes_of(&radius),
118        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
119    });
120
121    const BIND_GROUP_LAYOUT_DESCRIPTOR: wgpu::BindGroupLayoutDescriptor =
122        wgpu::BindGroupLayoutDescriptor {
123            label: Some("Bind Group Layout"),
124            entries: &[
125                // Position uniform buffer
126                wgpu::BindGroupLayoutEntry {
127                    binding: 0,
128                    visibility: wgpu::ShaderStages::COMPUTE,
129                    ty: wgpu::BindingType::Buffer {
130                        ty: wgpu::BufferBindingType::Uniform,
131                        has_dynamic_offset: false,
132                        min_binding_size: None,
133                    },
134                    count: None,
135                },
136                // Radius uniform buffer
137                wgpu::BindGroupLayoutEntry {
138                    binding: 1,
139                    visibility: wgpu::ShaderStages::COMPUTE,
140                    ty: wgpu::BindingType::Buffer {
141                        ty: wgpu::BufferBindingType::Uniform,
142                        has_dynamic_offset: false,
143                        min_binding_size: None,
144                    },
145                    count: None,
146                },
147                // Selection buffer (only in modifier pipeline)
148                wgpu::BindGroupLayoutEntry {
149                    binding: 2,
150                    visibility: wgpu::ShaderStages::COMPUTE,
151                    ty: wgpu::BindingType::Buffer {
152                        ty: wgpu::BufferBindingType::Storage { read_only: true },
153                        has_dynamic_offset: false,
154                        min_binding_size: None,
155                    },
156                    count: None,
157                },
158            ],
159        };
160
161    log::debug!("Creating cylinder selection compute bundle");
162    let cylinder_selection_bundle = gs::core::ComputeBundleBuilder::new()
163        .label("Selection")
164        .bind_group_layouts([
165            &gs::SelectionBundle::<GaussianPod>::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
166            &wgpu::BindGroupLayoutDescriptor {
167                entries: &BIND_GROUP_LAYOUT_DESCRIPTOR.entries[..2],
168                ..BIND_GROUP_LAYOUT_DESCRIPTOR
169            },
170        ])
171        .main_shader("package::selection".parse().unwrap())
172        .entry_point("main")
173        .wesl_compile_options(wesl::CompileOptions {
174            features: GaussianPod::wesl_features(),
175            ..Default::default()
176        })
177        .resolver({
178            let mut resolver =
179                wesl::StandardResolver::new("examples/shader/custom_modify_selection");
180            resolver.add_package(&gs::core::shader::PACKAGE);
181            resolver
182        })
183        .build_without_bind_groups(&device)
184        .map_err(|e| log::error!("{e}"))
185        .expect("selection bundle");
186
187    log::debug!("Creating custom modifier");
188    #[allow(dead_code)]
189    let modifier_factory = |selection_buffer: &gs::SelectionBuffer| /* -> impl gs::Modifier<GaussianPod> */ {
190            log::debug!("Creating custom modifier compute bundle");
191            let modifier_bundle = gs::core::ComputeBundleBuilder::new()
192                .label("Modifier")
193                .bind_group_layouts([
194                    &gs::MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
195                    &BIND_GROUP_LAYOUT_DESCRIPTOR,
196                ])
197                .resolver({
198                    let mut resolver =
199                        wesl::StandardResolver::new("examples/shader/custom_modify_selection");
200                    resolver.add_package(&gs::core::shader::PACKAGE);
201                    resolver.add_package(&gs::shader::PACKAGE);
202                    resolver
203                })
204                .main_shader("package::modifier".parse().unwrap())
205                .entry_point("main")
206                .wesl_compile_options(wesl::CompileOptions {
207                    features: GaussianPod::wesl_features(),
208                    ..Default::default()
209                })
210                .build(
211                    &device,
212                    [
213                        vec![
214                            editor.gaussians_buffer.buffer().as_entire_binding(),
215                            editor.model_transform_buffer.buffer().as_entire_binding(),
216                            editor
217                                .gaussian_transform_buffer
218                                .buffer()
219                                .as_entire_binding(),
220                        ],
221                        vec![
222                            pos_buffer.as_entire_binding(),
223                            radius_buffer.as_entire_binding(),
224                            selection_buffer.buffer().as_entire_binding(),
225                        ],
226                    ],
227                )
228                .map_err(|e| log::error!("{e}"))
229                .expect("modifier bundle");
230
231            // This is a modifier closure because this function signature has blanket impl of the modifier trait
232            move |_device: &wgpu::Device,
233                  encoder: &mut wgpu::CommandEncoder,
234                  gaussians: &gs::core::GaussiansBuffer<GaussianPod>,
235                  _model_transform: &gs::core::ModelTransformBuffer,
236                  _gaussian_transform: &gs::core::GaussianTransformBuffer| {
237                  modifier_bundle.dispatch(encoder, gaussians.len() as u32);
238            }
239        };
240
241    #[allow(dead_code)]
242    struct Modifier<G: gs::core::GaussianPod>(gs::core::ComputeBundle, std::marker::PhantomData<G>);
243
244    impl<G: gs::core::GaussianPod> Modifier<G> {
245        #[allow(dead_code)]
246        fn new(
247            device: &wgpu::Device,
248            editor: &gs::Editor<G>,
249            pos_buffer: &wgpu::Buffer,
250            radius_buffer: &wgpu::Buffer,
251            selection_buffer: &gs::SelectionBuffer,
252        ) -> Self {
253            log::debug!("Creating custom modifier compute bundle");
254            let modifier_bundle = gs::core::ComputeBundleBuilder::new()
255                .label("Modifier")
256                .bind_group_layouts([
257                    &gs::MODIFIER_GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
258                    &BIND_GROUP_LAYOUT_DESCRIPTOR,
259                ])
260                .resolver({
261                    let mut resolver =
262                        wesl::StandardResolver::new("examples/shader/custom_modify_selection");
263                    resolver.add_package(&gs::core::shader::PACKAGE);
264                    resolver.add_package(&gs::shader::PACKAGE);
265                    resolver
266                })
267                .main_shader("package::modifier".parse().unwrap())
268                .entry_point("main")
269                .wesl_compile_options(wesl::CompileOptions {
270                    features: GaussianPod::wesl_features(),
271                    ..Default::default()
272                })
273                .build(
274                    device,
275                    [
276                        vec![
277                            editor.gaussians_buffer.buffer().as_entire_binding(),
278                            editor.model_transform_buffer.buffer().as_entire_binding(),
279                            editor
280                                .gaussian_transform_buffer
281                                .buffer()
282                                .as_entire_binding(),
283                        ],
284                        vec![
285                            pos_buffer.as_entire_binding(),
286                            radius_buffer.as_entire_binding(),
287                            selection_buffer.buffer().as_entire_binding(),
288                        ],
289                    ],
290                )
291                .map_err(|e| log::error!("{e}"))
292                .expect("modifier bundle");
293
294            Self(modifier_bundle, std::marker::PhantomData)
295        }
296    }
297
298    impl<G: gs::core::GaussianPod> gs::Modifier<G> for Modifier<G> {
299        fn apply(
300            &self,
301            _device: &wgpu::Device,
302            encoder: &mut wgpu::CommandEncoder,
303            gaussians: &gs::core::GaussiansBuffer<G>,
304            _model_transform: &gs::core::ModelTransformBuffer,
305            _gaussian_transform: &gs::core::GaussianTransformBuffer,
306        ) {
307            self.0.dispatch(encoder, gaussians.len() as u32);
308        }
309    }
310
311    log::debug!("Creating selection modifier");
312    let mut selection_modifier = gs::SelectionModifier::<GaussianPod, _>::new(
313        &device,
314        &editor.gaussians_buffer,
315        vec![cylinder_selection_bundle],
316        modifier_factory,
317        // Uncomment the following line to use modifier struct instead of closure
318        // |selection_buffer| {
319        //     Modifier::new(
320        //         &device,
321        //         &editor,
322        //         &pos_buffer,
323        //         &radius_buffer,
324        //         selection_buffer,
325        //     )
326        // },
327    );
328
329    log::debug!("Creating selection expression");
330    selection_modifier.selection_expr = gs::SelectionExpr::selection(
331        0,
332        vec![
333            selection_modifier.selection.bundles[0]
334                .create_bind_group(
335                    &device,
336                    1, // index 0 is the Gaussians buffer, so we use 1,
337                    [
338                        pos_buffer.as_entire_binding(),
339                        radius_buffer.as_entire_binding(),
340                    ],
341                )
342                .expect("selection expr bind group"),
343        ],
344    );
345
346    log::info!("Starting editing process");
347    let time = std::time::Instant::now();
348
349    log::debug!("Editing Gaussians");
350    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
351        label: Some("Edit Encoder"),
352    });
353
354    editor.apply(
355        &device,
356        &mut encoder,
357        [&selection_modifier as &dyn gs::Modifier<GaussianPod>],
358    );
359
360    queue.submit(Some(encoder.finish()));
361
362    device
363        .poll(wgpu::PollType::wait_indefinitely())
364        .expect("poll");
365
366    log::info!("Editing process completed in {:?}", time.elapsed());
367
368    log::debug!("Downloading Gaussians");
369    let modified_gaussians = editor
370        .gaussians_buffer
371        .download_gaussians(&device, &queue)
372        .await
373        .map(|gs| {
374            match &args.output[args.output.len().saturating_sub(4)..] {
375                ".ply" => {
376                    gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
377                }
378                ".spz" => {
379                    gs::core::Gaussians::Spz(
380                        gs::core::SpzGaussians::from_gaussians_with_options(
381                            gs,
382                            &gs::core::SpzGaussiansFromGaussianSliceOptions {
383                                version: 2, // Version 2 is more widely supported as of now
384                                ..Default::default()
385                            },
386                        )
387                        .expect("SpzGaussians from gaussians"),
388                    )
389                }
390                _ => panic!("Unsupported output file extension, expected .ply or .spz"),
391            }
392        })
393        .expect("gaussians download");
394
395    log::debug!("Writing modified Gaussians to output file");
396    modified_gaussians
397        .write_to_file(&args.output)
398        .expect("write modified Gaussians to output file");
399
400    log::info!("Modified Gaussians written to {}", args.output);
401}

Auto Trait Implementations§

§

impl<G> Freeze for Editor<G>

§

impl<G> !RefUnwindSafe for Editor<G>

§

impl<G> Send for Editor<G>

§

impl<G> Sync for Editor<G>

§

impl<G> Unpin for Editor<G>
where G: Unpin,

§

impl<G> UnsafeUnpin for Editor<G>

§

impl<G> !UnwindSafe for Editor<G>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,