use std::{collections::BTreeSet, fs, path::PathBuf};
use benthic_protocol::{
render_data::{AvatarObject, RenderObject},
session::{CacheDir, Session, cache_enabled, create_sub_agent_dir, write_json},
skeleton::{JointName, Skeleton},
};
use glam::Mat4;
use indexmap::IndexMap;
use metaverse_mesh::mesh::generate::generate_skinned_mesh;
use uuid::Uuid;
use crate::{
avatar::{Avatar, OutfitObject},
avatar_object_handler::AvatarState::{FullyLoaded, Incomplete},
errors::AvatarError,
skeleton::update_global_avatar_skeleton,
};
pub enum AvatarState {
FullyLoaded,
Incomplete,
}
pub enum AvatarType {
User,
NonUser,
}
pub fn init_avatar<C, L, U>(
session: &mut Session<C, Avatar, L, U>,
avatar: &Avatar,
) -> Result<AvatarType, AvatarError> {
if session.agent_id == avatar.agent_id {
if session.inventory_data.inventory_init {
session.avatars.insert(avatar.agent_id, avatar.clone());
Ok(AvatarType::User)
} else {
Err(AvatarError::InventoryUninitialized {})
}
} else {
Err(AvatarError::Unimplemented {
feature: "Non-Agent avatars".to_string(),
})
}
}
pub fn add_object_to_avatar<C, L, U>(
session: &mut Session<C, Avatar, L, U>,
agent_id: Uuid,
object: OutfitObject,
) -> Result<AvatarState, AvatarError> {
let avatar = session
.avatars
.get_mut(&agent_id)
.ok_or(AvatarError::AgentNotFound { agent: agent_id })?;
match &object {
OutfitObject::MeshObject(path) => {
let file = fs::File::open(path)?;
let parts: Vec<RenderObject> = serde_json::from_reader(file)?;
if let Some(skin) = &parts[0].skin {
update_global_avatar_skeleton(avatar, &skin.skeleton);
}
}
_ => {
}
}
avatar.items.push(object);
if avatar.items.len() == avatar.outfit_size {
Ok(FullyLoaded)
} else {
Ok(Incomplete)
}
}
pub async fn finalize_avatar(
agent_id: Uuid,
skeleton: Skeleton,
used_joints: BTreeSet<JointName>,
items: Vec<OutfitObject>,
) -> Result<(PathBuf, Skeleton), AvatarError> {
let skeleton = finalize_skeleton(skeleton);
let json_paths: Vec<PathBuf> = items
.into_iter()
.filter_map(|item| {
if let OutfitObject::MeshObject(path) = item {
Some(path)
} else {
None
}
})
.collect();
let avatar_object = AvatarObject {
objects: json_paths,
global_skeleton: skeleton.clone(),
used_joints: used_joints.clone(),
};
let json_path_str = agent_id.to_string();
let json_path = PathBuf::from(&json_path_str);
let json_path = if json_path.exists() && cache_enabled() {
json_path.clone()
} else {
write_json(&avatar_object, &json_path_str, CacheDir::Agent(agent_id))?
};
let base_dir = create_sub_agent_dir(&agent_id.to_string())?;
let glb_path = base_dir.join(format!("{:?}_high.glb", agent_id));
if !glb_path.exists() || !cache_enabled() {
generate_skinned_mesh(json_path.clone(), glb_path.clone())?
}
Ok((glb_path, skeleton))
}
fn finalize_skeleton(mut skeleton: Skeleton) -> Skeleton {
let globals: IndexMap<JointName, Mat4> = skeleton
.joints
.iter()
.map(|(name, joint)| (*name, joint.global_transforms.last().unwrap().transform))
.collect();
for joint in skeleton.joints.values_mut() {
let global = globals[&joint.name];
let local = match joint.parent {
Some(parent_name) => globals[&parent_name].inverse() * global,
None => global,
};
joint.local_transforms.last_mut().unwrap().transform = local;
}
skeleton
}