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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
use std::ops::Range;

use re_log::ResultExt;

use crate::{
    allocator::{CpuWriteGpuReadError, DataTextureSource, DataTextureSourceWriteError},
    renderer::{
        gpu_data::{LineStripInfo, LineVertex},
        LineBatchInfo, LineDrawData, LineDrawDataError, LineStripFlags,
    },
    Color32, DebugLabel, DepthOffset, OutlineMaskPreference, PickingLayerInstanceId,
    PickingLayerObjectId, RenderContext, Size,
};

/// Builder for a vector of line strips, making it easy to create [`crate::renderer::LineDrawData`].
///
/// TODO(andreas): We could make significant optimizations here by making this builder capable
/// of writing to a GPU readable memory location for all its data.
pub struct LineDrawableBuilder<'ctx> {
    pub ctx: &'ctx RenderContext,

    pub(crate) vertices_buffer: DataTextureSource<'ctx, LineVertex>,
    pub(crate) batches: Vec<LineBatchInfo>,
    pub(crate) strips_buffer: DataTextureSource<'ctx, LineStripInfo>,

    /// Buffer for picking instance id - every strip gets its own instance id.
    /// Therefore, there need to be always as many picking instance ids as there are strips.
    pub(crate) picking_instance_ids_buffer: DataTextureSource<'ctx, PickingLayerInstanceId>,

    pub(crate) radius_boost_in_ui_points_for_outlines: f32,
}

impl<'ctx> LineDrawableBuilder<'ctx> {
    pub fn new(ctx: &'ctx RenderContext) -> Self {
        Self {
            ctx,
            vertices_buffer: DataTextureSource::new(ctx),
            strips_buffer: DataTextureSource::new(ctx),
            batches: Vec::with_capacity(16),
            picking_instance_ids_buffer: DataTextureSource::new(ctx),
            radius_boost_in_ui_points_for_outlines: 0.0,
        }
    }

    /// Returns number of strips that can be added without reallocation.
    /// This may be smaller than the requested number if the maximum number of strips is reached.
    pub fn reserve_strips(&mut self, num_strips: usize) -> Result<usize, CpuWriteGpuReadError> {
        // We know that the maximum number is independent of datatype, so we can use the same value for all.
        self.strips_buffer.reserve(num_strips)?;
        self.picking_instance_ids_buffer.reserve(num_strips)
    }

    /// Returns number of vertices that can be added without reallocation.
    /// This may be smaller than the requested number if the maximum number of vertices is reached.
    pub fn reserve_vertices(&mut self, num_vertices: usize) -> Result<usize, CpuWriteGpuReadError> {
        self.vertices_buffer.reserve(num_vertices)
    }

    /// Boosts the size of the points by the given amount of ui-points for the purpose of drawing outlines.
    pub fn radius_boost_in_ui_points_for_outlines(
        &mut self,
        radius_boost_in_ui_points_for_outlines: f32,
    ) {
        self.radius_boost_in_ui_points_for_outlines = radius_boost_in_ui_points_for_outlines;
    }

    /// Start of a new batch.
    pub fn batch(&mut self, label: impl Into<DebugLabel>) -> LineBatchBuilder<'_, 'ctx> {
        self.batches.push(LineBatchInfo {
            label: label.into(),
            ..LineBatchInfo::default()
        });

        LineBatchBuilder(self)
    }

    /// Finalizes the builder and returns a line draw data with all the lines added so far.
    pub fn into_draw_data(mut self) -> Result<LineDrawData, LineDrawDataError> {
        if !self.vertices_buffer.is_empty() {
            // sentinel at the end to facilitate caps.
            self.vertices_buffer.push(LineVertex::SENTINEL)?;
        }

        LineDrawData::new(self)
    }

    pub fn is_empty(&self) -> bool {
        self.strips_buffer.is_empty()
    }

    pub fn default_box_flags() -> LineStripFlags {
        LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS
    }
}

pub struct LineBatchBuilder<'a, 'ctx>(&'a mut LineDrawableBuilder<'ctx>);

impl<'a, 'ctx> Drop for LineBatchBuilder<'a, 'ctx> {
    fn drop(&mut self) {
        // Remove batch again if it wasn't actually used.
        if self.0.batches.last().unwrap().line_vertex_count == 0 {
            self.0.batches.pop();
        }
    }
}

impl<'a, 'ctx> LineBatchBuilder<'a, 'ctx> {
    #[inline]
    fn batch_mut(&mut self) -> &mut LineBatchInfo {
        self.0
            .batches
            .last_mut()
            .expect("batch should have been added on PointCloudBatchBuilder creation")
    }

    fn add_vertices(
        &mut self,
        points: impl ExactSizeIterator<Item = glam::Vec3>,
        strip_index: u32,
    ) -> Result<(), DataTextureSourceWriteError> {
        let num_new_vertices = points.len();
        if num_new_vertices == 0 {
            return Ok(());
        }

        // Sentinel at the beginning and end to facilitate caps.
        let add_start_sentinel = self.0.vertices_buffer.is_empty();
        let num_sentinels_to_add = if add_start_sentinel {
            LineVertex::NUM_SENTINEL_VERTICES // Start and end sentinel.
        } else {
            1 // End sentinel only.
        };

        // Do a reserve ahead of time including sentinel vertices, in order to check whether we're hitting the data texture limit.
        let reserve_count = num_new_vertices + num_sentinels_to_add;
        let num_available_points = self.0.vertices_buffer.reserve(reserve_count)?;
        let num_new_vertices = if reserve_count > num_available_points {
            re_log::error_once!(
                "Reached maximum number of vertices for lines strips of {}. Ignoring all excess vertices.",
                self.0.vertices_buffer.len() + num_available_points - LineVertex::NUM_SENTINEL_VERTICES
            );
            num_available_points - num_sentinels_to_add
        } else {
            num_new_vertices
        };

        if add_start_sentinel {
            self.0.vertices_buffer.push(LineVertex::SENTINEL)?;
        }

        // TODO(andreas): It would be nice to pass on the iterator as is so we don't have to do yet another
        // copy of the data and instead write into the buffers directly - if done right this should be the fastest.
        // But it's surprisingly tricky to do this effectively.
        let vertices = points
            .map(|pos| LineVertex {
                position: pos,
                strip_index,
            })
            .take(num_new_vertices)
            .collect::<Vec<_>>();
        self.0.vertices_buffer.extend_from_slice(&vertices)?;

        self.batch_mut().line_vertex_count += num_new_vertices as u32;

        Ok(())
    }

    /// `num_vertices_added` excludes start sentinel.
    fn create_strip_builder(
        &mut self,
        mut num_strips_added: usize,
        num_vertices_added: usize,
    ) -> LineStripBuilder<'_, 'ctx> {
        // Reserve space ahead of time to figure out whether we're hitting the data texture limit.
        let Some(num_available_strips) = self
            .0
            .strips_buffer
            .reserve(num_strips_added)
            .ok_or_log_error_once()
        else {
            return LineStripBuilder::new_empty(self.0);
        };
        if num_available_strips < num_strips_added {
            re_log::error_once!(
                "Reached maximum number of strips for lines of {}. Ignoring all excess strips.",
                self.0.strips_buffer.len() + num_available_strips
            );
            num_strips_added = num_available_strips;
        }

        let vertex_range = if num_vertices_added == 0 {
            0..0
        } else {
            let vertex_buffer_element_count = self.0.vertices_buffer.len();
            // The vertex range works with "logical line vertices", meaning we don't want to include the start sentinel
            // which at this point is already included in `vertices_buffer`, thus -1.
            let total_vertex_count = vertex_buffer_element_count - 1;
            (total_vertex_count - num_vertices_added)..(total_vertex_count)
        };

        LineStripBuilder {
            builder: self.0,
            outline_mask_ids: OutlineMaskPreference::NONE,
            picking_instance_id: PickingLayerInstanceId::default(),
            vertex_range,
            num_strips_added,
            strip: LineStripInfo::default(),
        }
    }

    /// Sets the `world_from_obj` matrix for the *entire* batch.
    #[inline]
    pub fn world_from_obj(mut self, world_from_obj: glam::Affine3A) -> Self {
        self.batch_mut().world_from_obj = world_from_obj;
        self
    }

    /// Sets an outline mask for every element in the batch.
    #[inline]
    pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
        self.batch_mut().overall_outline_mask_ids = outline_mask_ids;
        self
    }

    /// Sets the picking object id for every element in the batch.
    #[inline]
    pub fn picking_object_id(mut self, picking_object_id: PickingLayerObjectId) -> Self {
        self.batch_mut().picking_object_id = picking_object_id;
        self
    }

    /// Sets the depth offset for the entire batch.
    #[inline]
    pub fn depth_offset(mut self, depth_offset: DepthOffset) -> Self {
        self.batch_mut().depth_offset = depth_offset;
        self
    }

    /// Sets the length factor as multiple of a line's radius applied to all triangle caps in this batch.
    ///
    /// This controls how far the "pointy end" of the triangle/arrow-head extends.
    /// (defaults to 4.0)
    #[inline]
    pub fn triangle_cap_length_factor(mut self, triangle_cap_length_factor: f32) -> Self {
        self.batch_mut().triangle_cap_length_factor = triangle_cap_length_factor;
        self
    }

    /// Sets the width factor as multiple of a line's radius applied to all triangle caps in this batch.
    ///
    /// This controls how wide the triangle/arrow-head is orthogonal to the line's direction.
    /// (defaults to 2.0)
    #[inline]
    pub fn triangle_cap_width_factor(mut self, triangle_cap_width_factor: f32) -> Self {
        self.batch_mut().triangle_cap_width_factor = triangle_cap_width_factor;
        self
    }

    /// Adds a 3D series of line connected points.
    pub fn add_strip(
        &mut self,
        points: impl ExactSizeIterator<Item = glam::Vec3>,
    ) -> LineStripBuilder<'_, 'ctx> {
        let strip_index = self.0.strips_buffer.len() as u32;
        let num_vertices_added = points.len();

        self.add_vertices(points, strip_index)
            .ok_or_log_error_once();

        self.create_strip_builder(1, num_vertices_added)
    }

    /// Adds a single 3D line segment connecting two points.
    #[inline]
    pub fn add_segment(&mut self, a: glam::Vec3, b: glam::Vec3) -> LineStripBuilder<'_, 'ctx> {
        self.add_strip([a, b].into_iter())
    }

    /// Adds a series of unconnected 3D line segments.
    pub fn add_segments(
        &mut self,
        segments: impl ExactSizeIterator<Item = (glam::Vec3, glam::Vec3)>,
    ) -> LineStripBuilder<'_, 'ctx> {
        #![allow(clippy::tuple_array_conversions)] // false positive

        let old_strip_count = self.0.strips_buffer.len();
        let mut strip_index = old_strip_count as u32;

        let num_strips_added = segments.len();
        let num_vertices_added = num_strips_added * 2;

        // It's tempting to assign the same strip to all vertices, after all they share
        // color/radius/tag properties.
        // However, if we don't assign different strip indices, we don't know when a strip (==segment) starts and ends.
        // TODO(andreas): There's likely some low hanging fruit here to make this faster by collapsing into a single call to `add_vertices`.
        for (a, b) in segments {
            self.add_vertices([a, b].into_iter(), strip_index)
                .ok_or_log_error_once();
            strip_index += 1;
        }

        self.create_strip_builder(num_strips_added, num_vertices_added)
    }

    /// Add box outlines from a unit cube transformed by `transform`.
    ///
    /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good)
    #[inline]
    pub fn add_box_outline_from_transform(
        &mut self,
        transform: glam::Affine3A,
    ) -> LineStripBuilder<'_, 'ctx> {
        let corners = [
            transform.transform_point3(glam::vec3(-0.5, -0.5, -0.5)),
            transform.transform_point3(glam::vec3(-0.5, -0.5, 0.5)),
            transform.transform_point3(glam::vec3(-0.5, 0.5, -0.5)),
            transform.transform_point3(glam::vec3(-0.5, 0.5, 0.5)),
            transform.transform_point3(glam::vec3(0.5, -0.5, -0.5)),
            transform.transform_point3(glam::vec3(0.5, -0.5, 0.5)),
            transform.transform_point3(glam::vec3(0.5, 0.5, -0.5)),
            transform.transform_point3(glam::vec3(0.5, 0.5, 0.5)),
        ];
        self.add_box_from_corners(corners)
    }

    /// Add box outlines.
    ///
    /// Internally a single closed line strip.
    /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good)
    ///
    /// Returns None for empty and non-finite boxes.
    pub fn add_box_outline(
        &mut self,
        bbox: &re_math::BoundingBox,
    ) -> Option<LineStripBuilder<'_, 'ctx>> {
        if !bbox.is_something() || !bbox.is_finite() {
            return None;
        }

        Some(self.add_box_from_corners(bbox.corners()))
    }

    fn add_box_from_corners(&mut self, corners: [glam::Vec3; 8]) -> LineStripBuilder<'_, 'ctx> {
        let mut strip_index = self.0.strips_buffer.len() as u32;

        // Bottom plus connection to top.
        self.add_vertices(
            [
                // bottom loop
                corners[0b000],
                corners[0b001],
                corners[0b011],
                corners[0b010],
                corners[0b000],
                // joined to top loop
                corners[0b100],
                corners[0b101],
                corners[0b111],
                corners[0b110],
                corners[0b100],
            ]
            .into_iter(),
            strip_index,
        )
        .ok_or_log_error_once();
        strip_index += 1;

        // remaining side edges.
        for line in [
            [corners[0b001], corners[0b101]],
            [corners[0b010], corners[0b110]],
            [corners[0b011], corners[0b111]],
        ] {
            self.add_vertices(line.into_iter(), strip_index)
                .ok_or_log_error_once();
            strip_index += 1;
        }

        let num_strips_added = 4;
        let num_vertices_added = 10 + 3 * 2;
        self.create_strip_builder(num_strips_added, num_vertices_added)
            .flags(LineDrawableBuilder::default_box_flags())
    }

    /// Add rectangle outlines.
    ///
    /// Internally adds a single linestrip with 5 vertices.
    /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good)
    #[inline]
    pub fn add_rectangle_outline(
        &mut self,
        top_left_corner: glam::Vec3,
        extent_u: glam::Vec3,
        extent_v: glam::Vec3,
    ) -> LineStripBuilder<'_, 'ctx> {
        self.add_strip(
            [
                top_left_corner,
                top_left_corner + extent_u,
                top_left_corner + extent_u + extent_v,
                top_left_corner + extent_v,
                top_left_corner,
            ]
            .into_iter(),
        )
        .flags(LineDrawableBuilder::default_box_flags())
    }

    /// Adds a 2D series of line connected points.
    ///
    /// Uses autogenerated depth value.
    #[inline]
    pub fn add_strip_2d(
        &mut self,
        points: impl ExactSizeIterator<Item = glam::Vec2>,
    ) -> LineStripBuilder<'_, 'ctx> {
        self.add_strip(points.map(|p| p.extend(0.0)))
            .flags(LineStripFlags::FLAG_FORCE_ORTHO_SPANNING)
    }

    /// Adds a single 2D line segment connecting two points. Uses autogenerated depth value.
    #[inline]
    pub fn add_segment_2d(&mut self, a: glam::Vec2, b: glam::Vec2) -> LineStripBuilder<'_, 'ctx> {
        self.add_strip_2d([a, b].into_iter())
            .flags(LineStripFlags::FLAG_FORCE_ORTHO_SPANNING)
    }

    /// Adds a series of unconnected 2D line segments.
    ///
    /// Uses autogenerated depth value, all segments get the same depth value.
    #[inline]
    pub fn add_segments_2d(
        &mut self,
        segments: impl ExactSizeIterator<Item = (glam::Vec2, glam::Vec2)>,
    ) -> LineStripBuilder<'_, 'ctx> {
        self.add_segments(segments.map(|(a, b)| (a.extend(0.0), b.extend(0.0))))
            .flags(LineStripFlags::FLAG_FORCE_ORTHO_SPANNING)
    }

    /// Add 2D rectangle outlines.
    ///
    /// Internally adds 4 2D line segments with rounded line heads.
    /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good)
    #[inline]
    pub fn add_rectangle_outline_2d(
        &mut self,
        top_left_corner: glam::Vec2,
        extent_u: glam::Vec2,
        extent_v: glam::Vec2,
    ) -> LineStripBuilder<'_, 'ctx> {
        self.add_rectangle_outline(
            top_left_corner.extend(0.0),
            extent_u.extend(0.0),
            extent_v.extend(0.0),
        )
        .flags(LineStripFlags::FLAG_FORCE_ORTHO_SPANNING)
    }

    /// Add 2D rectangle outlines with axis along X and Y.
    ///
    /// Internally adds 4 2D line segments with rounded line heads.
    /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good)
    #[inline]
    pub fn add_axis_aligned_rectangle_outline_2d(
        &mut self,
        min: glam::Vec2,
        max: glam::Vec2,
    ) -> LineStripBuilder<'_, 'ctx> {
        self.add_rectangle_outline(
            min.extend(0.0),
            glam::Vec3::X * (max.x - min.x),
            glam::Vec3::Y * (max.y - min.y),
        )
        .flags(LineStripFlags::FLAG_FORCE_ORTHO_SPANNING)
    }
}

pub struct LineStripBuilder<'a, 'ctx> {
    builder: &'a mut LineDrawableBuilder<'ctx>,
    outline_mask_ids: OutlineMaskPreference,
    vertex_range: Range<usize>,

    picking_instance_id: PickingLayerInstanceId,
    strip: LineStripInfo,
    num_strips_added: usize,
}

impl<'a, 'ctx> LineStripBuilder<'a, 'ctx> {
    pub fn new_empty(builder: &'a mut LineDrawableBuilder<'ctx>) -> Self {
        Self {
            builder,
            outline_mask_ids: OutlineMaskPreference::NONE,
            vertex_range: 0..0,
            picking_instance_id: PickingLayerInstanceId::default(),
            strip: LineStripInfo::default(),
            num_strips_added: 0,
        }
    }

    #[inline]
    pub fn radius(mut self, radius: Size) -> Self {
        self.strip.radius = radius.into();
        self
    }

    #[inline]
    pub fn color(mut self, color: Color32) -> Self {
        self.strip.color = color;
        self
    }

    /// Adds (!) flags to the line strip.
    #[inline]
    pub fn flags(mut self, flags: LineStripFlags) -> Self {
        self.strip.flags |= flags;
        self
    }

    #[inline]
    pub fn picking_instance_id(mut self, instance_id: PickingLayerInstanceId) -> Self {
        self.picking_instance_id = instance_id;
        self
    }

    /// Sets an individual outline mask ids.
    /// Note that this has a relatively high performance impact.
    #[inline]
    pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
        self.outline_mask_ids = outline_mask_ids;
        self
    }
}

impl<'a, 'ctx> Drop for LineStripBuilder<'a, 'ctx> {
    fn drop(&mut self) {
        if self.num_strips_added == 0 {
            // Happens if we reached the maximum number of strips.
            return;
        }

        if self.outline_mask_ids.is_some() {
            self.builder
                .batches
                .last_mut()
                .unwrap()
                .additional_outline_mask_ids_vertex_ranges
                .push((
                    self.vertex_range.start as u32..self.vertex_range.end as u32,
                    self.outline_mask_ids,
                ));
        }

        self.builder
            .picking_instance_ids_buffer
            .add_n(self.picking_instance_id, self.num_strips_added)
            .ok_or_log_error_once();
        self.builder
            .strips_buffer
            .add_n(self.strip, self.num_strips_added)
            .ok_or_log_error_once();

        debug_assert!(
            self.builder.strips_buffer.len() == self.builder.picking_instance_ids_buffer.len()
        );
    }
}