use js_sys::{Array, Object, Promise, Uint8Array};
use std::cell::RefCell;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use crate::{AttributeDataType, DracoDecodeConfig};
thread_local! {
static DRACO_DECODE_FUNC_MODULE: RefCell<Option<JsValue>> = RefCell::new(None);
}
async fn get_js_module() -> Result<JsValue, JsValue> {
if let Some(module) = DRACO_DECODE_FUNC_MODULE.with(|m| m.borrow().clone()) {
return Ok(module);
}
let js_code = include_str!("../javascript/index.es.js");
let escaped = js_code.replace("\\", "\\\\").replace("`", "\\`");
let setup_code = format!(
r#"
(function() {{
const code = `{escaped}`;
const blob = new Blob([code], {{ type: "application/javascript" }});
const url = URL.createObjectURL(blob);
return import(url).then(mod => {{
URL.revokeObjectURL(url);
return mod;
}});
}})()
"#
);
let js_module = js_sys::eval(&setup_code)?;
let module_promise: Promise = js_module.dyn_into()?;
let module = JsFuture::from(module_promise).await?;
DRACO_DECODE_FUNC_MODULE.with(|m| m.replace(Some(module.clone())));
Ok(module)
}
async fn decode_draco_mesh_from_embedded_js(
data: &js_sys::Uint8Array,
byte_length: usize,
) -> Result<js_sys::Uint8Array, JsValue> {
let module = get_js_module().await?;
let decode_fn = js_sys::Reflect::get(&module, &JsValue::from_str("decodeDracoMeshInWorker"))?
.dyn_into::<js_sys::Function>()?;
let this = JsValue::NULL;
let result = decode_fn.call2(&this, data, &JsValue::from(byte_length))?;
let decode_promise: Promise = result.dyn_into()?;
let out_buf = JsFuture::from(decode_promise).await?;
Ok(out_buf.dyn_into::<Uint8Array>()?)
}
pub async fn decode_mesh_wasm_worker(data: &[u8], config: &DracoDecodeConfig) -> Option<Vec<u8>> {
let js_array = Uint8Array::from(data);
let estimate_buffer_size = config.estimate_buffer_size();
match decode_draco_mesh_from_embedded_js(&js_array, estimate_buffer_size).await {
Ok(decoded) => Some(decoded.to_vec()),
Err(err) => {
web_sys::console::error_1(&err);
None
}
}
}
async fn decode_draco_mesh_from_embedded_js_with_config(
data: &js_sys::Uint8Array,
) -> Result<(Vec<u8>, DracoDecodeConfig), JsValue> {
let module = get_js_module().await?;
let decode_fn = js_sys::Reflect::get(
&module,
&JsValue::from_str("decodeDracoMeshInWorkerWithConfig"),
)?
.dyn_into::<js_sys::Function>()?;
let this = JsValue::NULL;
let result = decode_fn.call1(&this, data)?;
let decode_promise: Promise = result.dyn_into()?;
let out_obj = JsFuture::from(decode_promise).await?;
let decoded_array =
js_sys::Reflect::get(&out_obj, &JsValue::from_str("decoded"))?.dyn_into::<Uint8Array>()?;
let config_obj = js_sys::Reflect::get(&out_obj, &JsValue::from_str("config"))?;
let vertex_count = js_sys::Reflect::get(&config_obj, &JsValue::from_str("vertex_count"))?
.as_f64()
.unwrap_or(0.0) as u32;
let index_count = js_sys::Reflect::get(&config_obj, &JsValue::from_str("index_count"))?
.as_f64()
.unwrap_or(0.0) as u32;
let attributes_array =
js_sys::Reflect::get(&config_obj, &JsValue::from_str("attributes"))?.dyn_into::<Array>()?;
let mut config = DracoDecodeConfig::new(vertex_count, index_count);
for i in 0..attributes_array.length() {
let attr_obj = attributes_array.get(i).dyn_into::<Object>()?;
let dim = js_sys::Reflect::get(&attr_obj, &JsValue::from_str("dim"))?
.as_f64()
.unwrap_or(0.0) as u32;
let data_type = js_sys::Reflect::get(&attr_obj, &JsValue::from_str("data_type"))?
.as_f64()
.unwrap_or(0.0) as i32;
let attr_data_type = match data_type {
0 => AttributeDataType::Int8,
1 => AttributeDataType::UInt8,
2 => AttributeDataType::Int16,
3 => AttributeDataType::UInt16,
4 => AttributeDataType::Int32,
5 => AttributeDataType::UInt32,
6 => AttributeDataType::Float32,
_ => AttributeDataType::Float32,
};
config.add_attribute(dim, attr_data_type);
}
Ok((decoded_array.to_vec(), config))
}
pub async fn decode_mesh_wasm_worker_with_config(data: &[u8]) -> Option<crate::MeshDecodeResult> {
let js_array = Uint8Array::from(data);
match decode_draco_mesh_from_embedded_js_with_config(&js_array).await {
Ok((decoded, config)) => Some(crate::MeshDecodeResult {
data: decoded,
config,
}),
Err(err) => {
web_sys::console::error_1(&err);
None
}
}
}