Skip to content

Commit

Permalink
Cameras are now handled like other scene parts (#738)
Browse files Browse the repository at this point in the history
* "the camera" is now consistently at the projection matrix
   * this is what is shown when a camera is selected, Fixes #638
* It is not necessary to define a rigid transform to define a camera - the camera just picks the rigid transform at the place where it sits in the hierarchy, i.e. it defaults to identity and transforms can be higher up in the tree above the projection transform
* `SpaceCoordinates` default now to RUB and can be logged anywhere at or above the projection matrix of a camera, Fixes #731
  • Loading branch information
Wumpf authored Jan 12, 2023
1 parent c6399ad commit 7cd1f33
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 331 deletions.
28 changes: 1 addition & 27 deletions crates/re_viewer/src/misc/space_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,6 @@ impl SpaceInfo {
}
}

/// Recursively gather all descendants that have no or only a rigid transform.
pub fn descendants_with_rigid_or_no_transform(
&self,
spaces_info: &SpacesInfo,
) -> IntSet<ObjPath> {
fn gather_rigidly_transformed_children(
space: &SpaceInfo,
spaces_info: &SpacesInfo,
objects: &mut IntSet<ObjPath>,
) {
objects.extend(space.descendants_without_transform.iter().cloned());

for (child_path, transform) in &space.child_spaces {
if let re_log_types::Transform::Rigid3(_) = transform {
if let Some(child_space) = spaces_info.get(child_path) {
gather_rigidly_transformed_children(child_space, spaces_info, objects);
}
}
}
}

let mut objects = IntSet::default();
gather_rigidly_transformed_children(self, spaces_info, &mut objects);
objects
}

pub fn parent<'a>(&self, spaces_info: &'a SpacesInfo) -> Option<(&'a SpaceInfo, &Transform)> {
self.parent.as_ref().and_then(|(parent_path, transform)| {
spaces_info.get(parent_path).map(|space| (space, transform))
Expand Down Expand Up @@ -279,7 +253,7 @@ fn query_view_coordinates_arrow(
view_coords
}

fn query_view_coordinates(
pub fn query_view_coordinates(
obj_db: &ObjDb,
time_ctrl: &TimeControl,
obj_path: &ObjPath,
Expand Down
120 changes: 75 additions & 45 deletions crates/re_viewer/src/ui/space_view.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;

use re_data_store::{InstanceId, ObjPath, ObjectTree, ObjectsProperties, TimeInt};

use nohash_hasher::IntSet;
Expand Down Expand Up @@ -65,29 +67,33 @@ pub(crate) struct SpaceView {

/// Set to `false` the first time the user messes around with the list of queried objects.
pub allow_auto_adding_more_object: bool,

/// Transforms seen last frame, renewed every frame.
/// TODO(andreas): This should probably live on `SpacesInfo` and created there lazily?
/// See also [#741](https://github.com/rerun-io/rerun/issues/741)
#[serde(skip)]
cached_transforms: TransformCache,
}

impl SpaceView {
pub fn new(
ctx: &ViewerContext<'_>,
category: ViewCategory,
space_info: &SpaceInfo,
spaces_info: &SpacesInfo,
default_spatial_naviation_mode: SpatialNavigationMode,
queried_objects: IntSet<ObjPath>,
default_spatial_navigation_mode: SpatialNavigationMode,
initial_transforms: TransformCache,
) -> Self {
let mut view_state = ViewState::default();

if category == ViewCategory::Spatial {
view_state.state_spatial.nav_mode = default_spatial_naviation_mode;
view_state.state_spatial.nav_mode = default_spatial_navigation_mode;
}

let root_path = space_info.path.iter().next().map_or_else(
|| space_info.path.clone(),
|c| ObjPath::from(vec![c.to_owned()]),
);

let queried_objects = Self::default_queried_objects(ctx, category, space_info, spaces_info);

let name = if queried_objects.len() == 1 {
// a single object in this space-view - name the space after it
let obj_path = queried_objects.iter().next().unwrap();
Expand All @@ -110,43 +116,86 @@ impl SpaceView {
view_state,
category,
allow_auto_adding_more_object: true,
cached_transforms: initial_transforms,
}
}

/// List of objects a space view queries by default.
/// List of objects a space view queries by default for a given category.
///
/// These are all objects in the given space which have the requested category and are reachable by a transform.
pub fn default_queried_objects(
ctx: &ViewerContext<'_>,
category: ViewCategory,
root_space: &SpaceInfo,
space_info: &SpaceInfo,
spaces_info: &SpacesInfo,
transforms: &TransformCache,
) -> IntSet<ObjPath> {
crate::profile_function!();

let timeline = ctx.rec_cfg.time_ctrl.timeline();
let log_db = &ctx.log_db;

root_space
.descendants_with_rigid_or_no_transform(spaces_info)
.iter()
.cloned()
.filter(|obj_path| categorize_obj_path(timeline, log_db, obj_path).contains(category))
.collect()
let mut objects = IntSet::default();
space_info.visit_descendants(spaces_info, &mut |space| {
objects.extend(
space
.descendants_without_transform
.iter()
.filter(|obj_path| {
transforms.reference_from_obj(obj_path).is_reachable()
&& categorize_obj_path(timeline, log_db, obj_path).contains(category)
})
.cloned(),
);
});
objects
}

/// List of objects a space view queries by default for all any possible category.
pub fn default_queried_objects_by_category(
ctx: &ViewerContext<'_>,
space_info: &SpaceInfo,
transforms: &TransformCache,
) -> BTreeMap<ViewCategory, IntSet<ObjPath>> {
let timeline = ctx.rec_cfg.time_ctrl.timeline();
let log_db = &ctx.log_db;

let mut groups: BTreeMap<ViewCategory, IntSet<ObjPath>> = Default::default();
for obj_path in transforms.objects_with_reachable_transform() {
if obj_path == &space_info.path || obj_path.is_descendant_of(&space_info.path) {
for category in categorize_obj_path(timeline, log_db, obj_path) {
groups.entry(category).or_default().insert(obj_path.clone());
}
}
}
groups
}

pub fn on_frame_start(&mut self, ctx: &mut ViewerContext<'_>, spaces_info: &SpacesInfo) {
self.data_blueprint.on_frame_start();

if !self.allow_auto_adding_more_object {
return;
}
let Some(space) = spaces_info.get(&self.space_path) else {
let Some(space_info) = spaces_info.get(&self.space_path) else {
return;
};
// Add objects that have been logged since we were created
self.queried_objects =
Self::default_queried_objects(ctx, self.category, space, spaces_info);
self.data_blueprint
.insert_objects_according_to_hierarchy(&self.queried_objects, &self.space_path);

self.cached_transforms = TransformCache::determine_transforms(
spaces_info,
space_info,
self.data_blueprint.data_blueprints_projected(),
);

if self.allow_auto_adding_more_object {
// Add objects that have been logged since we were created
self.queried_objects = Self::default_queried_objects(
ctx,
self.category,
space_info,
spaces_info,
&self.cached_transforms,
);
self.data_blueprint
.insert_objects_according_to_hierarchy(&self.queried_objects, &self.space_path);
}
}

pub fn selection_ui(&mut self, ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
Expand Down Expand Up @@ -380,7 +429,6 @@ impl SpaceView {
&mut self,
ctx: &mut ViewerContext<'_>,
ui: &mut egui::Ui,
spaces_info: &SpacesInfo,
reference_space_info: &SpaceInfo,
latest_at: TimeInt,
) {
Expand Down Expand Up @@ -413,26 +461,17 @@ impl SpaceView {
}

ViewCategory::Spatial => {
let Some(reference_space) = spaces_info.get(&self.space_path) else {
return;
};
let transforms = TransformCache::determine_transforms(
spaces_info,
reference_space,
self.data_blueprint.data_blueprints_projected(),
);
let mut scene = view_spatial::SceneSpatial::default();
scene.load_objects(
ctx,
&query,
&transforms,
&self.cached_transforms,
self.view_state.state_spatial.hovered_instance_hash(),
);
self.view_state.ui_spatial(
ctx,
ui,
&self.space_path,
spaces_info,
reference_space_info,
scene,
self.data_blueprint.data_blueprints_projected(),
Expand Down Expand Up @@ -477,27 +516,18 @@ pub(crate) struct ViewState {

impl ViewState {
// TODO(andreas): split into smaller parts, some of it shouldn't be part of the ui path and instead scene loading.
#[allow(clippy::too_many_arguments)]
fn ui_spatial(
&mut self,
ctx: &mut ViewerContext<'_>,
ui: &mut egui::Ui,
space: &ObjPath,
spaces_info: &SpacesInfo,
space_info: &SpaceInfo,
scene: view_spatial::SceneSpatial,
obj_properties: &ObjectsProperties,
) {
ui.vertical(|ui| {
self.state_spatial.view_spatial(
ctx,
ui,
space,
scene,
spaces_info,
space_info,
obj_properties,
);
self.state_spatial
.view_spatial(ctx, ui, space, scene, space_info, obj_properties);
});
}

Expand Down
26 changes: 22 additions & 4 deletions crates/re_viewer/src/ui/transform_cache.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use nohash_hasher::IntMap;
use re_data_store::{ObjPath, ObjectsProperties};
use re_log_types::ObjPathHash;

use crate::misc::space_info::{SpaceInfo, SpacesInfo};

/// Provides transforms from an object to a chosen reference space for all elements in the scene.
///
/// The renderer then uses this reference space as its world space,
/// making world and reference space equivalent for a given space view.
#[derive(Clone, Default)]
pub struct TransformCache {
reference_from_obj_per_object: IntMap<ObjPathHash, ReferenceFromObjTransform>,
reference_from_obj_per_object: IntMap<ObjPath, ReferenceFromObjTransform>,
}

#[derive(Clone, Copy)]
Expand All @@ -33,6 +33,15 @@ pub enum ReferenceFromObjTransform {
Reachable(glam::Mat4),
}

impl ReferenceFromObjTransform {
pub fn is_reachable(&self) -> bool {
match self {
ReferenceFromObjTransform::Unreachable(_) => false,
ReferenceFromObjTransform::Reachable(_) => true,
}
}
}

impl TransformCache {
/// Determines transforms for all objects relative to a `reference_space`.
/// I.e. the resulting transforms are "reference from scene"
Expand Down Expand Up @@ -149,7 +158,7 @@ impl TransformCache {
space
.descendants_without_transform
.iter()
.map(|obj| (*obj.hash(), transform.clone())),
.map(|obj| (obj.clone(), transform.clone())),
);
}

Expand Down Expand Up @@ -242,10 +251,19 @@ impl TransformCache {
/// This is typically used as the "world space" for the renderer in a given frame.
pub fn reference_from_obj(&self, obj_path: &ObjPath) -> ReferenceFromObjTransform {
self.reference_from_obj_per_object
.get(obj_path.hash())
.get(obj_path)
.cloned()
.unwrap_or(ReferenceFromObjTransform::Unreachable(
UnreachableTransformReason::Unconnected,
))
}

pub fn objects_with_reachable_transform(&self) -> impl Iterator<Item = &ObjPath> {
self.reference_from_obj_per_object
.iter()
.filter_map(|(obj, transform)| match transform {
ReferenceFromObjTransform::Unreachable(_) => None,
ReferenceFromObjTransform::Reachable(_) => Some(obj),
})
}
}
27 changes: 8 additions & 19 deletions crates/re_viewer/src/ui/view_category.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use std::collections::BTreeMap;

use nohash_hasher::IntSet;
use re_data_store::{LogDb, ObjPath, Timeline};
use re_data_store::{query_transform, LogDb, ObjPath, Timeline};
use re_log_types::DataPath;

#[derive(
Expand Down Expand Up @@ -48,7 +45,13 @@ pub fn categorize_obj_path(
) -> ViewCategorySet {
crate::profile_function!();

let Some(obj_type) = log_db.obj_db.types.get(obj_path.obj_type_path()) else {
let Some(obj_type) = log_db.obj_db.types.get(obj_path.obj_type_path()) else {
// If it has a transform we might want to visualize it in space
// (as of writing we do that only for projections, i.e. cameras, but visualizations for rigid transforms may be added)
if query_transform(&log_db.obj_db, timeline, obj_path, Some(i64::MAX)).is_some() {
return ViewCategory::Spatial.into();
}

return ViewCategorySet::default();
};

Expand Down Expand Up @@ -97,17 +100,3 @@ pub fn categorize_obj_path(
}
}
}

pub fn group_by_category<'a>(
timeline: &Timeline,
log_db: &LogDb,
objects: impl Iterator<Item = &'a ObjPath>,
) -> BTreeMap<ViewCategory, IntSet<ObjPath>> {
let mut groups: BTreeMap<ViewCategory, IntSet<ObjPath>> = Default::default();
for obj_path in objects {
for category in categorize_obj_path(timeline, log_db, obj_path) {
groups.entry(category).or_default().insert(obj_path.clone());
}
}
groups
}
Loading

1 comment on commit 7cd1f33

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: 7cd1f33 Previous: c6399ad Ratio
datastore/insert/batch/rects/insert 279325 ns/iter (± 924) 281337 ns/iter (± 940) 0.99
datastore/latest_at/batch/rects/query 722 ns/iter (± 0) 725 ns/iter (± 2) 1.00
datastore/latest_at/missing_components/primary 303 ns/iter (± 0) 301 ns/iter (± 0) 1.01
datastore/latest_at/missing_components/secondaries 375 ns/iter (± 0) 371 ns/iter (± 1) 1.01
datastore/range/batch/rects/query 45781 ns/iter (± 53) 45616 ns/iter (± 77) 1.00
obj_mono_points/insert 987375957 ns/iter (± 8873665) 1000041563 ns/iter (± 4826371) 0.99
obj_mono_points/query 417272 ns/iter (± 1633) 380941 ns/iter (± 2337) 1.10
obj_batch_points/insert 97010113 ns/iter (± 521312) 98082509 ns/iter (± 548268) 0.99
obj_batch_points/query 10978 ns/iter (± 38) 10974 ns/iter (± 111) 1.00
obj_batch_points_sequential/insert 22774683 ns/iter (± 541866) 23671476 ns/iter (± 209425) 0.96
obj_batch_points_sequential/query 7273 ns/iter (± 11) 7226 ns/iter (± 15) 1.01
mono_points_classic/generate_messages 4250699 ns/iter (± 51439) 4298346 ns/iter (± 321390) 0.99
mono_points_classic/encode_log_msg 11068985 ns/iter (± 417721) 13606104 ns/iter (± 673017) 0.81
mono_points_classic/encode_total 16601050 ns/iter (± 1382327) 17249920 ns/iter (± 1315776) 0.96
mono_points_classic/decode_total 37746833 ns/iter (± 665547) 38877424 ns/iter (± 712725) 0.97
mono_points_arrow/generate_message_bundles 52391568 ns/iter (± 960741) 52922837 ns/iter (± 879012) 0.99
mono_points_arrow/generate_messages 139313344 ns/iter (± 1472522) 138699222 ns/iter (± 1366861) 1.00
mono_points_arrow/encode_log_msg 163103827 ns/iter (± 1717978) 166067481 ns/iter (± 1459015) 0.98
mono_points_arrow/encode_total 355536605 ns/iter (± 1671881) 360366737 ns/iter (± 2238860) 0.99
mono_points_arrow/decode_log_msg 188388908 ns/iter (± 1131968) 190634344 ns/iter (± 858703) 0.99
mono_points_arrow/decode_message_bundles 79380145 ns/iter (± 1204784) 80468659 ns/iter (± 1116716) 0.99
mono_points_arrow/decode_total 263634166 ns/iter (± 1855429) 266559261 ns/iter (± 2335569) 0.99
batch_points_classic/generate_messages 3531 ns/iter (± 21) 3548 ns/iter (± 28) 1.00
batch_points_classic/encode_log_msg 424990 ns/iter (± 974) 427889 ns/iter (± 1075) 0.99
batch_points_classic/encode_total 430947 ns/iter (± 1375) 434037 ns/iter (± 981) 0.99
batch_points_classic/decode_total 728606 ns/iter (± 1911) 732313 ns/iter (± 1287) 0.99
batch_points_arrow/generate_message_bundles 315178 ns/iter (± 484) 316209 ns/iter (± 608) 1.00
batch_points_arrow/generate_messages 6315 ns/iter (± 22) 6260 ns/iter (± 36) 1.01
batch_points_arrow/encode_log_msg 366655 ns/iter (± 1696) 361294 ns/iter (± 1477) 1.01
batch_points_arrow/encode_total 706502 ns/iter (± 1870) 717988 ns/iter (± 3316) 0.98
batch_points_arrow/decode_log_msg 351258 ns/iter (± 1870) 347708 ns/iter (± 1263) 1.01
batch_points_arrow/decode_message_bundles 2168 ns/iter (± 11) 2166 ns/iter (± 16) 1.00
batch_points_arrow/decode_total 354014 ns/iter (± 1007) 351859 ns/iter (± 1417) 1.01
arrow_mono_points/insert 6914257874 ns/iter (± 17822220) 6939614106 ns/iter (± 30576582) 1.00
arrow_mono_points/query 1646651 ns/iter (± 18424) 1648412 ns/iter (± 15555) 1.00
arrow_batch_points/insert 2651069 ns/iter (± 12927) 2652920 ns/iter (± 53097) 1.00
arrow_batch_points/query 15376 ns/iter (± 47) 15322 ns/iter (± 20) 1.00
obj_batch_points_sequential/Tuid::random 37 ns/iter (± 0) 37 ns/iter (± 0) 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.