1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
use glam::Vec3;
use re_math::IsoTransform;
use re_log_types::EntityPath;
use re_types::archetypes::Pinhole;
use re_types::components::ViewCoordinates;
use crate::visualizers::image_view_coordinates;
/// A logged camera that connects spaces.
#[derive(Clone, PartialEq)]
pub struct SpaceCamera3D {
/// Path to the entity which has the projection (pinhole, ortho or otherwise) transforms.
///
/// We expect the camera transform to apply to this instance and every path below it.
pub ent_path: EntityPath,
/// The coordinate system of the pinhole entity ("view-space").
pub pinhole_view_coordinates: ViewCoordinates,
/// Camera "Extrinsics", i.e. the pose of the camera.
pub world_from_camera: IsoTransform,
// -------------------------
// Optional projection-related things:
/// The projection transform of a child-entity.
pub pinhole: Option<Pinhole>,
/// Distance of a picture plane from the camera.
pub picture_plane_distance: f32,
}
impl SpaceCamera3D {
/// Where in scene-space is the camera origin?
pub fn position(&self) -> Vec3 {
self.world_from_camera.translation()
}
pub fn world_from_cam(&self) -> IsoTransform {
self.world_from_camera
}
pub fn cam_from_world(&self) -> IsoTransform {
self.world_from_cam().inverse()
}
/// Scene-space from Rerun view-space (RUB).
pub fn world_from_rub_view(&self) -> Option<IsoTransform> {
match self.pinhole_view_coordinates.from_rub_quat() {
Ok(from_rub) => Some(self.world_from_camera * IsoTransform::from_quat(from_rub)),
Err(err) => {
re_log::warn_once!("Camera {:?}: {err}", self.ent_path);
None
}
}
}
/// Returns x, y, and depth in image/pixel coordinates.
pub fn project_onto_2d(&self, point_in_world: Vec3) -> Option<Vec3> {
let pinhole = self.pinhole.as_ref()?;
let point_in_cam = self.cam_from_world().transform_point3(point_in_world);
// The pinhole view-coordinates are important here because they define how the image plane is aligned
// with the camera coordinate system. It is not a given that a user wants the image-plane aligned with the
// XY-plane in camera space.
//
// Because the [`Pinhole`] component currently assumes an input in the default `image_view_coordinates`
// we need to pre-transform the data from the user-defined `pinhole_view_coordinates` to the required
// `image_view_coordinates`.
//
// TODO(emilk): When Pinhole is an archetype instead of a component, `pinhole.project` should do this
// internally.
let point_in_image_unprojected =
image_view_coordinates().from_other(&self.pinhole_view_coordinates) * point_in_cam;
let point_in_image = pinhole.project(point_in_image_unprojected);
Some(point_in_image)
}
}