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 9 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
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
95 changes: 94 additions & 1 deletion 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 @@ -29,6 +29,27 @@ pub enum MsgSenderError {
PackingError(#[from] DataTableError),
}

/// Errors from [`MsgSender::from_file_path`]
#[derive(thiserror::Error, Debug)]
pub enum FromFileError {
#[error(transparent)]
FileRead(#[from] std::io::Error),

#[error(transparent)]
MsgSender(#[from] MsgSenderError),

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

#[cfg(not(target_arch = "wasm32"))]
#[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,
},
}

/// Facilitates building and sending component payloads with the Rerun SDK.
///
/// ```ignore
Expand Down Expand Up @@ -102,6 +123,65 @@ 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, FromFileError> {
let load_mesh = |ent_path, format| -> Result<Self, FromFileError> {
let mesh = crate::components::EncodedMesh3D {
mesh_id: crate::components::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 msg_sender =
Self::new(ent_path).with_component(&[crate::components::Mesh3D::Encoded(mesh)])?;
Ok(msg_sender)
};

let ent_path = re_log_types::EntityPath::new(vec![re_log_types::EntityPathPart::Index(
re_log_types::Index::String(file_path.to_string_lossy().to_string()),
)]);

let extension = file_path
.extension()
.unwrap_or_default()
.to_ascii_lowercase()
.to_string_lossy()
.to_string();

match extension.as_str() {
"glb" => load_mesh(ent_path, crate::components::MeshFormat::Glb),
"glft" => load_mesh(ent_path, crate::components::MeshFormat::Gltf),
"obj" => load_mesh(ent_path, crate::components::MeshFormat::Obj),

#[cfg(feature = "image")]
_ => {
// Assume and image (there are so many image extensions):
let tensor = re_log_types::component_types::Tensor::from_image_file(file_path)?;
let msg_sender = Self::new(ent_path).with_component(&[tensor])?;
Ok(msg_sender)
}

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

// --- Time ---

/// Appends a given `timepoint` to the current message.
Expand Down Expand Up @@ -250,6 +330,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
7 changes: 4 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,9 @@ 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 `.rrf` files, or `.glb`, `.png`, …
emilk marked this conversation as resolved.
Show resolved Hide resolved
Files { paths: Vec<std::path::PathBuf> },

/// Streaming an `.rrd` file over http.
RrdHttpStream { url: String },
Expand Down Expand Up @@ -41,7 +42,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