Skip to main content

gltforge_unity/
unity_mesh.rs

1use crate::{unity_gltf::UnityGltf, unity_indices::UnityIndices, unity_submesh::UnitySubMesh};
2
3/// A Unity-shaped glTF mesh: one shared vertex array with N sub-meshes (one per glTF primitive).
4/// Maps directly to a `UnityEngine.Mesh` with `subMeshCount` sub-meshes.
5pub struct UnityMesh {
6    /// Mesh name. Falls back to the glTF mesh index if the source mesh is unnamed.
7    pub name: String,
8
9    /// All vertex positions across all primitives, concatenated.
10    /// Left-handed coordinate system (X negated relative to glTF).
11    /// Tightly packed `[x, y, z]` floats — maps to `mesh.vertices`.
12    pub vertices: Vec<[f32; 3]>,
13
14    /// Vertex normals, same length as `vertices`. Empty if the source mesh has no normals.
15    /// Left-handed coordinate system (X negated relative to glTF).
16    /// Tightly packed `[x, y, z]` floats — maps to `mesh.normals`.
17    pub normals: Vec<[f32; 3]>,
18
19    /// Vertex tangents, same length as `vertices`. Empty if the source mesh has no tangents.
20    /// Left-handed coordinate system (X and W negated relative to glTF).
21    /// Tightly packed `[x, y, z, w]` floats — maps to `mesh.tangents`.
22    pub tangents: Vec<[f32; 4]>,
23
24    /// UV channels, densely packed from channel 0.
25    /// `uvs[k]` holds all vertices for `TEXCOORD_k` (V-flipped for Unity's bottom-left origin).
26    /// Only channels present on every primitive are included; the vec stops at the first absent channel.
27    /// Maps to `mesh.SetUVs(k, uvs[k])`.
28    pub uvs: Vec<Vec<[f32; 2]>>,
29
30    /// One sub-mesh per glTF primitive — maps to `mesh.SetTriangles(tris, submeshIndex)`.
31    pub sub_meshes: Vec<UnitySubMesh>,
32}
33
34/// Return the number of meshes in the document.
35///
36/// # Safety
37/// `ptr` must be a valid, non-null handle.
38#[unsafe(no_mangle)]
39pub unsafe extern "C" fn gltforge_mesh_count(ptr: *const UnityGltf) -> u32 {
40    unsafe { &*ptr }.meshes.len() as u32
41}
42
43/// Return the name of mesh `mesh_idx` as UTF-8 bytes (not null-terminated).
44/// Always non-null — unnamed meshes use their index as the name.
45/// `out_len` receives the byte length.
46///
47/// # Safety
48/// `ptr` must be a valid, non-null handle. `out_len` may be null.
49#[unsafe(no_mangle)]
50pub unsafe extern "C" fn gltforge_mesh_name(
51    ptr: *const UnityGltf,
52    mesh_idx: u32,
53    out_len: *mut u32,
54) -> *const u8 {
55    let gltf = unsafe { &*ptr };
56    match gltf.meshes.get(&mesh_idx) {
57        Some(m) => {
58            if !out_len.is_null() {
59                unsafe { *out_len = m.name.len() as u32 };
60            }
61            m.name.as_ptr()
62        }
63        None => {
64            if !out_len.is_null() {
65                unsafe { *out_len = 0 };
66            }
67            std::ptr::null()
68        }
69    }
70}
71
72/// Return the total vertex count for mesh `mesh_idx` (across all sub-meshes).
73///
74/// # Safety
75/// `ptr` must be a valid, non-null handle.
76#[unsafe(no_mangle)]
77pub unsafe extern "C" fn gltforge_mesh_vertex_count(ptr: *const UnityGltf, mesh_idx: u32) -> u32 {
78    unsafe { &*ptr }
79        .meshes
80        .get(&mesh_idx)
81        .map(|m| m.vertices.len() as u32)
82        .unwrap_or(0)
83}
84
85/// Return `16` or `32` indicating the index format for mesh `mesh_idx`.
86/// The format is uniform across all sub-meshes and is determined by total vertex count.
87///
88/// # Safety
89/// `ptr` must be a valid, non-null handle.
90#[unsafe(no_mangle)]
91pub unsafe extern "C" fn gltforge_mesh_index_format(ptr: *const UnityGltf, mesh_idx: u32) -> u32 {
92    let gltf = unsafe { &*ptr };
93    match gltf
94        .meshes
95        .get(&mesh_idx)
96        .and_then(|m| m.sub_meshes.first())
97        .map(|s| &s.indices)
98    {
99        Some(UnityIndices::U32(_)) => 32,
100        _ => 16,
101    }
102}
103
104/// Return a pointer to the normal data for mesh `mesh_idx`.
105/// `out_len` receives the total number of `f32` values (`vertex_count × 3`).
106/// Returns null with `out_len = 0` if the mesh has no normals.
107///
108/// # Safety
109/// `ptr` must be a valid, non-null handle. `out_len` may be null.
110#[unsafe(no_mangle)]
111pub unsafe extern "C" fn gltforge_mesh_normals(
112    ptr: *const UnityGltf,
113    mesh_idx: u32,
114    out_len: *mut u32,
115) -> *const f32 {
116    let gltf = unsafe { &*ptr };
117    match gltf.meshes.get(&mesh_idx) {
118        Some(m) if !m.normals.is_empty() => {
119            let flat = m.normals.as_flattened();
120            if !out_len.is_null() {
121                unsafe { *out_len = flat.len() as u32 };
122            }
123            flat.as_ptr()
124        }
125        _ => {
126            if !out_len.is_null() {
127                unsafe { *out_len = 0 };
128            }
129            std::ptr::null()
130        }
131    }
132}
133
134/// Return a pointer to the tangent data for mesh `mesh_idx`.
135/// `out_len` receives the total number of `f32` values (`vertex_count × 4`).
136/// Returns null with `out_len = 0` if the mesh has no tangents.
137///
138/// # Safety
139/// `ptr` must be a valid, non-null handle. `out_len` may be null.
140#[unsafe(no_mangle)]
141pub unsafe extern "C" fn gltforge_mesh_tangents(
142    ptr: *const UnityGltf,
143    mesh_idx: u32,
144    out_len: *mut u32,
145) -> *const f32 {
146    let gltf = unsafe { &*ptr };
147    match gltf.meshes.get(&mesh_idx) {
148        Some(m) if !m.tangents.is_empty() => {
149            let flat = m.tangents.as_flattened();
150            if !out_len.is_null() {
151                unsafe { *out_len = flat.len() as u32 };
152            }
153            flat.as_ptr()
154        }
155        _ => {
156            if !out_len.is_null() {
157                unsafe { *out_len = 0 };
158            }
159            std::ptr::null()
160        }
161    }
162}
163
164/// Return the number of UV channels for mesh `mesh_idx`.
165///
166/// # Safety
167/// `ptr` must be a valid, non-null handle.
168#[unsafe(no_mangle)]
169pub unsafe extern "C" fn gltforge_mesh_uv_channel_count(
170    ptr: *const UnityGltf,
171    mesh_idx: u32,
172) -> u32 {
173    unsafe { &*ptr }
174        .meshes
175        .get(&mesh_idx)
176        .map(|m| m.uvs.len() as u32)
177        .unwrap_or(0)
178}
179
180/// Return a pointer to the UV data for mesh `mesh_idx`, channel `channel`.
181/// `out_len` receives the total number of `f32` values (`vertex_count × 2`).
182/// Returns null with `out_len = 0` if the channel is absent.
183///
184/// # Safety
185/// `ptr` must be a valid, non-null handle. `out_len` may be null.
186#[unsafe(no_mangle)]
187pub unsafe extern "C" fn gltforge_mesh_uvs(
188    ptr: *const UnityGltf,
189    mesh_idx: u32,
190    channel: u32,
191    out_len: *mut u32,
192) -> *const f32 {
193    let gltf = unsafe { &*ptr };
194    match gltf
195        .meshes
196        .get(&mesh_idx)
197        .and_then(|m| m.uvs.get(channel as usize))
198    {
199        Some(ch) if !ch.is_empty() => {
200            let flat = ch.as_flattened();
201            if !out_len.is_null() {
202                unsafe { *out_len = flat.len() as u32 };
203            }
204            flat.as_ptr()
205        }
206        _ => {
207            if !out_len.is_null() {
208                unsafe { *out_len = 0 };
209            }
210            std::ptr::null()
211        }
212    }
213}
214
215/// Return a pointer to the position data for mesh `mesh_idx`.
216/// `out_len` receives the total number of `f32` values (`vertex_count × 3`).
217///
218/// # Safety
219/// `ptr` must be a valid, non-null handle. `out_len` may be null.
220#[unsafe(no_mangle)]
221pub unsafe extern "C" fn gltforge_mesh_positions(
222    ptr: *const UnityGltf,
223    mesh_idx: u32,
224    out_len: *mut u32,
225) -> *const f32 {
226    let gltf = unsafe { &*ptr };
227    match gltf.meshes.get(&mesh_idx) {
228        Some(m) => {
229            let flat = m.vertices.as_flattened();
230            if !out_len.is_null() {
231                unsafe { *out_len = flat.len() as u32 };
232            }
233            flat.as_ptr()
234        }
235        None => {
236            if !out_len.is_null() {
237                unsafe { *out_len = 0 };
238            }
239            std::ptr::null()
240        }
241    }
242}