Some checks failed
Native code generation / deterministic (push) Failing after 2m18s
Imaging and meshing gate / native (push) Failing after 1m30s
JPEG 2000 feature / linux (push) Successful in 2m40s
Native Rust workspace compile / compile (push) Failing after 57s
Skia feature / linux (push) Successful in 31m8s
193 lines
6.1 KiB
Rust
193 lines
6.1 KiB
Rust
//! `ModifyMaterialParams` capability integration.
|
|
|
|
#![allow(
|
|
clippy::missing_errors_doc,
|
|
clippy::must_use_candidate,
|
|
clippy::option_option
|
|
)]
|
|
|
|
use crate::assets::AssetMaterial;
|
|
use crate::{Error, GridClient, Simulator};
|
|
use libremetaverse_structured_data::{OSD, OSDParser};
|
|
use libremetaverse_types::{UUID, compat::CancellationToken};
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct GLTFMaterialUpdate {
|
|
asset_id: Option<Option<UUID>>,
|
|
object_id: UUID,
|
|
override_: Option<AssetMaterial>,
|
|
side: i32,
|
|
}
|
|
impl GLTFMaterialUpdate {
|
|
pub fn new() -> Result<Self, Error> {
|
|
Ok(Self {
|
|
asset_id: None,
|
|
object_id: UUID::zero(),
|
|
override_: None,
|
|
side: 0,
|
|
})
|
|
}
|
|
pub fn asset_id(&self) -> Option<Option<UUID>> {
|
|
self.asset_id
|
|
}
|
|
pub fn set_asset_id(&mut self, value: Option<Option<UUID>>) {
|
|
self.asset_id = value;
|
|
}
|
|
pub const fn object_id(&self) -> UUID {
|
|
self.object_id
|
|
}
|
|
pub fn set_object_id(&mut self, value: UUID) {
|
|
self.object_id = value;
|
|
}
|
|
pub fn override_(&self) -> Option<AssetMaterial> {
|
|
self.override_.clone()
|
|
}
|
|
pub fn set_override_(&mut self, value: Option<AssetMaterial>) {
|
|
self.override_ = value;
|
|
}
|
|
pub const fn side(&self) -> i32 {
|
|
self.side
|
|
}
|
|
pub fn set_side(&mut self, value: i32) {
|
|
self.side = value;
|
|
}
|
|
}
|
|
|
|
pub struct ObjectManager {
|
|
client: crate::client_core::ClientWeakHandle,
|
|
}
|
|
impl ObjectManager {
|
|
pub(crate) fn native_new(client: Option<Arc<GridClient>>) -> Result<Self, Error> {
|
|
Ok(Self {
|
|
client: client.ok_or(Error::ArgumentNull)?.native_weak_handle(),
|
|
})
|
|
}
|
|
fn client(&self) -> Result<GridClient, Error> {
|
|
self.client.upgrade().ok_or(Error::InvalidOperation)
|
|
}
|
|
|
|
pub(crate) async fn native_send_material_updates(
|
|
&self,
|
|
sim: Simulator,
|
|
updates: Box<dyn Iterator<Item = GLTFMaterialUpdate>>,
|
|
cancellation_token: Option<CancellationToken>,
|
|
) -> Result<bool, Error> {
|
|
let Some(caps) = sim.native_caps() else {
|
|
return Ok(false);
|
|
};
|
|
let deadline = Instant::now() + Duration::from_secs(2);
|
|
let uri = loop {
|
|
if let Some(uri) = caps.capability_uri("ModifyMaterialParams".into())? {
|
|
break uri;
|
|
}
|
|
if caps.seed_request_finished() || Instant::now() >= deadline {
|
|
return Ok(false);
|
|
}
|
|
std::thread::sleep(Duration::from_millis(1));
|
|
};
|
|
let mut body = Vec::new();
|
|
for update in updates {
|
|
if update.side < 0 {
|
|
return Err(Error::Argument);
|
|
}
|
|
let mut entry = HashMap::from([
|
|
("object_id".into(), OSD::UUID(update.object_id)),
|
|
("side".into(), OSD::Integer(update.side)),
|
|
]);
|
|
if let Some(Some(asset_id)) = update.asset_id {
|
|
entry.insert("asset_id".into(), OSD::UUID(asset_id));
|
|
}
|
|
if let Some(material) = update.override_ {
|
|
entry.insert("gltf_json".into(), OSD::String(material.to_json()?));
|
|
} else if update.asset_id.flatten().is_none() {
|
|
entry.insert("gltf_json".into(), OSD::String(String::new()));
|
|
}
|
|
body.push(OSD::Map(entry));
|
|
}
|
|
if body.is_empty() {
|
|
return Err(Error::Argument);
|
|
}
|
|
let payload = OSDParser::serialize_llsd_xml_bytes(OSD::Array(body))?;
|
|
let client = self.client()?;
|
|
let (response, bytes) = client
|
|
.native_http_caps_client()
|
|
.post_with_uri_string_bytes_cancellation_token_i_progress(
|
|
uri,
|
|
"application/llsd+xml".into(),
|
|
payload,
|
|
cancellation_token.unwrap_or_default(),
|
|
None,
|
|
)
|
|
.await?;
|
|
if !(200..300).contains(&response.status_code) {
|
|
return Ok(false);
|
|
}
|
|
let response: serde_json::Value =
|
|
serde_json::from_slice(&bytes).map_err(|_| Error::Argument)?;
|
|
Ok(response
|
|
.get("success")
|
|
.and_then(serde_json::Value::as_bool)
|
|
.unwrap_or(false))
|
|
}
|
|
pub(crate) async fn native_set_material_override(
|
|
&self,
|
|
sim: Simulator,
|
|
object_id: UUID,
|
|
side: i32,
|
|
override_material: AssetMaterial,
|
|
cancellation_token: Option<CancellationToken>,
|
|
) -> Result<bool, Error> {
|
|
let mut update = GLTFMaterialUpdate::new()?;
|
|
update.object_id = object_id;
|
|
update.side = side;
|
|
update.override_ = Some(override_material);
|
|
self.native_send_material_updates(
|
|
sim,
|
|
Box::new(std::iter::once(update)),
|
|
cancellation_token,
|
|
)
|
|
.await
|
|
}
|
|
pub(crate) async fn native_clear_material_override(
|
|
&self,
|
|
sim: Simulator,
|
|
object_id: UUID,
|
|
side: i32,
|
|
cancellation_token: Option<CancellationToken>,
|
|
) -> Result<bool, Error> {
|
|
let mut update = GLTFMaterialUpdate::new()?;
|
|
update.object_id = object_id;
|
|
update.side = side;
|
|
self.native_send_material_updates(
|
|
sim,
|
|
Box::new(std::iter::once(update)),
|
|
cancellation_token,
|
|
)
|
|
.await
|
|
}
|
|
pub(crate) async fn native_apply_material(
|
|
&self,
|
|
sim: Simulator,
|
|
object_id: UUID,
|
|
side: i32,
|
|
material_asset_id: UUID,
|
|
override_material: Option<AssetMaterial>,
|
|
cancellation_token: Option<CancellationToken>,
|
|
) -> Result<bool, Error> {
|
|
let mut update = GLTFMaterialUpdate::new()?;
|
|
update.object_id = object_id;
|
|
update.side = side;
|
|
update.asset_id = Some(Some(material_asset_id));
|
|
update.override_ = override_material;
|
|
self.native_send_material_updates(
|
|
sim,
|
|
Box::new(std::iter::once(update)),
|
|
cancellation_token,
|
|
)
|
|
.await
|
|
}
|
|
}
|