modelassetlib_native/jniimpl/
gltf.rs1extern 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 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 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 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 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}