1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use pasture_core::containers::{BorrowedBuffer, BorrowedBufferExt, OwningBuffer, VectorBuffer};
use pasture_core::layout::attributes::{COLOR_RGB, POSITION_3D};
use pasture_core::layout::conversion::BufferLayoutConverter;
use pasture_core::layout::{PointAttributeDataType, PointType};
use pasture_core::nalgebra::Vector3;
use pasture_derive::PointType;
use rand::prelude::Distribution;
use rand::{thread_rng, Rng};
#[derive(Copy, Clone, PointType, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
#[repr(C, packed)]
struct SourcePointType {
#[pasture(BUILTIN_POSITION_3D)]
pub position: Vector3<i32>,
#[pasture(BUILTIN_CLASSIFICATION)]
pub classification: u8,
#[pasture(BUILTIN_COLOR_RGB)]
pub color: Vector3<u16>,
}
struct PointDistribution;
impl Distribution<SourcePointType> for PointDistribution {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SourcePointType {
SourcePointType {
position: Vector3::new(rng.gen(), rng.gen(), rng.gen()),
classification: rng.gen(),
color: Vector3::new(rng.gen(), rng.gen(), rng.gen()),
}
}
}
fn gen_random_source_points(count: usize) -> impl Iterator<Item = SourcePointType> {
let rng = thread_rng();
rng.sample_iter::<SourcePointType, _>(PointDistribution)
.take(count)
}
#[derive(Copy, Clone, PointType, bytemuck::AnyBitPattern, bytemuck::NoUninit, Debug)]
#[repr(C, packed)]
struct TargetPointType {
#[pasture(BUILTIN_COLOR_RGB)]
pub color: Vector3<u8>,
#[pasture(BUILTIN_POSITION_3D)]
pub position: Vector3<f64>,
}
fn main() {
// In this example, we will look at how we can convert buffers from one `PointLayout` into another `PointLayout` using
// the `BufferLayoutConverter` type
// First, let's generate some points in a source `PointLayout` (in this case the `SourcePointType`):
let source_points = gen_random_source_points(64).collect::<Vec<_>>();
let source_points_buffer = source_points.iter().copied().collect::<VectorBuffer>();
// To make it a bit more explicit, here is the `PointLayout` of the source points:
let source_layout = source_points_buffer.point_layout();
// And the layout of the target points, which in this case will be the `TargetPointType`
let target_layout = TargetPointType::layout();
// Now we can create a `BufferLayoutConverter` for the two types that will convert points from the `source_layout` to
// the `target_layout`. First, let's try to use default conversions, so `pasture` will figure out how to map attributes
// in `SourcePointType` to attributes in `TargetPointType`
{
let default_converter = BufferLayoutConverter::for_layouts(source_layout, &target_layout);
// Conversion is as simple as calling `convert` on the `BufferLayoutConverter`. We do have to tell it what kind of
// buffer we want for our output data. Here, we request a `VectorBuffer`:
let converted_points = default_converter.convert::<VectorBuffer, _>(&source_points_buffer);
// Here are some guarantees that should hold:
assert_eq!(converted_points.len(), source_points_buffer.len());
assert_eq!(converted_points.point_layout(), &target_layout);
// If you already have a buffer that you want to convert *into*, this also works:
let mut into_buffer =
VectorBuffer::with_capacity(source_points_buffer.len(), target_layout.clone());
// `convert_into` expects that the target buffer has a length at least as big as the source buffer, so
// we have to resize first (which fills `into_buffer` with default values):
into_buffer.resize(source_points_buffer.len());
default_converter.convert_into(&source_points_buffer, &mut into_buffer);
assert_eq!(converted_points, into_buffer);
// As an explanation of what happened: The `BufferLayoutConverter` essentially performed the same operation as the
// following piece of code, but using runtime `PointLayout` information and the pasture buffer API:
let _equivalent_conversion_with_vec = source_points
.iter()
.map(|source_point| {
let source_color = source_point.color;
let source_position = source_point.position;
TargetPointType {
color: Vector3::new(
source_color.x as u8,
source_color.y as u8,
source_color.z as u8,
),
position: Vector3::new(
source_position.x as f64,
source_position.y as f64,
source_position.z as f64,
),
}
})
.collect::<Vec<_>>();
}
// Sometimes we want more control over how the attributes are converted. For this, we can set custom mappings
// on the `BufferLayoutConverter`. Let's try this:
{
let mut default_converter =
BufferLayoutConverter::for_layouts(source_layout, &target_layout);
// We can map any attribute onto any other attribute, as long as their dataytpes can be converted (so no
// vector-to-scalar conversion, e.g. going from positions to classifications)
default_converter.set_custom_mapping(
// Make sure that the attributes and their datatypes exactly match the source and target layouts!
&POSITION_3D.with_custom_datatype(PointAttributeDataType::Vec3i32),
&COLOR_RGB.with_custom_datatype(PointAttributeDataType::Vec3u8),
);
// Custom mappings also support transformations. With them, we can apply some arbitrary transformation
// to each value. A useful example is the conversion from local integer coordinates into world-space
// floating-point coordinates when parsing an LAS file, which would work something like this:
const OFFSET: Vector3<f64> = Vector3::new(10.0, 10.0, 10.0);
const SCALE: Vector3<f64> = Vector3::new(0.001, 0.001, 0.001);
default_converter.set_custom_mapping_with_transformation(
&POSITION_3D.with_custom_datatype(PointAttributeDataType::Vec3i32),
&POSITION_3D,
|local_position: Vector3<f64>| local_position.component_mul(&SCALE) + OFFSET,
// We also have to specify whether we want to transform the source attribute or the target attribute. Here, we
// want to retain the precision of the f64 offset and scale values, so we first apply the conversion i32->f64
// and then apply the transformation to the target attribute!
false,
);
let converted_points = default_converter.convert::<VectorBuffer, _>(&source_points_buffer);
for point in converted_points.view::<TargetPointType>().into_iter() {
println!("{point:#?}");
}
}
}