feat: enhance model loading with URL support and downloader initialization
This commit is contained in:
@@ -5,7 +5,7 @@ mod model_downloader;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
|
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
@@ -23,6 +23,9 @@ use tokio::runtime::Runtime;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use model_downloader::ModelDownloader;
|
use model_downloader::ModelDownloader;
|
||||||
|
|
||||||
|
// Global model downloader instance
|
||||||
|
static MODEL_DOWNLOADER: OnceLock<ModelDownloader> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
struct BridgeState {
|
struct BridgeState {
|
||||||
nodes: HashMap<u64, Node>,
|
nodes: HashMap<u64, Node>,
|
||||||
@@ -77,7 +80,42 @@ impl Reify for BridgeState {
|
|||||||
|
|
||||||
eprintln!("[bridge/reify] Reifying {} nodes", self.nodes.len());
|
eprintln!("[bridge/reify] Reifying {} nodes", self.nodes.len());
|
||||||
|
|
||||||
fn get_model_path(entity_type: u8) -> Option<PathBuf> {
|
// Initialize model downloader if not already done
|
||||||
|
let downloader = MODEL_DOWNLOADER.get_or_init(|| {
|
||||||
|
let cache_dir = dirs::cache_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||||
|
.join("starworld/models");
|
||||||
|
ModelDownloader::new(cache_dir).expect("Failed to create model downloader")
|
||||||
|
});
|
||||||
|
|
||||||
|
fn get_model_path(entity_type: u8, model_url: &str, downloader: &ModelDownloader) -> Option<PathBuf> {
|
||||||
|
// First check if there's a model URL provided
|
||||||
|
if !model_url.is_empty() {
|
||||||
|
// Handle HTTP/HTTPS URLs
|
||||||
|
if model_url.starts_with("http://") || model_url.starts_with("https://") {
|
||||||
|
eprintln!("[bridge/reify] Attempting to download model from URL: {}", model_url);
|
||||||
|
if let Some(path) = downloader.get_model(model_url) {
|
||||||
|
eprintln!("[bridge/reify] Using downloaded model: {}", path.display());
|
||||||
|
return Some(path);
|
||||||
|
} else {
|
||||||
|
eprintln!("[bridge/reify] Model download failed or in progress, falling back to primitive");
|
||||||
|
// Fall through to use primitive based on entity type
|
||||||
|
}
|
||||||
|
} else if model_url.starts_with("file://") {
|
||||||
|
// Local file path
|
||||||
|
let path = PathBuf::from(&model_url[7..]);
|
||||||
|
if path.exists() {
|
||||||
|
eprintln!("[bridge/reify] Using local file: {}", path.display());
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
} else if PathBuf::from(model_url).exists() {
|
||||||
|
// Direct path
|
||||||
|
eprintln!("[bridge/reify] Using direct path: {}", model_url);
|
||||||
|
return Some(PathBuf::from(model_url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to primitive models based on entity type
|
||||||
let cache_dir = dirs::cache_dir()?.join("starworld/primitives");
|
let cache_dir = dirs::cache_dir()?.join("starworld/primitives");
|
||||||
let filename = match entity_type {
|
let filename = match entity_type {
|
||||||
1 => "cube.glb", // Box
|
1 => "cube.glb", // Box
|
||||||
@@ -89,7 +127,7 @@ impl Reify for BridgeState {
|
|||||||
if path.exists() {
|
if path.exists() {
|
||||||
Some(path)
|
Some(path)
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[bridge/reify] Model file not found: {}", path.display());
|
eprintln!("[bridge/reify] Primitive model file not found: {}", path.display());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,18 +147,35 @@ impl Reify for BridgeState {
|
|||||||
let scale_array = [vis_scale.x, vis_scale.y, vis_scale.z];
|
let scale_array = [vis_scale.x, vis_scale.y, vis_scale.z];
|
||||||
let transform = stardust_xr_fusion::spatial::Transform::from_translation_rotation_scale(trans_array, rot_array, scale_array);
|
let transform = stardust_xr_fusion::spatial::Transform::from_translation_rotation_scale(trans_array, rot_array, scale_array);
|
||||||
|
|
||||||
// Try to load the appropriate model based on entity type
|
// Try to load the appropriate model based on entity type and model URL
|
||||||
let model_child = if let Some(model_path) = get_model_path(node.entity_type) {
|
let model_child = if let Some(model_path) = get_model_path(node.entity_type, &node.model_url, downloader) {
|
||||||
eprintln!("[bridge/reify] Loading {} model for node {} from {}",
|
let entity_type_name = match node.entity_type {
|
||||||
match node.entity_type {
|
1 => "cube",
|
||||||
1 => "cube",
|
2 => "sphere",
|
||||||
2 => "sphere",
|
3 => "3D model",
|
||||||
3 => "3D model",
|
_ => "unknown"
|
||||||
_ => "unknown"
|
};
|
||||||
}, id, model_path.display());
|
|
||||||
|
let model_source = if !node.model_url.is_empty() {
|
||||||
|
format!("from URL: {}", node.model_url)
|
||||||
|
} else {
|
||||||
|
format!("primitive from {}", model_path.display())
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("[bridge/reify] Loading {} for node {} {}",
|
||||||
|
entity_type_name, id, model_source);
|
||||||
|
|
||||||
match Model::direct(&model_path) {
|
match Model::direct(&model_path) {
|
||||||
Ok(model) => Some(model.build()),
|
Ok(model) => {
|
||||||
|
// TODO: Apply color tint to the model
|
||||||
|
// This would require material manipulation which is not yet exposed
|
||||||
|
// in the asteroids API. For now, just log it.
|
||||||
|
if node.color != [1.0, 1.0, 1.0, 1.0] {
|
||||||
|
eprintln!("[bridge/reify] Node {} has color tint: {:?} (not yet applied)",
|
||||||
|
id, node.color);
|
||||||
|
}
|
||||||
|
Some(model.build())
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
|
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
|
||||||
None
|
None
|
||||||
|
|||||||
Reference in New Issue
Block a user