Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show meshes and images with rerun foo.obj bar.png #2060

Merged
merged 24 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ polars-core = "0.29"
polars-lazy = "0.29"
polars-ops = "0.29"
puffin = "0.14"
rayon = "1.7"
rfd = { version = "0.11.3", default_features = false, features = [
"xdg-portal",
] }
Expand Down
7 changes: 0 additions & 7 deletions crates/re_log_types/src/component_types/tensor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,13 +776,6 @@ pub enum TensorImageLoadError {
expected: Vec<TensorDimension>,
found: Vec<TensorDimension>,
},

#[cfg(not(target_arch = "wasm32"))]
#[error("Unsupported file extension '{extension}' for file {path:?}")]
UnknownExtension {
extension: String,
path: std::path::PathBuf,
},
}

#[cfg(feature = "image")]
Expand Down
89 changes: 89 additions & 0 deletions crates/re_log_types/src/data_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,95 @@ impl DataCellInner {
}
}

// ----------------------------------------------------------------------------

#[cfg(not(target_arch = "wasm32"))]
/// Errors from [`DataCell::from_file_path`]
#[derive(thiserror::Error, Debug)]
pub enum FromFileError {
#[error(transparent)]
FileRead(#[from] std::io::Error),

#[error(transparent)]
DataCellError(#[from] crate::DataCellError),

#[cfg(feature = "image")]
#[error(transparent)]
TensorImageLoad(#[from] crate::component_types::TensorImageLoadError),

#[error("Unsupported file extension '{extension}' for file {path:?}. To load image files, make sure you compile with the 'image' feature")]
UnknownExtension {
extension: String,
path: std::path::PathBuf,
},
}

#[cfg(not(target_arch = "wasm32"))]
impl DataCell {
/// Read the file at the given path.
///
/// Supported file extensions are:
/// * `glb`, `gltf`, `obj`: encoded meshes, leaving it to the viewer to decode
/// * `jpg`, `jpeg`: encoded JPEG, leaving it to the viewer to decode. Requires the `image` feature.
/// * `png` and other image formats: decoded here. Requires the `image` feature.
///
/// All other extensions will return an error.
pub fn from_file_path(file_path: &std::path::Path) -> Result<Self, FromFileError> {
let extension = file_path
.extension()
.unwrap_or_default()
.to_ascii_lowercase()
.to_string_lossy()
.to_string();

match extension.as_str() {
"glb" => Self::from_mesh_file_path(file_path, crate::MeshFormat::Glb),
"glft" => Self::from_mesh_file_path(file_path, crate::MeshFormat::Gltf),
"obj" => Self::from_mesh_file_path(file_path, crate::MeshFormat::Obj),

#[cfg(feature = "image")]
_ => {
// Assume and image (there are so many image extensions):
let tensor = crate::Tensor::from_image_file(file_path)?;
Ok(Self::try_from_native(std::iter::once(&tensor))?)
}

#[cfg(not(feature = "image"))]
_ => Err(FromFileError::UnknownExtension {
extension,
path: file_path.to_owned(),
}),
}
}

/// Read the mesh file at the given path.
///
/// Supported file extensions are:
/// * `glb`, `gltf`, `obj`: encoded meshes, leaving it to the viewer to decode
///
/// All other extensions will return an error.
pub fn from_mesh_file_path(
file_path: &std::path::Path,
format: crate::MeshFormat,
) -> Result<Self, FromFileError> {
let mesh = crate::EncodedMesh3D {
mesh_id: crate::MeshId::random(),
format,
bytes: std::fs::read(file_path)?.into(),
transform: [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 0.0],
],
};
let mesh = crate::Mesh3D::Encoded(mesh);
Ok(Self::try_from_native(std::iter::once(&mesh))?)
}
}

// ----------------------------------------------------------------------------

#[test]
fn data_cell_sizes() {
use crate::{component_types::InstanceKey, Component as _};
Expand Down
2 changes: 1 addition & 1 deletion crates/re_log_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use self::component_types::DrawOrder;
pub use self::component_types::{EncodedMesh3D, Mesh3D, MeshFormat, MeshId, RawMesh3D};
pub use self::component_types::{Tensor, ViewCoordinates};
pub use self::data::*;
pub use self::data_cell::{DataCell, DataCellError, DataCellInner, DataCellResult};
pub use self::data_cell::{DataCell, DataCellError, DataCellInner, DataCellResult, FromFileError};
pub use self::data_row::{DataRow, DataRowError, DataRowResult, RowId};
pub use self::data_table::{
DataCellColumn, DataCellOptVec, DataTable, DataTableError, DataTableResult, EntityPathVec,
Expand Down
10 changes: 10 additions & 0 deletions crates/re_log_types/src/path/entity_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ impl EntityPath {
Self::from(parts)
}

/// Treat the file path as one opaque string.
///
/// The file path separators will NOT become splits in the new path.
/// The returned path will only have one part.
pub fn from_file_path(file_path: &std::path::Path) -> Self {
emilk marked this conversation as resolved.
Show resolved Hide resolved
Self::new(vec![EntityPathPart::Index(crate::Index::String(
file_path.to_string_lossy().to_string(),
))])
}

#[inline]
pub fn iter(&self) -> impl Iterator<Item = &EntityPathPart> {
self.path.iter()
Expand Down
2 changes: 2 additions & 0 deletions crates/re_renderer/src/wgpu_resources/buffer_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ impl GpuBufferPool {
/// For more efficient allocation (faster, less fragmentation) you should sub-allocate buffers whenever possible
/// either manually or using a higher level allocator.
pub fn alloc(&self, device: &wgpu::Device, desc: &BufferDesc) -> GpuBuffer {
crate::profile_function!();
self.pool.alloc(desc, |desc| {
crate::profile_scope!("create_buffer");
device.create_buffer(&wgpu::BufferDescriptor {
label: desc.label.get(),
size: desc.size,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ where

// Otherwise create a new resource
re_log::trace!(?desc, "Allocated new resource");
let inner_resource = creation_func(desc);
let inner_resource = {
crate::profile_scope!("creation_func");
creation_func(desc)
};
self.total_resource_size_in_bytes.fetch_add(
desc.resource_size_in_bytes(),
std::sync::atomic::Ordering::Relaxed,
Expand Down
50 changes: 48 additions & 2 deletions crates/re_sdk/src/msg_sender.rs
emilk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use re_log_types::{component_types::InstanceKey, DataRow, DataTableError, RowId};
use re_log_types::{component_types::InstanceKey, DataRow, DataTableError, RecordingId, RowId};

use crate::{
log::DataCell,
Expand Down Expand Up @@ -102,6 +102,26 @@ impl MsgSender {
}
}

/// Read the file at the given path and log it.
///
/// Supported file extensions are:
/// * `glb`, `gltf`, `obj`: encoded meshes, leaving it to the viewer to decode
/// * `jpg`, `jpeg`: encoded JPEG, leaving it to the viewer to decode. Requires the `image` feature.
/// * `png` and other image formats: decoded here. Requires the `image` feature.
///
/// All other extensions will return an error.
pub fn from_file_path(
file_path: &std::path::Path,
) -> Result<Self, re_log_types::FromFileError> {
let ent_path = re_log_types::EntityPath::from_file_path(file_path);
let cell = DataCell::from_file_path(file_path)?;
Ok(Self {
num_instances: Some(cell.num_instances()),
instanced: vec![cell],
..Self::new(ent_path)
})
Comment on lines +117 to +122
Copy link
Member

Choose a reason for hiding this comment

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

nice

}

// --- Time ---

/// Appends a given `timepoint` to the current message.
Expand Down Expand Up @@ -161,11 +181,24 @@ impl MsgSender {
/// the same component type multiple times in a single message.
/// Doing so will return an error when trying to `send()` the message.
pub fn with_component<'a, C: SerializableComponent>(
mut self,
self,
data: impl IntoIterator<Item = &'a C>,
) -> Result<Self, MsgSenderError> {
let cell = DataCell::try_from_native(data).map_err(DataTableError::from)?;
self.with_cell(cell)
}

/// Appends a component collection to the current message.
///
/// All component collections stored in the message must have the same row-length (i.e. number
/// of instances)!
/// The row-length of the first appended collection is used as ground truth.
///
/// ⚠ This can only be called once per type of component!
/// The SDK does not yet support batch insertions, which are semantically identical to adding
/// the same component type multiple times in a single message.
/// Doing so will return an error when trying to `send()` the message.
pub fn with_cell(mut self, cell: DataCell) -> Result<MsgSender, MsgSenderError> {
let num_instances = cell.num_instances();

if let Some(cur_num_instances) = self.num_instances {
Expand Down Expand Up @@ -250,6 +283,19 @@ impl MsgSender {
Ok(())
}

/// Turns the current message into a single [`re_log_types::LogMsg`]
pub fn into_log_msg(
self,
recording_id: RecordingId,
) -> Result<re_log_types::LogMsg, DataTableError> {
let data_table = re_log_types::DataTable::from_rows(
re_log_types::TableId::random(),
self.into_rows().into_iter().flatten(),
);
let arrow_msg = data_table.to_arrow_msg()?;
Ok(re_log_types::LogMsg::ArrowMsg(recording_id, arrow_msg))
}
emilk marked this conversation as resolved.
Show resolved Hide resolved

fn into_rows(self) -> [Option<DataRow>; 2] {
let Self {
entity_path,
Expand Down
8 changes: 5 additions & 3 deletions crates/re_smart_channel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ pub use crossbeam::channel::{RecvError, RecvTimeoutError, SendError, TryRecvErro
/// Where is the messages coming from?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Source {
/// The source if a file on disk
File { path: std::path::PathBuf },
/// The source is one or more files on disk.
/// This could be `.rrd` files, or `.glb`, `.png`, …
// TODO(#2121): Remove this
Files { paths: Vec<std::path::PathBuf> },

/// Streaming an `.rrd` file over http.
RrdHttpStream { url: String },
Expand Down Expand Up @@ -41,7 +43,7 @@ pub enum Source {
impl Source {
pub fn is_network(&self) -> bool {
match self {
Self::File { .. } | Self::Sdk | Self::RrdWebEventListener => false,
Self::Files { .. } | Self::Sdk | Self::RrdWebEventListener => false,
Self::RrdHttpStream { .. } | Self::WsClient { .. } | Self::TcpServer { .. } => true,
}
}
Expand Down
19 changes: 14 additions & 5 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,8 +638,13 @@ fn wait_screen_ui(ui: &mut egui::Ui, rx: &Receiver<LogMsg>) {
}

match rx.source() {
re_smart_channel::Source::File { path } => {
ui.strong(format!("Loading {}…", path.display()));
re_smart_channel::Source::Files { paths } => {
ui.strong(format!(
"Loading {}…",
paths
.iter()
.format_with(", ", |path, f| f(&format_args!("{}", path.display())))
));
}
re_smart_channel::Source::RrdHttpStream { url } => {
ui.strong(format!("Loading {url}…"));
Expand Down Expand Up @@ -1904,7 +1909,9 @@ fn load_file_path(path: &std::path::Path) -> Option<LogDb> {
match load_file_path_impl(path) {
Ok(mut new_log_db) => {
re_log::info!("Loaded {path:?}");
new_log_db.data_source = Some(re_smart_channel::Source::File { path: path.into() });
new_log_db.data_source = Some(re_smart_channel::Source::Files {
paths: vec![path.into()],
});
Some(new_log_db)
}
Err(err) => {
Expand All @@ -1924,7 +1931,9 @@ fn load_file_contents(name: &str, read: impl std::io::Read) -> Option<LogDb> {
match load_rrd_to_log_db(read) {
Ok(mut log_db) => {
re_log::info!("Loaded {name:?}");
log_db.data_source = Some(re_smart_channel::Source::File { path: name.into() });
log_db.data_source = Some(re_smart_channel::Source::Files {
paths: vec![name.into()],
});
Some(log_db)
}
Err(err) => {
Expand Down Expand Up @@ -1957,7 +1966,7 @@ fn new_recording_confg(
let play_state = match data_source {
// Play files from the start by default - it feels nice and alive./
// RrdHttpStream downloads the whole file before decoding it, so we treat it the same as a file.
re_smart_channel::Source::File { .. }
re_smart_channel::Source::Files { .. }
| re_smart_channel::Source::RrdHttpStream { .. }
| re_smart_channel::Source::RrdWebEventListener => PlayState::Playing,

Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/viewer_analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl ViewerAnalytics {

if let Some(data_source) = &log_db.data_source {
let data_source = match data_source {
re_smart_channel::Source::File { .. } => "file", // .rrd
re_smart_channel::Source::Files { .. } => "file", // .rrd, .png, .glb, …
re_smart_channel::Source::RrdHttpStream { .. } => "http",
re_smart_channel::Source::RrdWebEventListener { .. } => "web_event",
re_smart_channel::Source::Sdk => "sdk", // show()
Expand Down
6 changes: 4 additions & 2 deletions crates/rerun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ glam = ["re_sdk?/glam"]
global_session = ["re_sdk?/global_session"]

## Integration with the [`image`](https://crates.io/crates/image/) crate.
image = ["re_log_types/image"]
image = ["re_log_types/image", "re_sdk?/image"]

## Support spawning a native viewer.
## This adds a lot of extra dependencies, so only enable this feature if you need it!
Expand Down Expand Up @@ -105,8 +105,10 @@ webbrowser = { version = "0.8", optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
backtrace = "0.3"
clap = { workspace = true, features = ["derive"] }
mimalloc.workspace = true
ctrlc.workspace = true
mimalloc.workspace = true
puffin.workspace = true
rayon.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }

# Native unix dependencies:
Expand Down
Loading