Loads a glTF asset from TCP streams? #11108
-
First post here; gotta say Bevy is amazing! You guys rock. I want to populate a 3d Bevy world with assets coming in over a TCP connection from another app where they're created on the fly. I already have the asset in a String, and hacked by writing it out as a file; this works to read it back in:
Is there anything like (Got excited for a moment about https://github.com/vleue/bevy_embedded_assets, but lack the temerity to figure out if it's done somewhere in there... ; ) |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
With the new Assets V2 system, I think your best shot is to implement a custom From what I understand you would need to implement a custom Then you could do something like commands.spawn(SceneBundle {
scene: asset_server.load("tcp://../assets/models/tmp/foo.gltf#Scene0"),
..default()
}); Maybe the Hope that helps :) |
Beta Was this translation helpful? Give feedback.
-
Here's an example for you (loads glTF asset directly from github): use std::path::Path;
use std::str::FromStr;
use std::time::Duration;
use bevy::asset::io::{AssetReader, AssetReaderError, AssetSource, PathStream, Reader};
use bevy::prelude::*;
use bevy::utils::BoxedFuture;
use isahc::config::{Configurable, RedirectPolicy};
use isahc::http::Uri;
use isahc::HttpClient;
const PROTO_HTTP: u8 = 0;
const PROTO_HTTPS: u8 = 1;
struct HttpAssetReader<const PROTO: u8>(HttpClient);
impl<const PROTO: u8> AssetReader for HttpAssetReader<PROTO> {
fn read<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let proto = match PROTO {
PROTO_HTTP => "http",
PROTO_HTTPS => "https",
_ => unreachable!(),
};
let uri = (|| { Uri::from_str(&format!("{}://{}", proto, path.to_str()?)).ok() })();
let Some(uri) = uri else {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid path").into());
};
let response = match self.0.get_async(uri).await {
Ok(response) => response,
Err(err) => {
let err: std::io::Error = err.into();
return Err(err.into());
}
};
if !response.status().is_success() {
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, response.status().to_string()).into());
}
let body = response.into_body();
let reader: Box<Reader> = Box::new(body);
Ok(reader)
})
}
fn read_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
fn read_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
fn is_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<bool, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
}
#[allow(clippy::redundant_clone)]
fn main() {
let client = HttpClient::builder()
.max_connections(10)
.max_connections_per_host(10)
.tcp_keepalive(Duration::from_secs(10))
.redirect_policy(RedirectPolicy::Follow)
.build()
.unwrap();
App::new()
.register_asset_source("http", AssetSource::build().with_reader({
let client = client.clone();
move || Box::new(HttpAssetReader::<PROTO_HTTP>(client.clone()))
}))
.register_asset_source("https", AssetSource::build().with_reader({
let client = client.clone();
move || Box::new(HttpAssetReader::<PROTO_HTTPS>(client.clone()))
}))
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, bevy::window::close_on_esc)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.7, 0.7, 1.).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
..default()
});
commands.spawn(DirectionalLightBundle::default());
commands.spawn(SceneBundle {
scene: asset_server.load("https://github.com/bevyengine/bevy/raw/main/assets/models/AlienCake/cakeBirthday.glb#Scene0"),
..default()
});
} |
Beta Was this translation helpful? Give feedback.
-
Wow, thanks!
I will check this out early tomorrow.
.
…On Jan 5, 2024 at 8:54 PM -0800, Alex Kocharin ***@***.***>, wrote:
Here's an example for you (loads glTF asset directly from github):
use std::path::Path;
use std::str::FromStr;
use std::time::Duration;
use bevy::asset::io::{AssetReader, AssetReaderError, AssetSource, PathStream, Reader};
use bevy::prelude::*;
use bevy::utils::BoxedFuture;
use isahc::config::{Configurable, RedirectPolicy};
use isahc::http::Uri;
use isahc::HttpClient;
const PROTO_HTTP: u8 = 0;
const PROTO_HTTPS: u8 = 1;
struct HttpAssetReader<const PROTO: u8>(HttpClient);
impl<const PROTO: u8> AssetReader for HttpAssetReader<PROTO> {
fn read<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let proto = match PROTO {
PROTO_HTTP => "http",
PROTO_HTTPS => "https",
_ => unreachable!(),
};
let uri = (|| { Uri::from_str(&format!("{}://{}", proto, path.to_str()?)).ok() })();
let Some(uri) = uri else {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid path").into());
};
let response = match self.0.get_async(uri).await {
Ok(response) => response,
Err(err) => {
let err: std::io::Error = err.into();
return Err(err.into());
}
};
if !response.status().is_success() {
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, response.status().to_string()).into());
}
let body = response.into_body();
let reader: Box<Reader> = Box::new(body);
Ok(reader)
})
}
fn read_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
fn read_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
fn is_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<bool, AssetReaderError>> {
Box::pin(async move { Err(AssetReaderError::NotFound(path.to_path_buf())) })
}
}
#[allow(clippy::redundant_clone)]
fn main() {
let client = HttpClient::builder()
.max_connections(10)
.max_connections_per_host(10)
.tcp_keepalive(Duration::from_secs(10))
.redirect_policy(RedirectPolicy::Follow)
.build()
.unwrap();
App::new()
.register_asset_source("http", AssetSource::build().with_reader({
let client = client.clone();
move || Box::new(HttpAssetReader::<PROTO_HTTP>(client.clone()))
}))
.register_asset_source("https", AssetSource::build().with_reader({
let client = client.clone();
move || Box::new(HttpAssetReader::<PROTO_HTTPS>(client.clone()))
}))
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, bevy::window::close_on_esc)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.7, 0.7, 1.).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
..default()
});
commands.spawn(DirectionalLightBundle::default());
commands.spawn(SceneBundle {
scene: asset_server.load("https://github.com/bevyengine/bevy/raw/main/assets/models/AlienCake/cakeBirthday.glb#Scene0"),
..default()
});
}
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
With the new Assets V2 system, I think your best shot is to implement a custom
AssetSource
which uses TCP to read the assets from the other app.The documentation still seems to be a bit sparse, but the Bevy 0.12 announcement post gives an introduction on this topic.
From what I understand you would need to implement a custom
AssetReader
and then configure a newAssetSource
to use that reader.Then you could do something like
Maybe the
FileAssetReader
can serve as reference.Hope that helps :)