use re_log::{ResultExt as _, debug_assert_eq};
use crate::allocator::DataTextureSource;
use crate::draw_phases::PickingLayerObjectId;
use crate::renderer::gpu_data::PositionRadius;
use crate::renderer::{
PointCloudBatchFlags, PointCloudBatchInfo, PointCloudDrawData, PointCloudDrawDataError,
};
use crate::{
Color32, CpuWriteGpuReadError, DepthOffset, Label, OutlineMaskPreference,
PickingLayerInstanceId, RenderContext, Size,
};
pub struct PointCloudBuilder<'ctx> {
pub(crate) ctx: &'ctx RenderContext,
pub(crate) position_radius_buffer: DataTextureSource<'ctx, PositionRadius>,
pub(crate) color_buffer: DataTextureSource<'ctx, Color32>,
pub(crate) picking_instance_ids_buffer: DataTextureSource<'ctx, PickingLayerInstanceId>,
pub(crate) batches: Vec<PointCloudBatchInfo>,
pub(crate) radius_boost_in_ui_points_for_outlines: f32,
}
impl<'ctx> PointCloudBuilder<'ctx> {
pub fn new(ctx: &'ctx RenderContext) -> Self {
Self {
ctx,
position_radius_buffer: DataTextureSource::new(ctx),
color_buffer: DataTextureSource::new(ctx),
picking_instance_ids_buffer: DataTextureSource::new(ctx),
batches: Vec::with_capacity(16),
radius_boost_in_ui_points_for_outlines: 0.0,
}
}
pub fn reserve(
&mut self,
expected_number_of_additional_points: usize,
) -> Result<usize, CpuWriteGpuReadError> {
re_tracing::profile_function_if!(100_000 < expected_number_of_additional_points);
self.position_radius_buffer
.reserve(expected_number_of_additional_points)?;
self.color_buffer
.reserve(expected_number_of_additional_points)?;
self.picking_instance_ids_buffer
.reserve(expected_number_of_additional_points)
}
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;
}
#[inline]
pub fn batch(&mut self, label: impl Into<Label>) -> PointCloudBatchBuilder<'_, 'ctx> {
self.batches.push(PointCloudBatchInfo {
label: label.into(),
..PointCloudBatchInfo::default()
});
PointCloudBatchBuilder(self)
}
#[inline]
pub fn batch_with_info(
&mut self,
info: PointCloudBatchInfo,
) -> PointCloudBatchBuilder<'_, 'ctx> {
self.batches.push(info);
PointCloudBatchBuilder(self)
}
pub fn into_draw_data(self) -> Result<PointCloudDrawData, PointCloudDrawDataError> {
PointCloudDrawData::new(self)
}
}
pub struct PointCloudBatchBuilder<'a, 'ctx>(&'a mut PointCloudBuilder<'ctx>);
impl Drop for PointCloudBatchBuilder<'_, '_> {
fn drop(&mut self) {
if self.0.batches.last().unwrap().point_count == 0 {
self.0.batches.pop();
}
}
}
impl PointCloudBatchBuilder<'_, '_> {
#[inline]
fn batch_mut(&mut self) -> &mut PointCloudBatchInfo {
self.0
.batches
.last_mut()
.expect("batch should have been added on PointCloudBatchBuilder creation")
}
#[inline]
pub fn world_from_obj(mut self, world_from_obj: glam::Affine3A) -> Self {
self.batch_mut().world_from_obj = world_from_obj;
self
}
#[inline]
pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
self.batch_mut().overall_outline_mask_ids = outline_mask_ids;
self
}
#[inline]
pub fn depth_offset(mut self, depth_offset: DepthOffset) -> Self {
self.batch_mut().depth_offset = depth_offset;
self
}
#[inline]
pub fn add_points_slow(
self,
positions: &[glam::Vec3],
radii: &[Size],
colors: &[Color32],
picking_ids: &[PickingLayerInstanceId],
) -> Self {
re_tracing::profile_function!();
let positions_and_radii = PositionRadius::from_many(positions, radii);
self.add_points(&positions_and_radii, colors, picking_ids)
}
#[inline]
pub fn add_points(
mut self,
positions_and_radii: &[PositionRadius],
colors: &[Color32],
picking_ids: &[PickingLayerInstanceId],
) -> Self {
re_tracing::profile_function!();
debug_assert_eq!(
self.0.position_radius_buffer.len(),
self.0.color_buffer.len()
);
debug_assert_eq!(
self.0.position_radius_buffer.len(),
self.0.picking_instance_ids_buffer.len()
);
let num_points = positions_and_radii.len();
let Some(num_available_points) = self
.0
.position_radius_buffer
.reserve(num_points)
.ok_or_log_error()
else {
return self;
};
let num_points = if num_points > num_available_points {
re_log::error_once!(
"Reached maximum number of points for point cloud of {}. Ignoring all excess points.",
self.0.position_radius_buffer.len() + num_available_points
);
num_available_points
} else {
num_points
};
if num_points == 0 {
return self;
}
let positions_and_radii =
&positions_and_radii[0..num_points.min(positions_and_radii.len())];
let colors = &colors[0..num_points.min(colors.len())];
let picking_ids = &picking_ids[0..num_points.min(picking_ids.len())];
self.batch_mut().point_count += num_points as u32;
{
re_tracing::profile_scope!("positions_and_radii");
self.0
.position_radius_buffer
.extend_from_slice(positions_and_radii)
.ok_or_log_error();
}
{
re_tracing::profile_scope!("colors");
self.0
.color_buffer
.extend_from_slice(colors)
.ok_or_log_error();
self.0
.color_buffer
.add_n(Color32::WHITE, num_points.saturating_sub(colors.len())) .ok_or_log_error();
}
{
re_tracing::profile_scope!("picking_ids");
self.0
.picking_instance_ids_buffer
.extend_from_slice(picking_ids)
.ok_or_log_error();
self.0
.picking_instance_ids_buffer
.add_n(
PickingLayerInstanceId::default(),
num_points.saturating_sub(picking_ids.len()),
)
.ok_or_log_error();
}
self
}
#[inline]
pub fn add_points_2d(
self,
positions: &[glam::Vec3],
radii: &[Size],
colors: &[Color32],
picking_ids: &[PickingLayerInstanceId],
) -> Self {
re_tracing::profile_function!();
self.add_points_slow(positions, radii, colors, picking_ids)
.flags(PointCloudBatchFlags::FLAG_DRAW_AS_CIRCLES)
}
#[inline]
pub fn flags(mut self, flags: PointCloudBatchFlags) -> Self {
self.batch_mut().flags |= flags;
self
}
#[inline]
pub fn picking_object_id(mut self, picking_object_id: PickingLayerObjectId) -> Self {
self.batch_mut().picking_object_id = picking_object_id;
self
}
#[inline]
pub fn push_additional_outline_mask_ids_for_range(
mut self,
range: std::ops::Range<u32>,
ids: OutlineMaskPreference,
) -> Self {
self.batch_mut()
.additional_outline_mask_ids_vertex_ranges
.push((range, ids));
self
}
}