From cf16d20824ad4b4223854ea85aec172bdf68c50e Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 23 Jan 2025 15:58:21 +0100 Subject: [PATCH] Make `Box`/`AssetVideo`/`ViewCoordinates`/`Asset3D` eager serialized in Rust (#8785) --- .../rerun/archetypes/annotation_context.fbs | 4 +- .../definitions/rerun/archetypes/asset3d.fbs | 4 +- .../rerun/archetypes/asset_video.fbs | 2 +- .../definitions/rerun/archetypes/boxes2d.fbs | 2 +- .../definitions/rerun/archetypes/boxes3d.fbs | 2 +- .../rerun/archetypes/view_coordinates.fbs | 4 +- .../src/archetypes/annotation_context.rs | 128 +++-- .../store/re_types/src/archetypes/asset3d.rs | 224 +++++--- .../re_types/src/archetypes/asset3d_ext.rs | 9 +- .../re_types/src/archetypes/asset_video.rs | 167 ++++-- .../src/archetypes/asset_video_ext.rs | 36 +- .../store/re_types/src/archetypes/boxes2d.rs | 405 +++++++------- .../re_types/src/archetypes/boxes2d_ext.rs | 22 +- .../store/re_types/src/archetypes/boxes3d.rs | 494 +++++++++--------- .../re_types/src/archetypes/boxes3d_ext.rs | 26 +- .../re_types/src/archetypes/transform3d.rs | 2 +- .../src/archetypes/view_coordinates.rs | 129 +++-- .../src/archetypes/view_coordinates_ext.rs | 12 +- crates/store/re_types/tests/types/asset3d.rs | 20 +- crates/store/re_types/tests/types/box2d.rs | 62 ++- crates/store/re_types/tests/types/box3d.rs | 73 ++- .../re_types/tests/types/view_coordinates.rs | 16 +- .../viewer/re_view_spatial/src/mesh_cache.rs | 6 +- .../viewer/re_view_spatial/src/mesh_loader.rs | 55 +- .../re_view_spatial/src/transform_cache.rs | 8 +- .../src/visualizers/assets3d.rs | 6 +- .../reference/migration/migration-0-22.md | 7 + .../snippets/all/archetypes/asset3d_simple.rs | 2 +- .../all/archetypes/transform3d_hierarchy.rs | 2 +- .../all/archetypes/view_coordinates_simple.rs | 2 +- examples/rust/clock/src/main.rs | 2 +- examples/rust/objectron/src/main.rs | 2 +- examples/rust/raw_mesh/src/main.rs | 5 +- .../roundtrips/view_coordinates/src/main.rs | 2 +- tests/rust/test_api/src/main.rs | 2 +- .../rust/test_pinhole_projection/src/main.rs | 2 +- 36 files changed, 1175 insertions(+), 771 deletions(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/annotation_context.fbs b/crates/store/re_types/definitions/rerun/archetypes/annotation_context.fbs index bd7442723f31..ecaefc2c23aa 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/annotation_context.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/annotation_context.fbs @@ -15,8 +15,8 @@ namespace rerun.archetypes; /// \example archetypes/annotation_context_segmentation title="Segmentation" image="https://static.rerun.io/annotation_context_segmentation/6c9e88fc9d44a08031cadd444c2e58a985cc1208/1200w.png"" /// \example archetypes/annotation_context_connections !api title="Connections" image="https://static.rerun.io/annotation_context_connections/4a8422bc154699c5334f574ff01b55c5cd1748e3/1200w.png" table AnnotationContext ( - // TODO(#7245): "attr.rust.archetype_eager", - "attr.rust.derive": "Eq, PartialEq", + "attr.rust.archetype_eager", + "attr.rust.derive": "PartialEq", "attr.docs.view_types": "Spatial2DView, Spatial3DView" ) { /// List of class descriptions, mapping class indices to class names, colors etc. diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs index d230ad0fb42a..fec03c7ceff2 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs @@ -11,8 +11,8 @@ namespace rerun.archetypes; /// /// \example archetypes/asset3d_simple title="Simple 3D asset" image="https://static.rerun.io/asset3d_simple/af238578188d3fd0de3e330212120e2842a8ddb2/1200w.png" table Asset3D ( - // TODO(#7245): "attr.rust.archetype_eager", - "attr.rust.derive": "PartialEq, Eq", + "attr.rust.archetype_eager", + "attr.rust.derive": "PartialEq", "attr.docs.category": "Spatial 3D", "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection" ) { diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs index 2d5209ac1c45..71760bc17ad4 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs @@ -12,7 +12,7 @@ namespace rerun.archetypes; /// \example archetypes/video_auto_frames title="Video with automatically determined frames" image="https://static.rerun.io/video_manual_frames/320a44e1e06b8b3a3161ecbbeae3e04d1ccb9589/1200w.png" /// \example archetypes/video_manual_frames title="Demonstrates manual use of video frame references" image="https://static.rerun.io/video_manual_frames/9f41c00f84a98cc3f26875fba7c1d2fa2bad7151/1200w.png" table AssetVideo ( - // TODO(#7245): "attr.rust.archetype_eager", + "attr.rust.archetype_eager", "attr.docs.category": "Video", "attr.docs.view_types": "Spatial2DView, Spatial3DView: if logged under a projection" ) { diff --git a/crates/store/re_types/definitions/rerun/archetypes/boxes2d.fbs b/crates/store/re_types/definitions/rerun/archetypes/boxes2d.fbs index 8c2ca8bd82e6..090e217407b1 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/boxes2d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/boxes2d.fbs @@ -6,7 +6,7 @@ namespace rerun.archetypes; /// /// \example archetypes/boxes2d_simple title="Simple 2D boxes" image="https://static.rerun.io/box2d_simple/ac4424f3cf747382867649610cbd749c45b2020b/1200w.png" table Boxes2D ( - // TODO(#7245): "attr.rust.archetype_eager", + "attr.rust.archetype_eager", "attr.rust.derive": "PartialEq", "attr.rust.new_pub_crate", "attr.cpp.no_field_ctors", diff --git a/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs index a690eb659903..a7633ddf24c1 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs @@ -11,7 +11,7 @@ namespace rerun.archetypes; /// \example archetypes/boxes3d_simple !api title="Simple 3D boxes" image="https://static.rerun.io/box3d_simple/d6a3f38d2e3360fbacac52bb43e44762635be9c8/1200w.png" /// \example archetypes/boxes3d_batch title="Batch of 3D boxes" image="https://static.rerun.io/box3d_batch/5aac5b5d29c9f2ecd572c93f6970fcec17f4984b/1200w.png" table Boxes3D ( - // TODO(#7245): "attr.rust.archetype_eager", + "attr.rust.archetype_eager", "attr.rust.derive": "PartialEq", "attr.rust.new_pub_crate", "attr.cpp.no_field_ctors", diff --git a/crates/store/re_types/definitions/rerun/archetypes/view_coordinates.fbs b/crates/store/re_types/definitions/rerun/archetypes/view_coordinates.fbs index 49ebdd02d0bc..d80f73e5bec8 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/view_coordinates.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/view_coordinates.fbs @@ -17,8 +17,8 @@ namespace rerun.archetypes; /// /// \example archetypes/view_coordinates_simple title="View coordinates for adjusting the eye camera" image="https://static.rerun.io/viewcoordinates/0833f0dc8616a676b7b2c566f2a6f613363680c5/1200w.png" table ViewCoordinates ( - // TODO(#7245): "attr.rust.archetype_eager", - "attr.rust.derive": "Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.archetype_eager", + "attr.rust.derive": "PartialEq", "attr.rust.repr": "transparent", "attr.docs.category": "Spatial 3D", "attr.docs.view_types": "Spatial3DView" diff --git a/crates/store/re_types/src/archetypes/annotation_context.rs b/crates/store/re_types/src/archetypes/annotation_context.rs index 2ad8d8e42cca..e9acbef71822 100644 --- a/crates/store/re_types/src/archetypes/annotation_context.rs +++ b/crates/store/re_types/src/archetypes/annotation_context.rs @@ -69,10 +69,10 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct AnnotationContext { /// List of class descriptions, mapping class indices to class names, colors etc. - pub context: crate::components::AnnotationContext, + pub context: Option, } impl AnnotationContext { @@ -168,39 +168,21 @@ impl ::re_types_core::Archetype for AnnotationContext { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let context = { - let array = arrays_by_descr - .get(&Self::descriptor_context()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.AnnotationContext#context")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.AnnotationContext#context")? - .into_iter() - .next() - .flatten() - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.AnnotationContext#context")? - }; + let context = arrays_by_descr + .get(&Self::descriptor_context()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_context())); Ok(Self { context }) } } impl ::re_types_core::AsComponents for AnnotationContext { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; - [ - Some(Self::indicator()), - (Some(&self.context as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_context()), - } - }), - ] - .into_iter() - .flatten() - .collect() + [Self::indicator().serialized(), self.context.clone()] + .into_iter() + .flatten() + .collect() } } @@ -211,9 +193,90 @@ impl AnnotationContext { #[inline] pub fn new(context: impl Into) -> Self { Self { - context: context.into(), + context: try_serialize_field(Self::descriptor_context(), [context]), + } + } + + /// Update only some specific fields of a `AnnotationContext`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `AnnotationContext`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + context: Some(SerializedComponentBatch::new( + crate::components::AnnotationContext::arrow_empty(), + Self::descriptor_context(), + )), } } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [self + .context + .map(|context| context.partitioned(_lengths.clone())) + .transpose()?]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_context = self.context.as_ref().map(|b| b.array.len()); + let len = None.or(len_context).unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// List of class descriptions, mapping class indices to class names, colors etc. + #[inline] + pub fn with_context( + mut self, + context: impl Into, + ) -> Self { + self.context = try_serialize_field(Self::descriptor_context(), [context]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::AnnotationContext`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_context`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_context( + mut self, + context: impl IntoIterator>, + ) -> Self { + self.context = try_serialize_field(Self::descriptor_context(), context); + self + } } impl ::re_byte_size::SizeBytes for AnnotationContext { @@ -221,9 +284,4 @@ impl ::re_byte_size::SizeBytes for AnnotationContext { fn heap_size_bytes(&self) -> u64 { self.context.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - ::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/asset3d.rs b/crates/store/re_types/src/archetypes/asset3d.rs index 298dce11e646..8dd082075380 100644 --- a/crates/store/re_types/src/archetypes/asset3d.rs +++ b/crates/store/re_types/src/archetypes/asset3d.rs @@ -39,7 +39,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// let rec = rerun::RecordingStreamBuilder::new("rerun_example_asset3d").spawn()?; /// -/// rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; // Set an up-axis +/// rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; // Set an up-axis /// rec.log("world/asset", &rerun::Asset3D::from_file(path)?)?; /// /// Ok(()) @@ -54,10 +54,10 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct Asset3D { /// The asset's bytes. - pub blob: crate::components::Blob, + pub blob: Option, /// The Media Type of the asset. /// @@ -69,13 +69,13 @@ pub struct Asset3D { /// /// If omitted, the viewer will try to guess from the data blob. /// If it cannot guess, it won't be able to render the asset. - pub media_type: Option, + pub media_type: Option, /// A color multiplier applied to the whole asset. /// /// For mesh who already have `albedo_factor` in materials, /// it will be overwritten by actual `albedo_factor` of [`archetypes::Asset3D`][crate::archetypes::Asset3D] (if specified). - pub albedo_factor: Option, + pub albedo_factor: Option, } impl Asset3D { @@ -198,38 +198,19 @@ impl ::re_types_core::Archetype for Asset3D { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let blob = { - let array = arrays_by_descr - .get(&Self::descriptor_blob()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.Asset3D#blob")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Asset3D#blob")? - .into_iter() - .next() - .flatten() - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.Asset3D#blob")? - }; - let media_type = if let Some(array) = arrays_by_descr.get(&Self::descriptor_media_type()) { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Asset3D#media_type")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let albedo_factor = - if let Some(array) = arrays_by_descr.get(&Self::descriptor_albedo_factor()) { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Asset3D#albedo_factor")? - .into_iter() - .next() - .flatten() - } else { - None - }; + let blob = arrays_by_descr + .get(&Self::descriptor_blob()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_blob())); + let media_type = arrays_by_descr + .get(&Self::descriptor_media_type()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_media_type()) + }); + let albedo_factor = arrays_by_descr + .get(&Self::descriptor_albedo_factor()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_albedo_factor()) + }); Ok(Self { blob, media_type, @@ -239,33 +220,14 @@ impl ::re_types_core::Archetype for Asset3D { } impl ::re_types_core::AsComponents for Asset3D { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; [ - Some(Self::indicator()), - (Some(&self.blob as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_blob()), - } - }), - (self - .media_type - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_media_type()), - }), - (self - .albedo_factor - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_albedo_factor()), - }), + Self::indicator().serialized(), + self.blob.clone(), + self.media_type.clone(), + self.albedo_factor.clone(), ] .into_iter() .flatten() @@ -280,12 +242,111 @@ impl Asset3D { #[inline] pub fn new(blob: impl Into) -> Self { Self { - blob: blob.into(), + blob: try_serialize_field(Self::descriptor_blob(), [blob]), media_type: None, albedo_factor: None, } } + /// Update only some specific fields of a `Asset3D`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `Asset3D`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + blob: Some(SerializedComponentBatch::new( + crate::components::Blob::arrow_empty(), + Self::descriptor_blob(), + )), + media_type: Some(SerializedComponentBatch::new( + crate::components::MediaType::arrow_empty(), + Self::descriptor_media_type(), + )), + albedo_factor: Some(SerializedComponentBatch::new( + crate::components::AlbedoFactor::arrow_empty(), + Self::descriptor_albedo_factor(), + )), + } + } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [ + self.blob + .map(|blob| blob.partitioned(_lengths.clone())) + .transpose()?, + self.media_type + .map(|media_type| media_type.partitioned(_lengths.clone())) + .transpose()?, + self.albedo_factor + .map(|albedo_factor| albedo_factor.partitioned(_lengths.clone())) + .transpose()?, + ]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_blob = self.blob.as_ref().map(|b| b.array.len()); + let len_media_type = self.media_type.as_ref().map(|b| b.array.len()); + let len_albedo_factor = self.albedo_factor.as_ref().map(|b| b.array.len()); + let len = None + .or(len_blob) + .or(len_media_type) + .or(len_albedo_factor) + .unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// The asset's bytes. + #[inline] + pub fn with_blob(mut self, blob: impl Into) -> Self { + self.blob = try_serialize_field(Self::descriptor_blob(), [blob]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::Blob`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_blob`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_blob( + mut self, + blob: impl IntoIterator>, + ) -> Self { + self.blob = try_serialize_field(Self::descriptor_blob(), blob); + self + } + /// The Media Type of the asset. /// /// Supported values: @@ -298,7 +359,20 @@ impl Asset3D { /// If it cannot guess, it won't be able to render the asset. #[inline] pub fn with_media_type(mut self, media_type: impl Into) -> Self { - self.media_type = Some(media_type.into()); + self.media_type = try_serialize_field(Self::descriptor_media_type(), [media_type]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::MediaType`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_media_type`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_media_type( + mut self, + media_type: impl IntoIterator>, + ) -> Self { + self.media_type = try_serialize_field(Self::descriptor_media_type(), media_type); self } @@ -311,7 +385,20 @@ impl Asset3D { mut self, albedo_factor: impl Into, ) -> Self { - self.albedo_factor = Some(albedo_factor.into()); + self.albedo_factor = try_serialize_field(Self::descriptor_albedo_factor(), [albedo_factor]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::AlbedoFactor`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_albedo_factor`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_albedo_factor( + mut self, + albedo_factor: impl IntoIterator>, + ) -> Self { + self.albedo_factor = try_serialize_field(Self::descriptor_albedo_factor(), albedo_factor); self } } @@ -323,11 +410,4 @@ impl ::re_byte_size::SizeBytes for Asset3D { + self.media_type.heap_size_bytes() + self.albedo_factor.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - ::is_pod() - && >::is_pod() - && >::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/asset3d_ext.rs b/crates/store/re_types/src/archetypes/asset3d_ext.rs index 985ecfcb738f..e71fd713c4b3 100644 --- a/crates/store/re_types/src/archetypes/asset3d_ext.rs +++ b/crates/store/re_types/src/archetypes/asset3d_ext.rs @@ -32,11 +32,10 @@ impl Asset3D { #[inline] pub fn from_file_contents(contents: Vec, media_type: Option>) -> Self { let media_type = media_type.map(Into::into); - let media_type = MediaType::or_guess_from_data(media_type, &contents); - Self { - blob: contents.into(), - media_type, - albedo_factor: None, + if let Some(media_type) = MediaType::or_guess_from_data(media_type, &contents) { + Self::new(contents).with_media_type(media_type) + } else { + Self::new(contents) } } } diff --git a/crates/store/re_types/src/archetypes/asset_video.rs b/crates/store/re_types/src/archetypes/asset_video.rs index 2cfb6b65fa5e..500c337ea199 100644 --- a/crates/store/re_types/src/archetypes/asset_video.rs +++ b/crates/store/re_types/src/archetypes/asset_video.rs @@ -124,10 +124,10 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct AssetVideo { /// The asset's bytes. - pub blob: crate::components::Blob, + pub blob: Option, /// The Media Type of the asset. /// @@ -136,7 +136,7 @@ pub struct AssetVideo { /// /// If omitted, the viewer will try to guess from the data blob. /// If it cannot guess, it won't be able to render the asset. - pub media_type: Option, + pub media_type: Option, } impl AssetVideo { @@ -248,52 +248,26 @@ impl ::re_types_core::Archetype for AssetVideo { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let blob = { - let array = arrays_by_descr - .get(&Self::descriptor_blob()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.AssetVideo#blob")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.AssetVideo#blob")? - .into_iter() - .next() - .flatten() - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.AssetVideo#blob")? - }; - let media_type = if let Some(array) = arrays_by_descr.get(&Self::descriptor_media_type()) { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.AssetVideo#media_type")? - .into_iter() - .next() - .flatten() - } else { - None - }; + let blob = arrays_by_descr + .get(&Self::descriptor_blob()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_blob())); + let media_type = arrays_by_descr + .get(&Self::descriptor_media_type()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_media_type()) + }); Ok(Self { blob, media_type }) } } impl ::re_types_core::AsComponents for AssetVideo { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; [ - Some(Self::indicator()), - (Some(&self.blob as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_blob()), - } - }), - (self - .media_type - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_media_type()), - }), + Self::indicator().serialized(), + self.blob.clone(), + self.media_type.clone(), ] .into_iter() .flatten() @@ -308,11 +282,98 @@ impl AssetVideo { #[inline] pub fn new(blob: impl Into) -> Self { Self { - blob: blob.into(), + blob: try_serialize_field(Self::descriptor_blob(), [blob]), media_type: None, } } + /// Update only some specific fields of a `AssetVideo`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `AssetVideo`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + blob: Some(SerializedComponentBatch::new( + crate::components::Blob::arrow_empty(), + Self::descriptor_blob(), + )), + media_type: Some(SerializedComponentBatch::new( + crate::components::MediaType::arrow_empty(), + Self::descriptor_media_type(), + )), + } + } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [ + self.blob + .map(|blob| blob.partitioned(_lengths.clone())) + .transpose()?, + self.media_type + .map(|media_type| media_type.partitioned(_lengths.clone())) + .transpose()?, + ]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_blob = self.blob.as_ref().map(|b| b.array.len()); + let len_media_type = self.media_type.as_ref().map(|b| b.array.len()); + let len = None.or(len_blob).or(len_media_type).unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// The asset's bytes. + #[inline] + pub fn with_blob(mut self, blob: impl Into) -> Self { + self.blob = try_serialize_field(Self::descriptor_blob(), [blob]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::Blob`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_blob`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_blob( + mut self, + blob: impl IntoIterator>, + ) -> Self { + self.blob = try_serialize_field(Self::descriptor_blob(), blob); + self + } + /// The Media Type of the asset. /// /// Supported values: @@ -322,7 +383,20 @@ impl AssetVideo { /// If it cannot guess, it won't be able to render the asset. #[inline] pub fn with_media_type(mut self, media_type: impl Into) -> Self { - self.media_type = Some(media_type.into()); + self.media_type = try_serialize_field(Self::descriptor_media_type(), [media_type]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::MediaType`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_media_type`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_media_type( + mut self, + media_type: impl IntoIterator>, + ) -> Self { + self.media_type = try_serialize_field(Self::descriptor_media_type(), media_type); self } } @@ -332,9 +406,4 @@ impl ::re_byte_size::SizeBytes for AssetVideo { fn heap_size_bytes(&self) -> u64 { self.blob.heap_size_bytes() + self.media_type.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - ::is_pod() && >::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/asset_video_ext.rs b/crates/store/re_types/src/archetypes/asset_video_ext.rs index 9d1e0d73202b..ef8e82ebb107 100644 --- a/crates/store/re_types/src/archetypes/asset_video_ext.rs +++ b/crates/store/re_types/src/archetypes/asset_video_ext.rs @@ -32,30 +32,52 @@ impl AssetVideo { #[inline] pub fn from_file_contents(contents: Vec, media_type: Option>) -> Self { let media_type = media_type.map(Into::into); - let media_type = MediaType::or_guess_from_data(media_type, &contents); - Self { - blob: contents.into(), - media_type, + if let Some(media_type) = MediaType::or_guess_from_data(media_type, &contents) { + Self::new(contents).with_media_type(media_type) + } else { + Self::new(contents) } } /// Determines the presentation timestamps of all frames inside the video. /// /// Returned timestamps are in nanoseconds since start and are guaranteed to be monotonically increasing. + /// + /// Panics if the serialized blob data doesn't have the right datatype. #[cfg(feature = "video")] pub fn read_frame_timestamps_ns(&self) -> Result, re_video::VideoLoadError> { + use re_types_core::Loggable; + re_tracing::profile_function!(); + let Some(blob) = self.blob.as_ref() else { + return Ok(Vec::new()); + }; + + // Grab blob data without a copy. + let blob_list_array = blob + .array + .as_any() + .downcast_ref::() + .expect("Video blob data is not a ListArray"); + let blob_data = blob_list_array.values().to_data(); + let blob_bytes = blob_data.buffer(0); + let Some(media_type) = self .media_type - .clone() - .or_else(|| MediaType::guess_from_data(&self.blob)) + .as_ref() + .and_then(|mt| { + MediaType::from_arrow(&mt.array) + .ok() + .and_then(|mt| mt.first().cloned()) + }) + .or_else(|| MediaType::guess_from_data(blob_bytes)) else { return Err(re_video::VideoLoadError::UnrecognizedMimeType); }; Ok( - re_video::VideoData::load_from_bytes(self.blob.as_slice(), media_type.as_str())? + re_video::VideoData::load_from_bytes(blob_bytes, media_type.as_str())? .frame_timestamps_ns() .collect(), ) diff --git a/crates/store/re_types/src/archetypes/boxes2d.rs b/crates/store/re_types/src/archetypes/boxes2d.rs index 3b77c0f5e852..9bdd22e0aded 100644 --- a/crates/store/re_types/src/archetypes/boxes2d.rs +++ b/crates/store/re_types/src/archetypes/boxes2d.rs @@ -44,40 +44,40 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct Boxes2D { /// All half-extents that make up the batch of boxes. - pub half_sizes: Vec, + pub half_sizes: Option, /// Optional center positions of the boxes. - pub centers: Option>, + pub centers: Option, /// Optional colors for the boxes. - pub colors: Option>, + pub colors: Option, /// Optional radii for the lines that make up the boxes. - pub radii: Option>, + pub radii: Option, /// Optional text labels for the boxes. /// /// If there's a single label present, it will be placed at the center of the entity. /// Otherwise, each instance will have its own label. - pub labels: Option>, + pub labels: Option, /// Optional choice of whether the text labels should be shown by default. - pub show_labels: Option, + pub show_labels: Option, /// An optional floating point value that specifies the 2D drawing order. /// /// Objects with higher values are drawn on top of those with lower values. /// /// The default for 2D boxes is 10.0. - pub draw_order: Option, + pub draw_order: Option, /// Optional [`components::ClassId`][crate::components::ClassId]s for the boxes. /// /// The [`components::ClassId`][crate::components::ClassId] provides colors and labels if not specified explicitly. - pub class_ids: Option>, + pub class_ids: Option, } impl Boxes2D { @@ -264,97 +264,38 @@ impl ::re_types_core::Archetype for Boxes2D { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let half_sizes = { - let array = arrays_by_descr - .get(&Self::descriptor_half_sizes()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.Boxes2D#half_sizes")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#half_sizes")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#half_sizes")? - }; - let centers = if let Some(array) = arrays_by_descr.get(&Self::descriptor_centers()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#centers")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#centers")? - }) - } else { - None - }; - let colors = if let Some(array) = arrays_by_descr.get(&Self::descriptor_colors()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#colors")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#colors")? - }) - } else { - None - }; - let radii = if let Some(array) = arrays_by_descr.get(&Self::descriptor_radii()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#radii")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#radii")? - }) - } else { - None - }; - let labels = if let Some(array) = arrays_by_descr.get(&Self::descriptor_labels()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#labels")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#labels")? - }) - } else { - None - }; - let show_labels = if let Some(array) = arrays_by_descr.get(&Self::descriptor_show_labels()) - { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#show_labels")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let draw_order = if let Some(array) = arrays_by_descr.get(&Self::descriptor_draw_order()) { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#draw_order")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let class_ids = if let Some(array) = arrays_by_descr.get(&Self::descriptor_class_ids()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes2D#class_ids")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes2D#class_ids")? - }) - } else { - None - }; + let half_sizes = arrays_by_descr + .get(&Self::descriptor_half_sizes()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_half_sizes()) + }); + let centers = arrays_by_descr + .get(&Self::descriptor_centers()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_centers())); + let colors = arrays_by_descr + .get(&Self::descriptor_colors()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_colors())); + let radii = arrays_by_descr + .get(&Self::descriptor_radii()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_radii())); + let labels = arrays_by_descr + .get(&Self::descriptor_labels()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_labels())); + let show_labels = arrays_by_descr + .get(&Self::descriptor_show_labels()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_show_labels()) + }); + let draw_order = arrays_by_descr + .get(&Self::descriptor_draw_order()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_draw_order()) + }); + let class_ids = arrays_by_descr + .get(&Self::descriptor_class_ids()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_class_ids()) + }); Ok(Self { half_sizes, centers, @@ -369,73 +310,19 @@ impl ::re_types_core::Archetype for Boxes2D { } impl ::re_types_core::AsComponents for Boxes2D { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; [ - Some(Self::indicator()), - (Some(&self.half_sizes as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_half_sizes()), - } - }), - (self - .centers - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_centers()), - }), - (self - .colors - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_colors()), - }), - (self - .radii - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_radii()), - }), - (self - .labels - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_labels()), - }), - (self - .show_labels - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_show_labels()), - }), - (self - .draw_order - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_draw_order()), - }), - (self - .class_ids - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_class_ids()), - }), + Self::indicator().serialized(), + self.half_sizes.clone(), + self.centers.clone(), + self.colors.clone(), + self.radii.clone(), + self.labels.clone(), + self.show_labels.clone(), + self.draw_order.clone(), + self.class_ids.clone(), ] .into_iter() .flatten() @@ -452,7 +339,7 @@ impl Boxes2D { half_sizes: impl IntoIterator>, ) -> Self { Self { - half_sizes: half_sizes.into_iter().map(Into::into).collect(), + half_sizes: try_serialize_field(Self::descriptor_half_sizes(), half_sizes), centers: None, colors: None, radii: None, @@ -463,13 +350,147 @@ impl Boxes2D { } } + /// Update only some specific fields of a `Boxes2D`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `Boxes2D`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + half_sizes: Some(SerializedComponentBatch::new( + crate::components::HalfSize2D::arrow_empty(), + Self::descriptor_half_sizes(), + )), + centers: Some(SerializedComponentBatch::new( + crate::components::Position2D::arrow_empty(), + Self::descriptor_centers(), + )), + colors: Some(SerializedComponentBatch::new( + crate::components::Color::arrow_empty(), + Self::descriptor_colors(), + )), + radii: Some(SerializedComponentBatch::new( + crate::components::Radius::arrow_empty(), + Self::descriptor_radii(), + )), + labels: Some(SerializedComponentBatch::new( + crate::components::Text::arrow_empty(), + Self::descriptor_labels(), + )), + show_labels: Some(SerializedComponentBatch::new( + crate::components::ShowLabels::arrow_empty(), + Self::descriptor_show_labels(), + )), + draw_order: Some(SerializedComponentBatch::new( + crate::components::DrawOrder::arrow_empty(), + Self::descriptor_draw_order(), + )), + class_ids: Some(SerializedComponentBatch::new( + crate::components::ClassId::arrow_empty(), + Self::descriptor_class_ids(), + )), + } + } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [ + self.half_sizes + .map(|half_sizes| half_sizes.partitioned(_lengths.clone())) + .transpose()?, + self.centers + .map(|centers| centers.partitioned(_lengths.clone())) + .transpose()?, + self.colors + .map(|colors| colors.partitioned(_lengths.clone())) + .transpose()?, + self.radii + .map(|radii| radii.partitioned(_lengths.clone())) + .transpose()?, + self.labels + .map(|labels| labels.partitioned(_lengths.clone())) + .transpose()?, + self.show_labels + .map(|show_labels| show_labels.partitioned(_lengths.clone())) + .transpose()?, + self.draw_order + .map(|draw_order| draw_order.partitioned(_lengths.clone())) + .transpose()?, + self.class_ids + .map(|class_ids| class_ids.partitioned(_lengths.clone())) + .transpose()?, + ]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_half_sizes = self.half_sizes.as_ref().map(|b| b.array.len()); + let len_centers = self.centers.as_ref().map(|b| b.array.len()); + let len_colors = self.colors.as_ref().map(|b| b.array.len()); + let len_radii = self.radii.as_ref().map(|b| b.array.len()); + let len_labels = self.labels.as_ref().map(|b| b.array.len()); + let len_show_labels = self.show_labels.as_ref().map(|b| b.array.len()); + let len_draw_order = self.draw_order.as_ref().map(|b| b.array.len()); + let len_class_ids = self.class_ids.as_ref().map(|b| b.array.len()); + let len = None + .or(len_half_sizes) + .or(len_centers) + .or(len_colors) + .or(len_radii) + .or(len_labels) + .or(len_show_labels) + .or(len_draw_order) + .or(len_class_ids) + .unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// All half-extents that make up the batch of boxes. + #[inline] + pub fn with_half_sizes( + mut self, + half_sizes: impl IntoIterator>, + ) -> Self { + self.half_sizes = try_serialize_field(Self::descriptor_half_sizes(), half_sizes); + self + } + /// Optional center positions of the boxes. #[inline] pub fn with_centers( mut self, centers: impl IntoIterator>, ) -> Self { - self.centers = Some(centers.into_iter().map(Into::into).collect()); + self.centers = try_serialize_field(Self::descriptor_centers(), centers); self } @@ -479,7 +500,7 @@ impl Boxes2D { mut self, colors: impl IntoIterator>, ) -> Self { - self.colors = Some(colors.into_iter().map(Into::into).collect()); + self.colors = try_serialize_field(Self::descriptor_colors(), colors); self } @@ -489,7 +510,7 @@ impl Boxes2D { mut self, radii: impl IntoIterator>, ) -> Self { - self.radii = Some(radii.into_iter().map(Into::into).collect()); + self.radii = try_serialize_field(Self::descriptor_radii(), radii); self } @@ -502,7 +523,7 @@ impl Boxes2D { mut self, labels: impl IntoIterator>, ) -> Self { - self.labels = Some(labels.into_iter().map(Into::into).collect()); + self.labels = try_serialize_field(Self::descriptor_labels(), labels); self } @@ -512,7 +533,20 @@ impl Boxes2D { mut self, show_labels: impl Into, ) -> Self { - self.show_labels = Some(show_labels.into()); + self.show_labels = try_serialize_field(Self::descriptor_show_labels(), [show_labels]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::ShowLabels`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_show_labels`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_show_labels( + mut self, + show_labels: impl IntoIterator>, + ) -> Self { + self.show_labels = try_serialize_field(Self::descriptor_show_labels(), show_labels); self } @@ -523,7 +557,20 @@ impl Boxes2D { /// The default for 2D boxes is 10.0. #[inline] pub fn with_draw_order(mut self, draw_order: impl Into) -> Self { - self.draw_order = Some(draw_order.into()); + self.draw_order = try_serialize_field(Self::descriptor_draw_order(), [draw_order]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::DrawOrder`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_draw_order`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_draw_order( + mut self, + draw_order: impl IntoIterator>, + ) -> Self { + self.draw_order = try_serialize_field(Self::descriptor_draw_order(), draw_order); self } @@ -535,7 +582,7 @@ impl Boxes2D { mut self, class_ids: impl IntoIterator>, ) -> Self { - self.class_ids = Some(class_ids.into_iter().map(Into::into).collect()); + self.class_ids = try_serialize_field(Self::descriptor_class_ids(), class_ids); self } } @@ -552,16 +599,4 @@ impl ::re_byte_size::SizeBytes for Boxes2D { + self.draw_order.heap_size_bytes() + self.class_ids.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - >::is_pod() - && >>::is_pod() - && >>::is_pod() - && >>::is_pod() - && >>::is_pod() - && >::is_pod() - && >::is_pod() - && >>::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/boxes2d_ext.rs b/crates/store/re_types/src/archetypes/boxes2d_ext.rs index fcdeb0160c71..a7eec16b1331 100644 --- a/crates/store/re_types/src/archetypes/boxes2d_ext.rs +++ b/crates/store/re_types/src/archetypes/boxes2d_ext.rs @@ -50,29 +50,31 @@ impl Boxes2D { mins: impl IntoIterator>, sizes: impl IntoIterator>, ) -> Self { - let boxes = Self::from_sizes(sizes); + let half_sizes: Vec<_> = sizes + .into_iter() + .map(|size| { + let size = size.into(); + HalfSize2D::new(size.x() / 2.0, size.y() / 2.0) + }) + .collect(); // The box semantics are such that the last half-size is used for all remaining boxes. - if let Some(last_half_size) = boxes.half_sizes.last() { + if let Some(last_half_size) = half_sizes.last() { let centers: Vec<_> = mins .into_iter() - .zip( - boxes - .half_sizes - .iter() - .chain(std::iter::repeat(last_half_size)), - ) + .zip(half_sizes.iter().chain(std::iter::repeat(last_half_size))) .map(|(min, half_size)| { let min = min.into(); Position2D::new(min.x() + half_size.x(), min.y() + half_size.y()) }) .collect(); - boxes.with_centers(centers) + Self::from_half_sizes(half_sizes).with_centers(centers) } else { if mins.into_iter().next().is_some() { re_log::warn_once!("Must provide at least one size to create boxes."); } - boxes.with_centers(std::iter::empty::()) + Self::from_half_sizes(half_sizes) + .with_centers(std::iter::empty::()) } } } diff --git a/crates/store/re_types/src/archetypes/boxes3d.rs b/crates/store/re_types/src/archetypes/boxes3d.rs index 6c552c979962..00dbb04ac9fd 100644 --- a/crates/store/re_types/src/archetypes/boxes3d.rs +++ b/crates/store/re_types/src/archetypes/boxes3d.rs @@ -63,51 +63,51 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct Boxes3D { /// All half-extents that make up the batch of boxes. - pub half_sizes: Vec, + pub half_sizes: Option, /// Optional center positions of the boxes. /// /// If not specified, the centers will be at (0, 0, 0). /// Note that this uses a [`components::PoseTranslation3D`][crate::components::PoseTranslation3D] which is also used by [`archetypes::InstancePoses3D`][crate::archetypes::InstancePoses3D]. - pub centers: Option>, + pub centers: Option, /// Rotations via axis + angle. /// /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. /// Note that this uses a [`components::PoseRotationAxisAngle`][crate::components::PoseRotationAxisAngle] which is also used by [`archetypes::InstancePoses3D`][crate::archetypes::InstancePoses3D]. - pub rotation_axis_angles: Option>, + pub rotation_axis_angles: Option, /// Rotations via quaternion. /// /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. /// Note that this uses a [`components::PoseRotationQuat`][crate::components::PoseRotationQuat] which is also used by [`archetypes::InstancePoses3D`][crate::archetypes::InstancePoses3D]. - pub quaternions: Option>, + pub quaternions: Option, /// Optional colors for the boxes. - pub colors: Option>, + pub colors: Option, /// Optional radii for the lines that make up the boxes. - pub radii: Option>, + pub radii: Option, /// Optionally choose whether the boxes are drawn with lines or solid. - pub fill_mode: Option, + pub fill_mode: Option, /// Optional text labels for the boxes. /// /// If there's a single label present, it will be placed at the center of the entity. /// Otherwise, each instance will have its own label. - pub labels: Option>, + pub labels: Option, /// Optional choice of whether the text labels should be shown by default. - pub show_labels: Option, + pub show_labels: Option, /// Optional [`components::ClassId`][crate::components::ClassId]s for the boxes. /// /// The [`components::ClassId`][crate::components::ClassId] provides colors and labels if not specified explicitly. - pub class_ids: Option>, + pub class_ids: Option, } impl Boxes3D { @@ -318,123 +318,51 @@ impl ::re_types_core::Archetype for Boxes3D { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let half_sizes = { - let array = arrays_by_descr - .get(&Self::descriptor_half_sizes()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.Boxes3D#half_sizes")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#half_sizes")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#half_sizes")? - }; - let centers = if let Some(array) = arrays_by_descr.get(&Self::descriptor_centers()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#centers")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#centers")? - }) - } else { - None - }; - let rotation_axis_angles = - if let Some(array) = arrays_by_descr.get(&Self::descriptor_rotation_axis_angles()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#rotation_axis_angles")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#rotation_axis_angles")? - }) - } else { - None - }; - let quaternions = if let Some(array) = arrays_by_descr.get(&Self::descriptor_quaternions()) - { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#quaternions")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#quaternions")? - }) - } else { - None - }; - let colors = if let Some(array) = arrays_by_descr.get(&Self::descriptor_colors()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#colors")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#colors")? - }) - } else { - None - }; - let radii = if let Some(array) = arrays_by_descr.get(&Self::descriptor_radii()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#radii")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#radii")? - }) - } else { - None - }; - let fill_mode = if let Some(array) = arrays_by_descr.get(&Self::descriptor_fill_mode()) { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#fill_mode")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let labels = if let Some(array) = arrays_by_descr.get(&Self::descriptor_labels()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#labels")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#labels")? - }) - } else { - None - }; - let show_labels = if let Some(array) = arrays_by_descr.get(&Self::descriptor_show_labels()) - { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#show_labels")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let class_ids = if let Some(array) = arrays_by_descr.get(&Self::descriptor_class_ids()) { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#class_ids")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#class_ids")? - }) - } else { - None - }; + let half_sizes = arrays_by_descr + .get(&Self::descriptor_half_sizes()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_half_sizes()) + }); + let centers = arrays_by_descr + .get(&Self::descriptor_centers()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_centers())); + let rotation_axis_angles = arrays_by_descr + .get(&Self::descriptor_rotation_axis_angles()) + .map(|array| { + SerializedComponentBatch::new( + array.clone(), + Self::descriptor_rotation_axis_angles(), + ) + }); + let quaternions = arrays_by_descr + .get(&Self::descriptor_quaternions()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_quaternions()) + }); + let colors = arrays_by_descr + .get(&Self::descriptor_colors()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_colors())); + let radii = arrays_by_descr + .get(&Self::descriptor_radii()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_radii())); + let fill_mode = arrays_by_descr + .get(&Self::descriptor_fill_mode()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_fill_mode()) + }); + let labels = arrays_by_descr + .get(&Self::descriptor_labels()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_labels())); + let show_labels = arrays_by_descr + .get(&Self::descriptor_show_labels()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_show_labels()) + }); + let class_ids = arrays_by_descr + .get(&Self::descriptor_class_ids()) + .map(|array| { + SerializedComponentBatch::new(array.clone(), Self::descriptor_class_ids()) + }); Ok(Self { half_sizes, centers, @@ -451,89 +379,21 @@ impl ::re_types_core::Archetype for Boxes3D { } impl ::re_types_core::AsComponents for Boxes3D { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; [ - Some(Self::indicator()), - (Some(&self.half_sizes as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_half_sizes()), - } - }), - (self - .centers - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_centers()), - }), - (self - .rotation_axis_angles - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_rotation_axis_angles()), - }), - (self - .quaternions - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_quaternions()), - }), - (self - .colors - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_colors()), - }), - (self - .radii - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_radii()), - }), - (self - .fill_mode - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_fill_mode()), - }), - (self - .labels - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_labels()), - }), - (self - .show_labels - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_show_labels()), - }), - (self - .class_ids - .as_ref() - .map(|comp_batch| (comp_batch as &dyn ComponentBatch))) - .map(|batch| ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_class_ids()), - }), + Self::indicator().serialized(), + self.half_sizes.clone(), + self.centers.clone(), + self.rotation_axis_angles.clone(), + self.quaternions.clone(), + self.colors.clone(), + self.radii.clone(), + self.fill_mode.clone(), + self.labels.clone(), + self.show_labels.clone(), + self.class_ids.clone(), ] .into_iter() .flatten() @@ -550,7 +410,7 @@ impl Boxes3D { half_sizes: impl IntoIterator>, ) -> Self { Self { - half_sizes: half_sizes.into_iter().map(Into::into).collect(), + half_sizes: try_serialize_field(Self::descriptor_half_sizes(), half_sizes), centers: None, rotation_axis_angles: None, quaternions: None, @@ -563,6 +423,158 @@ impl Boxes3D { } } + /// Update only some specific fields of a `Boxes3D`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `Boxes3D`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + half_sizes: Some(SerializedComponentBatch::new( + crate::components::HalfSize3D::arrow_empty(), + Self::descriptor_half_sizes(), + )), + centers: Some(SerializedComponentBatch::new( + crate::components::PoseTranslation3D::arrow_empty(), + Self::descriptor_centers(), + )), + rotation_axis_angles: Some(SerializedComponentBatch::new( + crate::components::PoseRotationAxisAngle::arrow_empty(), + Self::descriptor_rotation_axis_angles(), + )), + quaternions: Some(SerializedComponentBatch::new( + crate::components::PoseRotationQuat::arrow_empty(), + Self::descriptor_quaternions(), + )), + colors: Some(SerializedComponentBatch::new( + crate::components::Color::arrow_empty(), + Self::descriptor_colors(), + )), + radii: Some(SerializedComponentBatch::new( + crate::components::Radius::arrow_empty(), + Self::descriptor_radii(), + )), + fill_mode: Some(SerializedComponentBatch::new( + crate::components::FillMode::arrow_empty(), + Self::descriptor_fill_mode(), + )), + labels: Some(SerializedComponentBatch::new( + crate::components::Text::arrow_empty(), + Self::descriptor_labels(), + )), + show_labels: Some(SerializedComponentBatch::new( + crate::components::ShowLabels::arrow_empty(), + Self::descriptor_show_labels(), + )), + class_ids: Some(SerializedComponentBatch::new( + crate::components::ClassId::arrow_empty(), + Self::descriptor_class_ids(), + )), + } + } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [ + self.half_sizes + .map(|half_sizes| half_sizes.partitioned(_lengths.clone())) + .transpose()?, + self.centers + .map(|centers| centers.partitioned(_lengths.clone())) + .transpose()?, + self.rotation_axis_angles + .map(|rotation_axis_angles| rotation_axis_angles.partitioned(_lengths.clone())) + .transpose()?, + self.quaternions + .map(|quaternions| quaternions.partitioned(_lengths.clone())) + .transpose()?, + self.colors + .map(|colors| colors.partitioned(_lengths.clone())) + .transpose()?, + self.radii + .map(|radii| radii.partitioned(_lengths.clone())) + .transpose()?, + self.fill_mode + .map(|fill_mode| fill_mode.partitioned(_lengths.clone())) + .transpose()?, + self.labels + .map(|labels| labels.partitioned(_lengths.clone())) + .transpose()?, + self.show_labels + .map(|show_labels| show_labels.partitioned(_lengths.clone())) + .transpose()?, + self.class_ids + .map(|class_ids| class_ids.partitioned(_lengths.clone())) + .transpose()?, + ]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_half_sizes = self.half_sizes.as_ref().map(|b| b.array.len()); + let len_centers = self.centers.as_ref().map(|b| b.array.len()); + let len_rotation_axis_angles = self.rotation_axis_angles.as_ref().map(|b| b.array.len()); + let len_quaternions = self.quaternions.as_ref().map(|b| b.array.len()); + let len_colors = self.colors.as_ref().map(|b| b.array.len()); + let len_radii = self.radii.as_ref().map(|b| b.array.len()); + let len_fill_mode = self.fill_mode.as_ref().map(|b| b.array.len()); + let len_labels = self.labels.as_ref().map(|b| b.array.len()); + let len_show_labels = self.show_labels.as_ref().map(|b| b.array.len()); + let len_class_ids = self.class_ids.as_ref().map(|b| b.array.len()); + let len = None + .or(len_half_sizes) + .or(len_centers) + .or(len_rotation_axis_angles) + .or(len_quaternions) + .or(len_colors) + .or(len_radii) + .or(len_fill_mode) + .or(len_labels) + .or(len_show_labels) + .or(len_class_ids) + .unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// All half-extents that make up the batch of boxes. + #[inline] + pub fn with_half_sizes( + mut self, + half_sizes: impl IntoIterator>, + ) -> Self { + self.half_sizes = try_serialize_field(Self::descriptor_half_sizes(), half_sizes); + self + } + /// Optional center positions of the boxes. /// /// If not specified, the centers will be at (0, 0, 0). @@ -572,7 +584,7 @@ impl Boxes3D { mut self, centers: impl IntoIterator>, ) -> Self { - self.centers = Some(centers.into_iter().map(Into::into).collect()); + self.centers = try_serialize_field(Self::descriptor_centers(), centers); self } @@ -587,8 +599,10 @@ impl Boxes3D { Item = impl Into, >, ) -> Self { - self.rotation_axis_angles = - Some(rotation_axis_angles.into_iter().map(Into::into).collect()); + self.rotation_axis_angles = try_serialize_field( + Self::descriptor_rotation_axis_angles(), + rotation_axis_angles, + ); self } @@ -601,7 +615,7 @@ impl Boxes3D { mut self, quaternions: impl IntoIterator>, ) -> Self { - self.quaternions = Some(quaternions.into_iter().map(Into::into).collect()); + self.quaternions = try_serialize_field(Self::descriptor_quaternions(), quaternions); self } @@ -611,7 +625,7 @@ impl Boxes3D { mut self, colors: impl IntoIterator>, ) -> Self { - self.colors = Some(colors.into_iter().map(Into::into).collect()); + self.colors = try_serialize_field(Self::descriptor_colors(), colors); self } @@ -621,14 +635,27 @@ impl Boxes3D { mut self, radii: impl IntoIterator>, ) -> Self { - self.radii = Some(radii.into_iter().map(Into::into).collect()); + self.radii = try_serialize_field(Self::descriptor_radii(), radii); self } /// Optionally choose whether the boxes are drawn with lines or solid. #[inline] pub fn with_fill_mode(mut self, fill_mode: impl Into) -> Self { - self.fill_mode = Some(fill_mode.into()); + self.fill_mode = try_serialize_field(Self::descriptor_fill_mode(), [fill_mode]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::FillMode`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_fill_mode`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_fill_mode( + mut self, + fill_mode: impl IntoIterator>, + ) -> Self { + self.fill_mode = try_serialize_field(Self::descriptor_fill_mode(), fill_mode); self } @@ -641,7 +668,7 @@ impl Boxes3D { mut self, labels: impl IntoIterator>, ) -> Self { - self.labels = Some(labels.into_iter().map(Into::into).collect()); + self.labels = try_serialize_field(Self::descriptor_labels(), labels); self } @@ -651,7 +678,20 @@ impl Boxes3D { mut self, show_labels: impl Into, ) -> Self { - self.show_labels = Some(show_labels.into()); + self.show_labels = try_serialize_field(Self::descriptor_show_labels(), [show_labels]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::ShowLabels`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_show_labels`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_show_labels( + mut self, + show_labels: impl IntoIterator>, + ) -> Self { + self.show_labels = try_serialize_field(Self::descriptor_show_labels(), show_labels); self } @@ -663,7 +703,7 @@ impl Boxes3D { mut self, class_ids: impl IntoIterator>, ) -> Self { - self.class_ids = Some(class_ids.into_iter().map(Into::into).collect()); + self.class_ids = try_serialize_field(Self::descriptor_class_ids(), class_ids); self } } @@ -682,18 +722,4 @@ impl ::re_byte_size::SizeBytes for Boxes3D { + self.show_labels.heap_size_bytes() + self.class_ids.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - >::is_pod() - && >>::is_pod() - && >>::is_pod() - && >>::is_pod() - && >>::is_pod() - && >>::is_pod() - && >::is_pod() - && >>::is_pod() - && >::is_pod() - && >>::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/boxes3d_ext.rs b/crates/store/re_types/src/archetypes/boxes3d_ext.rs index 39df661d7958..a3d3ac3e7fe0 100644 --- a/crates/store/re_types/src/archetypes/boxes3d_ext.rs +++ b/crates/store/re_types/src/archetypes/boxes3d_ext.rs @@ -27,8 +27,8 @@ impl Boxes3D { #[inline] pub fn from_sizes(sizes: impl IntoIterator>) -> Self { Self::new(sizes.into_iter().map(|size| { - let wh = size.into(); - HalfSize3D::new(wh.x() / 2.0, wh.y() / 2.0, wh.z() / 2.0) + let size = size.into(); + HalfSize3D::new(size.x() / 2.0, size.y() / 2.0, size.z() / 2.0) })) } @@ -50,18 +50,19 @@ impl Boxes3D { mins: impl IntoIterator>, sizes: impl IntoIterator>, ) -> Self { - let boxes = Self::from_sizes(sizes); + let half_sizes: Vec<_> = sizes + .into_iter() + .map(|size| { + let size = size.into(); + HalfSize3D::new(size.x() / 2.0, size.y() / 2.0, size.z() / 2.0) + }) + .collect(); // The box semantics are such that the last half-size is used for all remaining boxes. - if let Some(last_half_size) = boxes.half_sizes.last() { + if let Some(last_half_size) = half_sizes.last() { let centers: Vec<_> = mins .into_iter() - .zip( - boxes - .half_sizes - .iter() - .chain(std::iter::repeat(last_half_size)), - ) + .zip(half_sizes.iter().chain(std::iter::repeat(last_half_size))) .map(|(min, half_size)| { let min = min.into(); PoseTranslation3D::new( @@ -71,12 +72,13 @@ impl Boxes3D { ) }) .collect(); - boxes.with_centers(centers) + Self::from_half_sizes(half_sizes).with_centers(centers) } else { if mins.into_iter().next().is_some() { re_log::warn_once!("Must provide at least one size to create boxes."); } - boxes.with_centers(std::iter::empty::()) + Self::from_half_sizes(half_sizes) + .with_centers(std::iter::empty::()) } } } diff --git a/crates/store/re_types/src/archetypes/transform3d.rs b/crates/store/re_types/src/archetypes/transform3d.rs index 97cec2130ff3..05750fd71694 100644 --- a/crates/store/re_types/src/archetypes/transform3d.rs +++ b/crates/store/re_types/src/archetypes/transform3d.rs @@ -84,7 +84,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// rec.set_time_seconds("sim_time", 0.0); /// /// // Planetary motion is typically in the XY plane. -/// rec.log_static("/", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; +/// rec.log_static("/", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; /// /// // Setup points, all are in the center of their own space: /// rec.log( diff --git a/crates/store/re_types/src/archetypes/view_coordinates.rs b/crates/store/re_types/src/archetypes/view_coordinates.rs index 50bd3297c176..49611969a09d 100644 --- a/crates/store/re_types/src/archetypes/view_coordinates.rs +++ b/crates/store/re_types/src/archetypes/view_coordinates.rs @@ -38,7 +38,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// fn main() -> Result<(), Box> { /// let rec = rerun::RecordingStreamBuilder::new("rerun_example_view_coordinates").spawn()?; /// -/// rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; // Set an up-axis +/// rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; // Set an up-axis /// rec.log( /// "world/xyz", /// &rerun::Arrows3D::from_vectors( @@ -59,11 +59,11 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Clone, Debug, PartialEq, Default)] #[repr(transparent)] pub struct ViewCoordinates { /// The directions of the [x, y, z] axes. - pub xyz: crate::components::ViewCoordinates, + pub xyz: Option, } impl ViewCoordinates { @@ -159,39 +159,21 @@ impl ::re_types_core::Archetype for ViewCoordinates { re_tracing::profile_function!(); use ::re_types_core::{Loggable as _, ResultExt as _}; let arrays_by_descr: ::nohash_hasher::IntMap<_, _> = arrow_data.into_iter().collect(); - let xyz = { - let array = arrays_by_descr - .get(&Self::descriptor_xyz()) - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.ViewCoordinates#xyz")?; - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.ViewCoordinates#xyz")? - .into_iter() - .next() - .flatten() - .ok_or_else(DeserializationError::missing_data) - .with_context("rerun.archetypes.ViewCoordinates#xyz")? - }; + let xyz = arrays_by_descr + .get(&Self::descriptor_xyz()) + .map(|array| SerializedComponentBatch::new(array.clone(), Self::descriptor_xyz())); Ok(Self { xyz }) } } impl ::re_types_core::AsComponents for ViewCoordinates { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); + #[inline] + fn as_serialized_batches(&self) -> Vec { use ::re_types_core::Archetype as _; - [ - Some(Self::indicator()), - (Some(&self.xyz as &dyn ComponentBatch)).map(|batch| { - ::re_types_core::ComponentBatchCowWithDescriptor { - batch: batch.into(), - descriptor_override: Some(Self::descriptor_xyz()), - } - }), - ] - .into_iter() - .flatten() - .collect() + [Self::indicator().serialized(), self.xyz.clone()] + .into_iter() + .flatten() + .collect() } } @@ -201,7 +183,87 @@ impl ViewCoordinates { /// Create a new `ViewCoordinates`. #[inline] pub fn new(xyz: impl Into) -> Self { - Self { xyz: xyz.into() } + Self { + xyz: try_serialize_field(Self::descriptor_xyz(), [xyz]), + } + } + + /// Update only some specific fields of a `ViewCoordinates`. + #[inline] + pub fn update_fields() -> Self { + Self::default() + } + + /// Clear all the fields of a `ViewCoordinates`. + #[inline] + pub fn clear_fields() -> Self { + use ::re_types_core::Loggable as _; + Self { + xyz: Some(SerializedComponentBatch::new( + crate::components::ViewCoordinates::arrow_empty(), + Self::descriptor_xyz(), + )), + } + } + + /// Partitions the component data into multiple sub-batches. + /// + /// Specifically, this transforms the existing [`SerializedComponentBatch`]es data into [`SerializedComponentColumn`]s + /// instead, via [`SerializedComponentBatch::partitioned`]. + /// + /// This makes it possible to use `RecordingStream::send_columns` to send columnar data directly into Rerun. + /// + /// The specified `lengths` must sum to the total length of the component batch. + /// + /// [`SerializedComponentColumn`]: [::re_types_core::SerializedComponentColumn] + #[inline] + pub fn columns( + self, + _lengths: I, + ) -> SerializationResult> + where + I: IntoIterator + Clone, + { + let columns = [self + .xyz + .map(|xyz| xyz.partitioned(_lengths.clone())) + .transpose()?]; + let indicator_column = + ::re_types_core::indicator_column::(_lengths.into_iter().count())?; + Ok(columns.into_iter().chain([indicator_column]).flatten()) + } + + /// Helper to partition the component data into unit-length sub-batches. + /// + /// This is semantically similar to calling [`Self::columns`] with `std::iter::take(1).repeat(n)`, + /// where `n` is automatically guessed. + #[inline] + pub fn columns_of_unit_batches( + self, + ) -> SerializationResult> { + let len_xyz = self.xyz.as_ref().map(|b| b.array.len()); + let len = None.or(len_xyz).unwrap_or(0); + self.columns(std::iter::repeat(1).take(len)) + } + + /// The directions of the [x, y, z] axes. + #[inline] + pub fn with_xyz(mut self, xyz: impl Into) -> Self { + self.xyz = try_serialize_field(Self::descriptor_xyz(), [xyz]); + self + } + + /// This method makes it possible to pack multiple [`crate::components::ViewCoordinates`] in a single component batch. + /// + /// This only makes sense when used in conjunction with [`Self::columns`]. [`Self::with_xyz`] should + /// be used when logging a single row's worth of data. + #[inline] + pub fn with_many_xyz( + mut self, + xyz: impl IntoIterator>, + ) -> Self { + self.xyz = try_serialize_field(Self::descriptor_xyz(), xyz); + self } } @@ -210,9 +272,4 @@ impl ::re_byte_size::SizeBytes for ViewCoordinates { fn heap_size_bytes(&self) -> u64 { self.xyz.heap_size_bytes() } - - #[inline] - fn is_pod() -> bool { - ::is_pod() - } } diff --git a/crates/store/re_types/src/archetypes/view_coordinates_ext.rs b/crates/store/re_types/src/archetypes/view_coordinates_ext.rs index 3961280ab492..098c63b24974 100644 --- a/crates/store/re_types/src/archetypes/view_coordinates_ext.rs +++ b/crates/store/re_types/src/archetypes/view_coordinates_ext.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use crate::{components, view_coordinates::ViewDir}; use super::ViewCoordinates; @@ -5,9 +7,13 @@ use super::ViewCoordinates; macro_rules! define_coordinates { ($docstring:literal, $name:ident => ($x:ident, $y:ident, $z:ident) ) => { #[doc = $docstring] - pub const $name: Self = Self { - xyz: components::ViewCoordinates::new(ViewDir::$x, ViewDir::$y, ViewDir::$z), - }; + pub fn $name() -> Self { + Self::new(components::ViewCoordinates::new( + ViewDir::$x, + ViewDir::$y, + ViewDir::$z, + )) + } }; } diff --git a/crates/store/re_types/tests/types/asset3d.rs b/crates/store/re_types/tests/types/asset3d.rs index cf91ae69781e..a89cb0f23020 100644 --- a/crates/store/re_types/tests/types/asset3d.rs +++ b/crates/store/re_types/tests/types/asset3d.rs @@ -1,8 +1,8 @@ use re_types::{ archetypes::Asset3D, - components::{Blob, MediaType}, + components::{AlbedoFactor, Blob, MediaType}, datatypes::{Rgba32, Utf8}, - Archetype as _, AsComponents as _, + Archetype as _, AsComponents as _, ComponentBatch, }; #[test] @@ -10,9 +10,19 @@ fn roundtrip() { const BYTES: &[u8] = &[1, 2, 3, 4, 5, 6]; let expected = Asset3D { - blob: Blob(BYTES.to_vec().into()), - media_type: Some(MediaType(Utf8(MediaType::GLTF.into()))), - albedo_factor: Some(Rgba32::from_unmultiplied_rgba(0xEE, 0x11, 0x22, 0x33).into()), + blob: Blob(BYTES.to_vec().into()) + .serialized() + .map(|blob| blob.with_descriptor_override(Asset3D::descriptor_blob())), + media_type: MediaType(Utf8(MediaType::GLTF.into())) + .serialized() + .map(|media_type| { + media_type.with_descriptor_override(Asset3D::descriptor_media_type()) + }), + albedo_factor: AlbedoFactor(Rgba32::from_unmultiplied_rgba(0xEE, 0x11, 0x22, 0x33)) + .serialized() + .map(|albedo_factor| { + albedo_factor.with_descriptor_override(Asset3D::descriptor_albedo_factor()) + }), }; let arch = Asset3D::from_file_contents(BYTES.to_vec(), Some(MediaType::gltf())) diff --git a/crates/store/re_types/tests/types/box2d.rs b/crates/store/re_types/tests/types/box2d.rs index 7a484c608dec..9af12392e8c1 100644 --- a/crates/store/re_types/tests/types/box2d.rs +++ b/crates/store/re_types/tests/types/box2d.rs @@ -1,34 +1,52 @@ -use re_types::{archetypes::Boxes2D, components, Archetype as _, AsComponents as _}; +use re_types::{ + archetypes::Boxes2D, components, Archetype as _, AsComponents as _, ComponentBatch as _, +}; #[test] fn roundtrip() { let expected = Boxes2D { half_sizes: vec![ - components::HalfSize2D::new(1.0, 2.0), // + components::HalfSize2D::new(1.0, 2.0), components::HalfSize2D::new(3.0, 4.0), - ], - centers: Some(vec![ - components::Position2D::new(1.0, 2.0), // + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_half_sizes())), + centers: vec![ + components::Position2D::new(1.0, 2.0), components::Position2D::new(3.0, 4.0), - ]), - colors: Some(vec![ - components::Color::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC), // + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_centers())), + colors: vec![ + components::Color::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC), components::Color::from_unmultiplied_rgba(0x00, 0xBB, 0x00, 0xDD), - ]), - radii: Some(vec![ - components::Radius::from(42.0), // + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_colors())), + radii: vec![ + components::Radius::from(42.0), components::Radius::from(43.0), - ]), - labels: Some(vec![ - "hello".into(), // - "friend".into(), // - ]), - draw_order: Some(components::DrawOrder(300.0.into())), - class_ids: Some(vec![ - components::ClassId::from(126), // - components::ClassId::from(127), // - ]), - show_labels: Some(true.into()), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_radii())), + labels: vec![ + components::Text::from("hello"), + components::Text::from("friend"), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_labels())), + draw_order: components::DrawOrder(300.0.into()) + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_draw_order())), + class_ids: vec![ + components::ClassId::from(126), + components::ClassId::from(127), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_class_ids())), + show_labels: components::ShowLabels(true.into()) + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes2D::descriptor_show_labels())), }; let arch = Boxes2D::from_half_sizes([(1.0, 2.0), (3.0, 4.0)]) diff --git a/crates/store/re_types/tests/types/box3d.rs b/crates/store/re_types/tests/types/box3d.rs index ea9b465c1aaa..89aec1f853d4 100644 --- a/crates/store/re_types/tests/types/box3d.rs +++ b/crates/store/re_types/tests/types/box3d.rs @@ -1,4 +1,6 @@ -use re_types::{archetypes::Boxes3D, components, datatypes, Archetype as _, AsComponents as _}; +use re_types::{ + archetypes::Boxes3D, components, datatypes, Archetype as _, AsComponents as _, ComponentBatch, +}; #[test] fn roundtrip() { @@ -6,37 +8,56 @@ fn roundtrip() { half_sizes: vec![ components::HalfSize3D::new(1.0, 2.0, 3.0), // components::HalfSize3D::new(4.0, 5.0, 6.0), - ], - centers: Some(vec![ + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_half_sizes())), + centers: vec![ components::PoseTranslation3D::new(1.0, 2.0, 3.0), // components::PoseTranslation3D::new(4.0, 5.0, 6.0), - ]), - quaternions: Some(vec![ - datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0]).into() - ]), - rotation_axis_angles: Some(vec![datatypes::RotationAxisAngle::new( + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_centers())), + quaternions: vec![components::PoseRotationQuat::from( + datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0]), + )] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_quaternions())), + rotation_axis_angles: vec![components::PoseRotationAxisAngle::new( [1.0, 2.0, 3.0], datatypes::Angle::from_radians(4.0), - ) - .into()]), - colors: Some(vec![ - components::Color::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC), // + )] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_rotation_axis_angles())), + colors: vec![ + components::Color::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC), components::Color::from_unmultiplied_rgba(0x00, 0xBB, 0x00, 0xDD), - ]), - radii: Some(vec![ - components::Radius::from(42.0), // + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_colors())), + radii: vec![ + components::Radius::from(42.0), components::Radius::from(43.0), - ]), - fill_mode: Some(components::FillMode::Solid), - labels: Some(vec![ - "hello".into(), // - "friend".into(), // - ]), - class_ids: Some(vec![ - components::ClassId::from(126), // - components::ClassId::from(127), // - ]), - show_labels: Some(false.into()), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_radii())), + fill_mode: components::FillMode::Solid + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_fill_mode())), + labels: vec![ + components::Text::from("hello"), + components::Text::from("friend"), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_labels())), + class_ids: vec![ + components::ClassId::from(126), + components::ClassId::from(127), + ] + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_class_ids())), + show_labels: components::ShowLabels(false.into()) + .serialized() + .map(|batch| batch.with_descriptor_override(Boxes3D::descriptor_show_labels())), }; let arch = Boxes3D::from_half_sizes([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]) diff --git a/crates/store/re_types/tests/types/view_coordinates.rs b/crates/store/re_types/tests/types/view_coordinates.rs index d1d01c7c75d2..3d777cbca3ea 100644 --- a/crates/store/re_types/tests/types/view_coordinates.rs +++ b/crates/store/re_types/tests/types/view_coordinates.rs @@ -1,15 +1,17 @@ use re_types::{ archetypes::ViewCoordinates, components, view_coordinates::ViewDir, Archetype as _, - AsComponents as _, + AsComponents as _, ComponentBatch, }; #[test] fn roundtrip() { let expected = ViewCoordinates { - xyz: components::ViewCoordinates::new(ViewDir::Right, ViewDir::Down, ViewDir::Forward), + xyz: components::ViewCoordinates::new(ViewDir::Right, ViewDir::Down, ViewDir::Forward) + .serialized() + .map(|xyz| xyz.with_descriptor_override(ViewCoordinates::descriptor_xyz())), }; - let arch = ViewCoordinates::RDF; + let arch = ViewCoordinates::RDF(); similar_asserts::assert_eq!(expected, arch); @@ -34,8 +36,10 @@ fn view_coordinates() { use glam::{vec3, Mat3}; use re_types::view_coordinates::{Handedness, SignedAxis3}; - assert_eq!(ViewCoordinates::RUB.xyz.to_rub(), Mat3::IDENTITY); - assert_eq!(ViewCoordinates::RUB.xyz.from_rub(), Mat3::IDENTITY); + let rub_component = + components::ViewCoordinates::new(ViewDir::Right, ViewDir::Up, ViewDir::Back); + assert_eq!(rub_component.to_rub(), Mat3::IDENTITY); + assert_eq!(rub_component.from_rub(), Mat3::IDENTITY); { assert!("UUDDLRLRBAStart" @@ -46,7 +50,7 @@ fn view_coordinates() { let rub = "RUB".parse::().unwrap(); let bru = "BRU".parse::().unwrap(); - assert_eq!(rub, ViewCoordinates::RUB.xyz); + assert_eq!(rub, rub_component); assert_eq!(rub.to_rub(), Mat3::IDENTITY); assert_eq!( diff --git a/crates/viewer/re_view_spatial/src/mesh_cache.rs b/crates/viewer/re_view_spatial/src/mesh_cache.rs index 9da0b806a3b5..b3fc594f2ad1 100644 --- a/crates/viewer/re_view_spatial/src/mesh_cache.rs +++ b/crates/viewer/re_view_spatial/src/mesh_cache.rs @@ -10,7 +10,7 @@ use re_renderer::RenderContext; use re_types::{components::MediaType, Component as _}; use re_viewer_context::Cache; -use crate::mesh_loader::LoadedMesh; +use crate::mesh_loader::{LoadedMesh, NativeAsset3D}; // ---------------------------------------------------------------------------- @@ -34,10 +34,10 @@ pub struct MeshCacheKey { pub struct MeshCache(HashMap>>>); /// Either a [`re_types::archetypes::Asset3D`] or [`re_types::archetypes::Mesh3D`] to be cached. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum AnyMesh<'a> { Asset { - asset: &'a re_types::archetypes::Asset3D, + asset: NativeAsset3D<'a>, }, Mesh { mesh: &'a re_types::archetypes::Mesh3D, diff --git a/crates/viewer/re_view_spatial/src/mesh_loader.rs b/crates/viewer/re_view_spatial/src/mesh_loader.rs index 5b4c3c594131..45e8a3860272 100644 --- a/crates/viewer/re_view_spatial/src/mesh_loader.rs +++ b/crates/viewer/re_view_spatial/src/mesh_loader.rs @@ -1,14 +1,18 @@ use itertools::Itertools; use re_chunk_store::RowId; use re_renderer::{mesh::GpuMesh, RenderContext, Rgba32Unmul}; -use re_types::{ - archetypes::{Asset3D, Mesh3D}, - components::{AlbedoFactor, MediaType}, -}; +use re_types::{archetypes::Mesh3D, components::MediaType}; use re_viewer_context::{gpu_bridge::texture_creation_desc_from_color_image, ImageInfo}; use crate::mesh_cache::AnyMesh; +#[derive(Debug, Clone)] +pub struct NativeAsset3D<'a> { + pub bytes: &'a [u8], + pub media_type: Option, + pub albedo_factor: Option, +} + pub struct LoadedMesh { name: String, @@ -34,15 +38,22 @@ impl LoadedMesh { } } - pub fn load_asset3d_parts( + fn load_asset3d( name: String, - media_type: &MediaType, - bytes: &[u8], + asset: NativeAsset3D<'_>, render_ctx: &RenderContext, - albedo_factor: &Option, ) -> anyhow::Result { re_tracing::profile_function!(); + let NativeAsset3D { + bytes, + media_type, + albedo_factor, + } = asset; + + let media_type = MediaType::or_guess_from_data(media_type, bytes) + .ok_or_else(|| anyhow::anyhow!("couldn't guess media type"))?; + let mut cpu_model = match media_type.as_str() { MediaType::GLTF | MediaType::GLB => { re_renderer::importer::gltf::load_gltf_from_buffer(&name, bytes, render_ctx)? @@ -56,7 +67,7 @@ impl LoadedMesh { if let Some(albedo_factor) = albedo_factor { for instance in &cpu_model.instances { for material in &mut cpu_model.meshes[instance.mesh].materials { - material.albedo_factor = albedo_factor.0.into(); + material.albedo_factor = albedo_factor; } } } @@ -71,32 +82,6 @@ impl LoadedMesh { }) } - fn load_asset3d( - name: String, - asset3d: &Asset3D, - render_ctx: &RenderContext, - ) -> anyhow::Result { - re_tracing::profile_function!(); - - let Asset3D { - blob, - media_type, - albedo_factor, - } = asset3d; - - let media_type = MediaType::or_guess_from_data(media_type.clone(), blob.as_slice()) - .ok_or_else(|| anyhow::anyhow!("couldn't guess media type"))?; - let slf = Self::load_asset3d_parts( - name, - &media_type, - blob.as_slice(), - render_ctx, - albedo_factor, - )?; - - Ok(slf) - } - fn load_mesh3d( name: String, mesh3d: &Mesh3D, diff --git a/crates/viewer/re_view_spatial/src/transform_cache.rs b/crates/viewer/re_view_spatial/src/transform_cache.rs index 8fa9b773b3aa..add872d14567 100644 --- a/crates/viewer/re_view_spatial/src/transform_cache.rs +++ b/crates/viewer/re_view_spatial/src/transform_cache.rs @@ -1193,7 +1193,7 @@ mod tests { .with_archetype( RowId::new(), [(timeline, 1)], - &archetypes::ViewCoordinates::BLU, + &archetypes::ViewCoordinates::BLU(), ) .build() .unwrap(); @@ -1265,7 +1265,7 @@ mod tests { .with_archetype( RowId::new(), TimePoint::default(), - &archetypes::ViewCoordinates::BRU, + &archetypes::ViewCoordinates::BRU(), ) .build() .unwrap(); @@ -1274,7 +1274,7 @@ mod tests { .with_archetype( RowId::new(), TimePoint::default(), - &archetypes::ViewCoordinates::BLU, + &archetypes::ViewCoordinates::BLU(), ) .build() .unwrap(); @@ -1523,7 +1523,7 @@ mod tests { .with_archetype( RowId::new(), [(timeline, 3)], - &archetypes::ViewCoordinates::BLU, + &archetypes::ViewCoordinates::BLU(), ) // Clear out the pinhole projection (this should yield nothing then for the remaining view coordinates.) .with_serialized_batch( diff --git a/crates/viewer/re_view_spatial/src/visualizers/assets3d.rs b/crates/viewer/re_view_spatial/src/visualizers/assets3d.rs index 4e74bbf14e60..30250868ace2 100644 --- a/crates/viewer/re_view_spatial/src/visualizers/assets3d.rs +++ b/crates/viewer/re_view_spatial/src/visualizers/assets3d.rs @@ -69,10 +69,10 @@ impl Asset3DVisualizer { &entity_path.to_string(), key.clone(), AnyMesh::Asset { - asset: &Asset3D { - blob: data.blob.clone().into(), + asset: crate::mesh_loader::NativeAsset3D { + bytes: data.blob.as_slice(), media_type: data.media_type.clone().map(Into::into), - albedo_factor: data.albedo_factor.copied(), + albedo_factor: data.albedo_factor.map(|a| a.0.into()), }, }, ctx.viewer_ctx.render_ctx, diff --git a/docs/content/reference/migration/migration-0-22.md b/docs/content/reference/migration/migration-0-22.md index 09806a9e8e56..f717ff95b301 100644 --- a/docs/content/reference/migration/migration-0-22.md +++ b/docs/content/reference/migration/migration-0-22.md @@ -25,3 +25,10 @@ It was no longer used for several releases, so we removed it. **Note**: although `rr.log_components()` is technically a public API, it is undocumented, and we discourage using it. For logging custom components, use [`rr.AnyValue`](https://ref.rerun.io/docs/python/main/common/custom_data/#rerun.AnyValues) and [`rr.AnyBatchValue`](https://ref.rerun.io/docs/python/main/common/custom_data/#rerun.AnyBatchValue). + + +### Rust's `ViewCoordinates` archetype now has static methods instead of constants + +As part of the switch to "eager archetype serialization" (serialization of archetype components now occurs at time of archetype instantiation rather than logging), we can no longer offer constants for the `ViewCoordinates` archetype like `ViewCoordinates::RUB`. + +Instead, there's now methods with the same name, i.e. `ViewCoordinates::RUB()`. diff --git a/docs/snippets/all/archetypes/asset3d_simple.rs b/docs/snippets/all/archetypes/asset3d_simple.rs index b379271ae2fd..fc8dc2bace76 100644 --- a/docs/snippets/all/archetypes/asset3d_simple.rs +++ b/docs/snippets/all/archetypes/asset3d_simple.rs @@ -10,7 +10,7 @@ fn main() -> anyhow::Result<()> { let rec = rerun::RecordingStreamBuilder::new("rerun_example_asset3d").spawn()?; - rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; // Set an up-axis + rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; // Set an up-axis rec.log("world/asset", &rerun::Asset3D::from_file(path)?)?; Ok(()) diff --git a/docs/snippets/all/archetypes/transform3d_hierarchy.rs b/docs/snippets/all/archetypes/transform3d_hierarchy.rs index cd3cda58582a..5fcfaeab44da 100644 --- a/docs/snippets/all/archetypes/transform3d_hierarchy.rs +++ b/docs/snippets/all/archetypes/transform3d_hierarchy.rs @@ -8,7 +8,7 @@ fn main() -> Result<(), Box> { rec.set_time_seconds("sim_time", 0.0); // Planetary motion is typically in the XY plane. - rec.log_static("/", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; + rec.log_static("/", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; // Setup points, all are in the center of their own space: rec.log( diff --git a/docs/snippets/all/archetypes/view_coordinates_simple.rs b/docs/snippets/all/archetypes/view_coordinates_simple.rs index 1969b3b839fd..871891825e14 100644 --- a/docs/snippets/all/archetypes/view_coordinates_simple.rs +++ b/docs/snippets/all/archetypes/view_coordinates_simple.rs @@ -3,7 +3,7 @@ fn main() -> Result<(), Box> { let rec = rerun::RecordingStreamBuilder::new("rerun_example_view_coordinates").spawn()?; - rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; // Set an up-axis + rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP())?; // Set an up-axis rec.log( "world/xyz", &rerun::Arrows3D::from_vectors( diff --git a/examples/rust/clock/src/main.rs b/examples/rust/clock/src/main.rs index a39cefd79391..2d31582e3b45 100644 --- a/examples/rust/clock/src/main.rs +++ b/examples/rust/clock/src/main.rs @@ -39,7 +39,7 @@ fn run(rec: &rerun::RecordingStream, args: &Args) -> anyhow::Result<()> { const WIDTH_M: f32 = 0.4; const WIDTH_H: f32 = 0.6; - rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Y_UP)?; + rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Y_UP())?; rec.log_static( "world/frame", diff --git a/examples/rust/objectron/src/main.rs b/examples/rust/objectron/src/main.rs index 940770ee1bf8..c3f4ceda0def 100644 --- a/examples/rust/objectron/src/main.rs +++ b/examples/rust/objectron/src/main.rs @@ -330,7 +330,7 @@ fn run(rec: &rerun::RecordingStream, args: &Args) -> anyhow::Result<()> { let annotations = read_annotations(&store_info.path_annotations)?; // See https://github.com/google-research-datasets/Objectron/issues/39 for coordinate systems - rec.log_static("world", &rerun::ViewCoordinates::RUB)?; + rec.log_static("world", &rerun::ViewCoordinates::RUB())?; log_baseline_objects(rec, &annotations.objects)?; diff --git a/examples/rust/raw_mesh/src/main.rs b/examples/rust/raw_mesh/src/main.rs index f01fbe75afd0..15255240f755 100644 --- a/examples/rust/raw_mesh/src/main.rs +++ b/examples/rust/raw_mesh/src/main.rs @@ -153,7 +153,10 @@ fn run(rec: &RecordingStream, args: &Args) -> anyhow::Result<()> { // Log raw glTF nodes and their transforms with Rerun for root in nodes { re_log::info!(scene = root.name, "logging glTF scene"); - rec.log_static(root.name.as_str(), &rerun::ViewCoordinates::RIGHT_HAND_Y_UP)?; + rec.log_static( + root.name.as_str(), + &rerun::ViewCoordinates::RIGHT_HAND_Y_UP(), + )?; log_node(rec, root)?; } diff --git a/tests/rust/roundtrips/view_coordinates/src/main.rs b/tests/rust/roundtrips/view_coordinates/src/main.rs index 1fa3e4f7a888..10aa28af9e8a 100644 --- a/tests/rust/roundtrips/view_coordinates/src/main.rs +++ b/tests/rust/roundtrips/view_coordinates/src/main.rs @@ -10,7 +10,7 @@ struct Args { } fn run(rec: &RecordingStream, _args: &Args) -> anyhow::Result<()> { - rec.log_static("/", &ViewCoordinates::RDF)?; + rec.log_static("/", &ViewCoordinates::RDF())?; Ok(()) } diff --git a/tests/rust/test_api/src/main.rs b/tests/rust/test_api/src/main.rs index 622f05c18ba5..421e1a9f8d3c 100644 --- a/tests/rust/test_api/src/main.rs +++ b/tests/rust/test_api/src/main.rs @@ -348,7 +348,7 @@ fn test_transforms_3d(rec: &RecordingStream) -> anyhow::Result<()> { rec: &RecordingStream, ent_path: impl Into, ) -> anyhow::Result<()> { - rec.log_static(ent_path, &ViewCoordinates::RIGHT_HAND_Z_UP) + rec.log_static(ent_path, &ViewCoordinates::RIGHT_HAND_Z_UP()) .map_err(Into::into) } log_coordinate_space(rec, "transforms3d")?; diff --git a/tests/rust/test_pinhole_projection/src/main.rs b/tests/rust/test_pinhole_projection/src/main.rs index 160838eae9a7..0ef4ad982a02 100644 --- a/tests/rust/test_pinhole_projection/src/main.rs +++ b/tests/rust/test_pinhole_projection/src/main.rs @@ -34,7 +34,7 @@ fn run(rec: &RecordingStream) -> anyhow::Result<()> { &rerun::TextDocument::from_markdown(DESCRIPTION), )?; - rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Y_DOWN)?; + rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Y_DOWN())?; const W: u32 = 2000; const H: u32 = 1000;