Skip to main content

modelassetlib_native/jniimpl/
gltf.rs

1extern crate jni;
2extern crate gltf;
3extern crate anyhow;
4
5use jni::JNIEnv;
6use jni::objects::{JByteArray, JObject, JString, JValue, JValueOwned};
7use anyhow::Result;
8use gltf::{buffer, image};
9use jni::sys::{jbyte, jbyteArray, jsize};
10use crate::util;
11use crate::util::gltf::{
12    LoadedGltfAccessor, LoadedGltf, LoadedGltfBuffer,
13    LoadedGltfWrapper, LoadedGltfImage, LoadedGltfBufferView};
14
15pub fn get_native_callback<'a>(
16    env: &mut JNIEnv<'a>,
17    this: &JObject<'a>
18) -> Result<JObject<'a>> {
19    let callback = env.get_field(this, "nativeCallback", "Ltop/srcres/mods/modelassetlib/gltf/Gltf$NativeCallback;");
20    match callback {
21        Ok(callback) => {
22            Ok(callback.l().unwrap())
23        }
24        Err(err) => {
25            util::jni::clear_exception_if_occurred(env);
26            util::jni::throw_runtime_exception(env, &format!("Failed to obtain nativeCallback: {}", err)).unwrap();
27            Err(err.into())
28        }
29    }
30}
31
32pub fn invoke_native_callback<'a>(
33    env: &mut JNIEnv<'a>,
34    this: &JObject<'a>,
35    name: &'a str,
36    sig: &'a str,
37    args: &[JValue]
38) -> Result<JValueOwned<'a>> {
39    let callback = get_native_callback(env, this)?;
40    let result = env.call_method(callback, name, sig, args);
41    match result {
42        Ok(result) => {
43            Ok(result)
44        }
45        Err(err) => {
46            util::jni::clear_exception_if_occurred(env);
47            util::jni::throw_runtime_exception(env, &format!(
48                "Failed to invoke {} {}: {}", name, sig, err)).unwrap();
49            Err(err.into())
50        }
51    }
52}
53
54pub fn invoke_native_callback_no_args<'a>(
55    env: &mut JNIEnv<'a>,
56    this: &JObject<'a>,
57    name: &'a str,
58    sig: &'a str
59) -> Result<JValueOwned<'a>> {
60    invoke_native_callback(env, this, name, sig, &[])
61}
62
63pub fn get_initial_gltf_data<'a>(
64    env: &mut JNIEnv<'a>,
65    this: &JObject<'a>
66) -> Result<JByteArray<'a>> {
67    let callback = get_native_callback(env, this)?;
68    Ok(JByteArray::from(env.call_method(callback, "getInitialGltfData", "()[B", &[])?.l()?))
69}
70
71fn init_gltf<'a>(
72    env: &mut JNIEnv<'a>,
73    this: &JObject<'a>
74) {
75    let gltf_data = get_initial_gltf_data(env, this);
76    match gltf_data {
77        Ok(gltf_data) => {
78            let gltf_data_len = env.get_array_length(&gltf_data).unwrap();
79            let mut gltf_data_vec: Vec<jbyte> = util::new_buffer_vec(gltf_data_len as usize, 0);
80            env.get_byte_array_region(&gltf_data, 0, gltf_data_vec.as_mut_slice()).unwrap();
81            let gltf_data_u8: Vec<_> = gltf_data_vec.iter().map(|it| *it as u8).collect();
82            let gltf_obj = gltf::Gltf::from_slice(gltf_data_u8.as_slice());
83            match gltf_obj {
84                Ok(gltf_obj) => {
85                    unsafe {
86                        env.set_rust_field(this, "rust_gltfObj", gltf_obj).unwrap()
87                    }
88                }
89                Err(err) => {
90                    util::jni::clear_exception_if_occurred(env);
91                    util::jni::throw_runtime_exception(env, &format!("Failed to create the glTF object: {}", err)).unwrap();
92                }
93            }
94        }
95        Err(_) => {
96            ()
97        }
98    }
99}
100
101pub fn load_gltf<'a>(
102    env: &mut JNIEnv<'a>,
103    this: &JObject<'a>
104) {
105    let gltf_obj: gltf::Gltf;
106    unsafe {
107        gltf_obj = env.take_rust_field(this, "rust_gltfObj").unwrap();
108    }
109
110    let loaded_gltf = LoadedGltf::new();
111    let loaded_gltf_wrapper = LoadedGltfWrapper::new(loaded_gltf);
112
113    // Load buffers.
114    gltf_obj.buffers().for_each(|it| {
115        let mut loaded_gltf = loaded_gltf_wrapper.get().lock().unwrap();
116        if let buffer::Source::Uri(uri) = it.source() {
117            let uri_jstr = env.new_string(uri).unwrap();
118            let data_arr = invoke_native_callback(
119                env, this, "loadBufferFromURI", "(Ljava/lang/String;)[B",
120                &[JValue::Object(&uri_jstr)]);
121            match data_arr {
122                Ok(data_arr) => {
123                    let data_arr = JByteArray::from(data_arr.l().unwrap());
124                    let data_arr_len = env.get_array_length(&data_arr).unwrap();
125                    let mut data = util::new_buffer_vec(data_arr_len as usize, 0);
126                    env.get_byte_array_region(data_arr, 0, data.as_mut_slice()).unwrap();
127                    let buf = LoadedGltfBuffer::new(
128                        loaded_gltf_wrapper.get(), it.index(), String::from(uri),
129                        data.iter().map(|x| *x as u8).collect());
130                    loaded_gltf.get_buffers_mut().push(buf);
131                }
132                Err(err) => {
133                    util::jni::clear_exception_if_occurred(env);
134                    util::jni::throw_runtime_exception(env, &format!("Failed to load glTF buffer: {}", err)).unwrap();
135                    return;
136                }
137            }
138        }
139    });
140
141    // Load buffer views.
142    gltf_obj.views().for_each(|it| {
143        let mut loaded_gltf = loaded_gltf_wrapper.get().lock().unwrap();
144        let loaded_buffer_view = LoadedGltfBufferView::new_from_view(
145            loaded_gltf_wrapper.get(), &it);
146        loaded_gltf.get_buffer_views_mut().push(loaded_buffer_view);
147    });
148
149    // Load accessors.
150    gltf_obj.accessors().for_each(|it| {
151        let mut loaded_gltf = loaded_gltf_wrapper.get().lock().unwrap();
152        let loaded_accessor = LoadedGltfAccessor::new_from_accessor(
153            loaded_gltf_wrapper.get(), &it).unwrap();
154        loaded_gltf.get_accessors_mut().push(loaded_accessor);
155    });
156
157    // Load images.
158    gltf_obj.images().for_each(|it| {
159        let mut loaded_gltf = loaded_gltf_wrapper.get().lock().unwrap();
160        if let image::Source::Uri { uri, mime_type } = it.source() {
161            let mime_type = mime_type.unwrap_or("");
162            let uri_jstr = env.new_string(uri).unwrap();
163            let mime_type_jstr = env.new_string(mime_type).unwrap();
164            let data_arr = invoke_native_callback(
165                env, this, "loadImageFromURI", "(Ljava/lang/String;Ljava/lang/String;)[B",
166                &[JValue::Object(&uri_jstr), JValue::Object(&mime_type_jstr)]);
167            match data_arr {
168                Ok(data_arr) => {
169                    let data_arr = JByteArray::from(data_arr.l().unwrap());
170                    let data_arr_len = env.get_array_length(&data_arr).unwrap();
171                    let mut data = util::new_buffer_vec(data_arr_len as usize, 0);
172                    env.get_byte_array_region(data_arr, 0, data.as_mut_slice()).unwrap();
173                    let img = LoadedGltfImage::new(
174                        loaded_gltf_wrapper.get(), it.index(), String::from(uri),
175                        data.iter().map(|x| *x as u8).collect());
176                    loaded_gltf.get_images_mut().push(img);
177                }
178                Err(err) => {
179                    util::jni::clear_exception_if_occurred(env);
180                    util::jni::throw_runtime_exception(env, &format!("Failed to load glTF buffer: {}", err)).unwrap();
181                    return;
182                }
183            }
184        }
185    });
186
187    unsafe {
188        env.set_rust_field(this, "rust_loadedGltfObj", loaded_gltf_wrapper).unwrap_or_else(|err| {
189            util::jni::clear_exception_if_occurred(env);
190            util::jni::throw_runtime_exception(env, &format!("Failed to set rust object rust_loadedGltfObj: {}", err)).unwrap()
191        });
192        env.set_rust_field(this, "rust_gltfObj", gltf_obj).unwrap_or_else(|err| {
193            util::jni::clear_exception_if_occurred(env);
194            util::jni::throw_runtime_exception(env, &format!("Failed to set rust object rust_gltfObj: {}", err)).unwrap()
195        });
196    }
197}
198
199pub fn announce_gltf_image_uris<'a>(
200    env: &mut JNIEnv<'a>,
201    this: &JObject<'a>
202) {
203    let gltf_obj: gltf::Gltf;
204    unsafe {
205        gltf_obj = env.take_rust_field(this, "rust_gltfObj").unwrap();
206    }
207
208    gltf_obj.images().for_each(|it| {
209        if let image::Source::Uri { uri, mime_type: _ } = it.source() {
210            let uri_jstr = env.new_string(uri).unwrap();
211            invoke_native_callback(env, this, "receiveImageURI", "(Ljava/lang/String;)V",
212                                   &[JValue::Object(&uri_jstr)]).unwrap();
213        }
214    });
215
216    unsafe {
217        env.set_rust_field(this, "rust_gltfObj", gltf_obj).unwrap_or_else(|err| {
218            util::jni::clear_exception_if_occurred(env);
219            util::jni::throw_runtime_exception(env, &format!("Failed to set rust object rust_gltfObj: {}", err)).unwrap()
220        });
221    }
222}
223
224pub fn handle_native_init<'a>(
225    env: &mut JNIEnv<'a>,
226    this: &JObject<'a>
227) {
228    init_gltf(env, this);
229    load_gltf(env, this);
230    announce_gltf_image_uris(env, this);
231}
232
233pub fn handle_native_destroy<'a>(
234    env: &mut JNIEnv<'a>,
235    this: &JObject<'a>
236) {
237    unsafe {
238        let gltf_obj: gltf::Gltf = env.take_rust_field(&this, "rust_gltfObj").unwrap();
239        drop(gltf_obj);
240    }
241}
242
243pub fn handle_get_image_data_by_uri<'a>(
244    env: &mut JNIEnv<'a>,
245    this: &JObject<'a>,
246    uri_jstr: &JString
247) -> jbyteArray {
248    let loaded_gltf_obj: LoadedGltfWrapper;
249    unsafe {
250        loaded_gltf_obj = env.take_rust_field(this, "rust_loadedGltfObj").unwrap();
251    }
252
253    let uri = String::from(env.get_string(uri_jstr).unwrap());
254    let mut target_img: Option<&LoadedGltfImage> = None;
255    let loaded_gltf = loaded_gltf_obj.get().lock().unwrap();
256    for image in loaded_gltf.get_images() {
257        if *image.get_uri() == uri {
258            target_img = Some(image);
259            break;
260        }
261    }
262
263    let mut result: Vec<u8> = Vec::new();
264    if let Some(image) = target_img {
265        result = Vec::with_capacity(image.get_data().len());
266        image.get_data().iter().for_each(|x| result.push(*x));
267    }
268    
269    drop(loaded_gltf);
270
271    unsafe {
272        env.set_rust_field(this, "rust_loadedGltfObj", loaded_gltf_obj).unwrap_or_else(|err| {
273            util::jni::clear_exception_if_occurred(env);
274            util::jni::throw_runtime_exception(env, &format!("Failed to set rust object rust_loadedGltfObj: {}", err)).unwrap()
275        });
276    }
277
278    let jresult = env.new_byte_array(result.len() as jsize).unwrap();
279    let result_jbyte: Vec<jbyte> = result.iter().map(|x| *x as jbyte).collect();
280    env.set_byte_array_region(&jresult, 0, result_jbyte.as_slice()).unwrap();
281    jresult.as_raw()
282}