use glam::{Vec3, Vec4};
use image::{DynamicImage, ImageBuffer, Luma, LumaA, Rgb, Rgba};
use jpeg2k::{Image, ImagePixelData};
use log::warn;
use metaverse_agent::skeleton::create_skeleton;
use metaverse_messages::http::login::login_error::{LoginError, Reason};
use metaverse_messages::http::login::login_response::{LoginResponse, LoginStatus};
use metaverse_messages::http::login::simulator_login_protocol::SimulatorLoginProtocol;
use metaverse_messages::http::mesh::Mesh;
use metaverse_messages::http::{item::Item, scene::SceneGroup};
use metaverse_messages::ui::login_event::Login;
use metaverse_messages::utils::object_types::ObjectType;
use metaverse_messages::utils::render_data::{RenderObject, SkinData};
use metaverse_messages::utils::skeleton::Skeleton;
use std::io::Error;
use std::path::PathBuf;
use uuid::Uuid;
pub async fn login_to_simulator(
login: Login,
) -> Result<(LoginResponse, std::net::IpAddr), LoginError> {
let url = login.url.clone();
let local_ip = {
let parsed_url = url::Url::parse(&url).map_err(|e| LoginError {
reason: Reason::Connection,
message: format!("Invalid URL: {:?}", e),
})?;
let host = parsed_url.host_str().ok_or(LoginError {
reason: Reason::Connection,
message: "URL has no host".into(),
})?;
let port = parsed_url.port_or_known_default().ok_or(LoginError {
reason: Reason::Connection,
message: "URL has no port".into(),
})?;
let stream = std::net::TcpStream::connect((host, port)).map_err(|e| LoginError {
reason: Reason::Connection,
message: format!("TCP connect failed: {:?}", e),
})?;
stream
.local_addr()
.map(|addr| addr.ip())
.map_err(|e| LoginError {
reason: Reason::Connection,
message: format!("Failed to get local IP: {:?}", e),
})?
};
let client = awc::Client::default();
let xml = SimulatorLoginProtocol::new(login).to_xmlrpc();
let mut response = client
.post(&url)
.insert_header(("Content-Type", "text/xml; charset=utf-8"))
.send_body(xml)
.await
.map_err(|e| LoginError {
reason: Reason::Connection,
message: format!("{:?}", e),
})?;
let body_bytes = response.body().await.map_err(|e| LoginError {
reason: Reason::Connection,
message: format!("{:?}", e),
})?;
let xml_string = String::from_utf8(body_bytes.to_vec()).unwrap();
match LoginResponse::from_xmlrpc(&xml_string) {
Ok(login) => match login {
LoginStatus::Success(success) => Ok((*success, local_ip)),
LoginStatus::Failure(failure) => Err(failure),
},
Err(e) => Err(e)?,
}
}
pub async fn download_asset(
item_type: String,
asset_id: Uuid,
server_endpoint: &str,
) -> std::io::Result<bytes::Bytes> {
let client = awc::Client::default();
let url = format!("{}?{}_id={}", server_endpoint, item_type, asset_id);
let mut response = client
.get(&url)
.send()
.await
.map_err(|e| io_error("Failed to send HTTP GET request", e))?;
let body_bytes = response
.body()
.await
.map_err(|e| io_error("Failed to read response body", e))?;
if body_bytes.is_empty() {
return Err(Error::other("Empty response body"));
}
Ok(body_bytes)
}
pub async fn download_object(
item_type: String,
asset_id: Uuid,
server_endpoint: &str,
) -> std::io::Result<SceneGroup> {
SceneGroup::from_xml(&download_asset(item_type, asset_id, server_endpoint).await?)
.map_err(|e| Error::other(format!("Failed to parse object: {}", e)))
}
pub async fn download_item(
item_type: String,
asset_id: Uuid,
server_endpoint: &str,
) -> std::io::Result<Item> {
Item::from_bytes(&download_asset(item_type, asset_id, server_endpoint).await?)
.map_err(|e| Error::other(format!("Failed to parse item: {}", e)))
}
pub async fn download_mesh(
item_type: String,
asset_id: Uuid,
server_endpoint: &str,
) -> std::io::Result<Mesh> {
Mesh::from_bytes(&download_asset(item_type, asset_id, server_endpoint).await?)
.map_err(|e| Error::other(format!("Failed to parse SceneGroup XML: {}", e)))
}
pub async fn download_texture(
item_type: String,
asset_id: Uuid,
server_endpoint: &str,
path: &PathBuf,
) -> std::io::Result<DynamicImage> {
let tex = &download_asset(item_type, asset_id, server_endpoint).await?;
let img = Image::from_bytes(tex).unwrap();
let pixels = img.get_pixels(None).unwrap();
let output = match pixels.data {
ImagePixelData::L8(data) => {
ImageBuffer::<Luma<u8>, _>::from_raw(pixels.width, pixels.height, data)
.map(DynamicImage::ImageLuma8)
.unwrap()
}
ImagePixelData::La8(data) => {
ImageBuffer::<LumaA<u8>, _>::from_raw(pixels.width, pixels.height, data)
.map(DynamicImage::ImageLumaA8)
.unwrap()
}
ImagePixelData::Rgb8(data) => {
ImageBuffer::<Rgb<u8>, _>::from_raw(pixels.width, pixels.height, data)
.map(DynamicImage::ImageRgb8)
.unwrap()
}
ImagePixelData::Rgba8(data) => {
ImageBuffer::<Rgba<u8>, _>::from_raw(pixels.width, pixels.height, data)
.map(DynamicImage::ImageRgba8)
.unwrap()
}
_ => return Err(Error::other("Unknown pixel format".to_string())),
};
output.save(&path).unwrap();
Ok(output)
}
fn io_error(msg: &str, err: impl std::fmt::Debug) -> std::io::Error {
Error::other(format!("{}: {:?}", msg, err))
}
pub async fn download_scene_group(
scene_group: &SceneGroup,
url: &str,
texture_path: &PathBuf,
) -> Result<Vec<RenderObject>, std::io::Error> {
let mut meshes = Vec::new();
for scene in &scene_group.parts {
meshes.push(
download_renderable_mesh(
scene.sculpt.texture,
scene.metadata.name.clone(),
url,
texture_path,
)
.await?,
);
}
Ok(meshes)
}
pub async fn download_renderable_mesh(
asset_id: Uuid,
name: String,
url: &str,
texture_path: &PathBuf,
) -> Result<RenderObject, std::io::Error> {
let mesh = download_mesh(ObjectType::Mesh.to_string(), asset_id, url).await?;
let domain = &mesh.high_level_of_detail.texture_coordinate_domain;
let uvs: Vec<[f32; 2]> = mesh
.high_level_of_detail
.texture_coordinate
.iter()
.map(|tc| {
let u_norm = tc.u as f32 / 65535.0;
let v_norm = tc.v as f32 / 65535.0;
let v_flipped = 1.0 - v_norm;
[
domain.min[0] + u_norm * (domain.max[0] - domain.min[0]),
domain.min[1] + v_flipped * (domain.max[1] - domain.min[1]),
]
})
.collect();
let object = if let Some(skin) = &mesh.skin {
let vertices: Vec<Vec3> = mesh
.high_level_of_detail
.vertices
.iter()
.map(|v| {
let v4 = skin.bind_shape_matrix * Vec4::new(v.x, v.y, v.z, 1.0);
Vec3::new(v4.x, v4.y, v4.z)
})
.collect();
let skeleton = create_skeleton(name.clone(), asset_id, skin).unwrap_or_else(|e| {
warn!("Failed to create skeleton: {:?}", e);
Skeleton::default()
});
let skin_data = SkinData {
skeleton,
weights: mesh
.high_level_of_detail
.weights
.clone()
.unwrap_or_default(),
joint_names: skin.joint_names.clone(),
inverse_bind_matrices: skin.inverse_bind_matrices.clone(),
};
RenderObject {
name: name,
id: asset_id,
indices: mesh.high_level_of_detail.indices,
vertices,
skin: Some(skin_data),
texture: Some(texture_path.clone()),
uv: Some(uvs),
}
} else {
let vertices: Vec<Vec3> = mesh.high_level_of_detail.vertices;
RenderObject {
name: name,
id: asset_id,
indices: mesh.high_level_of_detail.indices,
vertices,
skin: None,
texture: Some(texture_path.clone()),
uv: Some(uvs),
}
};
Ok(object)
}