Skip to main content

gltforge_unity/
unity_gltf.rs

1use crate::convert;
2use gltforge_unity_core::{UnityGameObject, UnityImage, UnityMesh, UnityPbrMetallicRoughness};
3
4use std::{collections::HashMap, ffi::CStr, os::raw::c_char, path::Path, sync::Arc};
5
6/// A Unity-shaped glTF — same structural relationships as glTF but with all
7/// data pre-converted to Unity's left-handed coordinate system.
8pub struct UnityGltf {
9    /// The name of the default scene. Falls back to the source filename (without extension)
10    /// if the glTF scene has no name.
11    pub scene_name: String,
12
13    /// Indices of the root GameObjects in the default scene.
14    pub root_game_objects: Vec<u32>,
15
16    /// All GameObjects, keyed by their glTF node index.
17    pub game_objects: HashMap<u32, UnityGameObject>,
18
19    /// All meshes, keyed by their glTF mesh index.
20    /// Each [`UnityMesh`] contains a shared vertex array and one sub-mesh per glTF primitive.
21    pub meshes: HashMap<u32, UnityMesh>,
22
23    /// All images, keyed by their glTF image index.
24    pub images: HashMap<u32, UnityImage>,
25
26    /// All `GLTF/PbrMetallicRoughness` materials, keyed by their glTF material index.
27    pub pbr_metallic_roughness: HashMap<u32, UnityPbrMetallicRoughness>,
28}
29
30/// Increment the reference count of a [`UnityGltf`] handle.
31///
32/// # Safety
33/// `ptr` must have been returned by [`gltforge_load`] and not yet fully released.
34#[unsafe(no_mangle)]
35pub unsafe extern "C" fn gltforge_retain(ptr: *const UnityGltf) {
36    if !ptr.is_null() {
37        unsafe { Arc::increment_strong_count(ptr) };
38    }
39}
40
41/// Decrement the reference count of a [`UnityGltf`] handle.
42/// Frees all data when the count reaches zero.
43///
44/// # Safety
45/// `ptr` must have been returned by [`gltforge_load`] or [`gltforge_retain`].
46#[unsafe(no_mangle)]
47pub unsafe extern "C" fn gltforge_release(ptr: *const UnityGltf) {
48    if !ptr.is_null() {
49        unsafe { drop(Arc::from_raw(ptr)) };
50    }
51}
52
53/// Parse a glTF file and build a [`UnityGltf`].
54///
55/// Returns an `Arc<UnityGltf>` as an opaque pointer, or null on any error.
56/// The caller must eventually pass the pointer to [`gltforge_release`].
57///
58/// # Safety
59/// `path` must be a valid, null-terminated UTF-8 string.
60#[unsafe(no_mangle)]
61pub unsafe extern "C" fn gltforge_load(path: *const c_char) -> *const UnityGltf {
62    let result = std::panic::catch_unwind(|| {
63        let path_str = unsafe { CStr::from_ptr(path) }.to_str().ok()?;
64        let path = Path::new(path_str);
65        let base_dir = path.parent()?;
66
67        let file_stem = path
68            .file_stem()
69            .and_then(|s| s.to_str())
70            .unwrap_or("unknown");
71
72        let data = std::fs::read(path).ok()?;
73        let (gltf, buffers) = if data.starts_with(b"glTF") {
74            gltforge::parser::parse_glb(&data).ok()?
75        } else {
76            let json = std::str::from_utf8(&data).ok()?;
77            let gltf = gltforge::parser::parse(json).ok()?;
78            let buffers = gltforge::parser::load_buffers(&gltf, base_dir).ok()?;
79            (gltf, buffers)
80        };
81        let unity_gltf = convert::build_unity_gltf(&gltf, &buffers, file_stem).ok()?;
82
83        Some(Arc::into_raw(Arc::new(unity_gltf)))
84    });
85
86    result.ok().flatten().unwrap_or(std::ptr::null())
87}