1#![allow(clippy::needless_pass_by_value)]
2
3use crate::{
4 animation::Animation, atlas::Atlas, enums::AttachmentType, enums::BlendMode, ffi, SpineError,
5 SpineMutPtr,
6};
7use std::{convert::TryInto, ffi::*, marker::PhantomData, path::Path};
8
9pub struct BoneIndex(i32);
10pub struct SlotIndex(i32);
11
12pub struct RegionAttachment<'a> {
13 pub(crate) inner: *mut ffi::spRegionAttachment,
14 _lifetime: PhantomData<&'a ()>,
15}
16impl<'a> RegionAttachment<'a> {
17 pub fn load_vertices(&self, bone: &Bone<'_>, vertices: &mut [f32; 8]) {
22 unsafe {
23 ffi::spRegionAttachment_computeWorldVertices(
24 self.inner,
25 bone.inner,
26 vertices.as_mut_ptr(),
27 0,
28 2,
29 );
30 }
31 }
32
33 pub fn get_vertices(&self, bone: &Bone<'_>) -> [f32; 8] {
38 let mut vertices = [0.0; 8];
39
40 self.load_vertices(bone, &mut vertices);
41
42 vertices
43 }
44
45 pub fn texture_id(&self) -> u32 {
50 unsafe {
52 let atlas_region = self.as_ref().rendererObject as *mut ffi::spAtlasRegion;
53 let page = (*atlas_region).page;
54 (*page).rendererObject as u32
55 }
56 }
57
58 pub fn rotation(&self) -> f32 {
59 self.as_ref().rotation
60 }
61
62 pub fn color(&self) -> [f32; 4] {
63 let color = &self.as_ref().color;
64 [color.r, color.b, color.g, color.a]
65 }
66
67 pub fn position(&self) -> [f32; 2] {
68 let r = self.as_ref();
69 [r.x, r.y]
70 }
71
72 pub fn scale(&self) -> [f32; 2] {
73 let r = self.as_ref();
74 [r.scaleX, r.scaleY]
75 }
76
77 pub fn dimensions(&self) -> [f32; 2] {
78 let r = self.as_ref();
79 [r.width, r.height]
80 }
81
82 pub fn uv(&self) -> [f32; 8] {
83 let r = self.as_ref();
84 r.uvs
85 }
86
87 pub fn offset(&self) -> [f32; 8] {
88 let r = self.as_ref();
89 r.offset
90 }
91
92 pub(crate) fn as_ref(&self) -> &ffi::spRegionAttachment {
93 unsafe { self.inner.as_ref().unwrap() }
94 }
95 pub(crate) fn as_mut(&mut self) -> &mut ffi::spRegionAttachment {
96 unsafe { self.inner.as_mut().unwrap() }
97 }
98}
99
100pub struct Attachment<'a> {
101 pub(crate) inner: *mut ffi::spAttachment,
102 _lifetime: PhantomData<&'a ()>,
103}
104impl<'a> Attachment<'a> {
105 pub fn as_region_attachment(&mut self) -> RegionAttachment<'a> {
106 RegionAttachment {
107 inner: self.inner as *mut ffi::spRegionAttachment,
108 _lifetime: PhantomData::<&'a ()>::default(),
109 }
110 }
111
112 pub fn name(&self) -> &str {
113 unsafe { CStr::from_ptr(self.as_ref().name) }
114 .to_str()
115 .unwrap()
116 }
117
118 pub fn kind(&self) -> AttachmentType {
119 self.as_ref().type_.into()
120 }
121
122 pub(crate) fn as_ref(&self) -> &ffi::spAttachment {
123 unsafe { self.inner.as_ref().unwrap() }
124 }
125 pub(crate) fn as_mut(&mut self) -> &mut ffi::spAttachment {
126 unsafe { self.inner.as_mut().unwrap() }
127 }
128}
129
130pub struct Slot<'a> {
131 pub(crate) inner: *mut ffi::spSlot,
132 _lifetime: PhantomData<&'a ()>,
133}
134impl<'a> Slot<'a> {
135 pub fn bone(&self) -> Option<Bone<'_>> {
136 let r = self.as_ref();
137
138 if r.bone.is_null() {
139 None
140 } else {
141 Some(Bone {
142 inner: r.bone,
143 _lifetime: PhantomData::<&'a ()>::default(),
144 })
145 }
146 }
147
148 pub fn color(&self) -> [f32; 4] {
149 let color = &self.as_ref().color;
150 [color.r, color.b, color.g, color.a]
151 }
152
153 pub fn blend_mode(&self) -> BlendMode {
154 unsafe { self.as_ref().data.as_ref() }
155 .unwrap()
156 .blendMode
157 .into()
158 }
159
160 pub fn active_attachment(&self) -> Option<Attachment<'_>> {
161 let r = self.as_ref();
162
163 if r.attachment.is_null() {
164 None
165 } else {
166 Some(Attachment {
167 inner: r.attachment,
168 _lifetime: PhantomData::<&'a ()>::default(),
169 })
170 }
171 }
172
173 pub fn index(&self) -> SlotIndex {
174 unsafe { SlotIndex((*self.as_ref().data).index) }
175 }
176
177 pub(crate) fn as_ref(&self) -> &ffi::spSlot {
178 unsafe { self.inner.as_ref().unwrap() }
179 }
180 pub(crate) fn as_mut(&mut self) -> &mut ffi::spSlot {
181 unsafe { self.inner.as_mut().unwrap() }
182 }
183}
184
185pub struct Bone<'a> {
186 pub(crate) inner: *mut ffi::spBone,
187 _lifetime: PhantomData<&'a ()>,
188}
189impl<'a> Bone<'a> {
190 pub fn index(&self) -> BoneIndex {
191 unsafe { BoneIndex((*self.as_ref().data).index) }
192 }
193
194 pub(crate) fn as_ref(&self) -> &ffi::spBone {
195 unsafe { self.inner.as_ref().unwrap() }
196 }
197 pub(crate) fn as_mut(&mut self) -> &mut ffi::spBone {
198 unsafe { self.inner.as_mut().unwrap() }
199 }
200}
201
202pub struct BoneData<'a> {
203 pub(crate) inner: &'a ffi::spBoneData,
204}
205impl<'a> BoneData<'a> {
206 pub fn name(&self) -> &str {
207 unsafe { CStr::from_ptr(self.inner.name) }.to_str().unwrap()
208 }
209}
210impl<'a> std::fmt::Debug for BoneData<'a> {
211 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
212 write!(f, "Bone {{ name: {} }}", self.name())
213 }
214}
215
216pub struct Skeleton {
217 pub(crate) inner: SpineMutPtr<ffi::spSkeleton>,
218}
219
220impl Skeleton {
221 pub fn new(data: &SkeletonData) -> Self {
222 Self {
223 inner: SpineMutPtr::new(
224 unsafe { ffi::spSkeleton_create(data.inner.as_mut_ptr()) },
225 Some(ffi::spSkeleton_dispose),
226 ),
227 }
228 }
229
230 pub fn slots(&self) -> Vec<Slot<'_>> {
231 let inner_ref = self.inner.as_ref();
232 let mut converted = Vec::with_capacity(inner_ref.slotsCount.try_into().unwrap());
233
234 for n in 0..inner_ref.slotsCount.try_into().unwrap() {
235 unsafe {
236 let inner = *(inner_ref.slots.add(n));
237 converted.push(Slot {
238 inner,
239 _lifetime: PhantomData::<&'_ ()>::default(),
240 });
241 }
242 }
243
244 converted
245 }
246
247 pub fn draw_slots(&self) -> Vec<Slot<'_>> {
248 let inner_ref = self.inner.as_ref();
249 let mut converted = Vec::with_capacity(inner_ref.slotsCount.try_into().unwrap());
250
251 for n in 0..inner_ref.slotsCount.try_into().unwrap() {
252 unsafe {
253 let inner = *(inner_ref.drawOrder.add(n));
254 converted.push(Slot {
255 inner,
256 _lifetime: PhantomData::<&'_ ()>::default(),
257 });
258 }
259 }
260
261 converted
262 }
263
264 pub fn bone(&self) -> Vec<Bone<'_>> {
265 let inner_ref = self.inner.as_ref();
266 let mut converted = Vec::with_capacity(inner_ref.bonesCount.try_into().unwrap());
267
268 for n in 0..inner_ref.bonesCount.try_into().unwrap() {
269 unsafe {
270 let inner = *(inner_ref.bones.add(n));
271 converted.push(Bone {
272 inner,
273 _lifetime: PhantomData::<&'_ ()>::default(),
274 });
275 }
276 }
277
278 converted
279 }
280
281 pub fn bone_index(&self, name: &str) -> BoneIndex {
282 let name = CString::new(name).unwrap();
283 BoneIndex(unsafe { ffi::spSkeleton_findBoneIndex(self.inner.as_mut_ptr(), name.as_ptr()) })
284 }
285
286 pub fn slot(&self, name: &str) -> Option<Slot<'_>> {
287 let name = CString::new(name).unwrap();
288 let inner = unsafe { ffi::spSkeleton_findSlot(self.inner.as_mut_ptr(), name.as_ptr()) };
289
290 if inner.is_null() {
291 None
292 } else {
293 Some(Slot {
294 inner,
295 _lifetime: PhantomData::<&'_ ()>::default(),
296 })
297 }
298 }
299
300 pub fn color(&self) -> [f32; 4] {
301 let color = &self.inner.as_ref().color;
302 [color.r, color.b, color.g, color.a]
303 }
304
305 pub fn position(&self) -> [f32; 2] {
306 let r = self.inner.as_ref();
307 [r.x, r.y]
308 }
309
310 pub fn scale(&self) -> [f32; 2] {
311 let r = self.inner.as_ref();
312 [r.scaleX, r.scaleY]
313 }
314
315 fn time(&self) -> f32 {
316 self.inner.as_ref().time
317 }
318
319 pub fn slot_index(&self, name: &str) -> SlotIndex {
320 let name = CString::new(name).unwrap();
321 SlotIndex(unsafe { ffi::spSkeleton_findSlotIndex(self.inner.as_mut_ptr(), name.as_ptr()) })
322 }
323
324 pub fn reset(&mut self) {
325 unsafe { ffi::spSkeleton_setToSetupPose(self.inner.as_mut_ptr()) }
326 }
327
328 pub fn reset_bones(&mut self) {
329 unsafe { ffi::spSkeleton_setBonesToSetupPose(self.inner.as_mut_ptr()) }
330 }
331
332 pub fn reset_slots(&mut self) {
333 unsafe { ffi::spSkeleton_setSlotsToSetupPose(self.inner.as_mut_ptr()) }
334 }
335
336 pub fn update_cache(&mut self) {
337 unsafe { ffi::spSkeleton_updateCache(self.inner.as_mut_ptr()) }
338 }
339
340 pub fn update(&mut self, delta: f32) {
341 unsafe { ffi::spSkeleton_update(self.inner.as_mut_ptr(), delta) }
342 }
343
344 pub fn update_world_transforms(&mut self) {
345 unsafe { ffi::spSkeleton_updateWorldTransform(self.inner.as_mut_ptr()) }
346 }
347}
348
349pub struct SkeletonData {
350 pub(crate) inner: SpineMutPtr<ffi::spSkeletonData>,
351 pub(crate) atlas: SpineMutPtr<ffi::spAtlas>,
352}
353
354impl SkeletonData {
355 pub(crate) fn as_mut(&mut self) -> &mut ffi::spSkeletonData {
356 self.inner.as_mut()
357 }
358 pub(crate) fn as_ref(&self) -> &ffi::spSkeletonData {
359 self.inner.as_ref()
360 }
361
362 pub fn animations<'a>(&'a self) -> Vec<Animation<'a>> {
363 let mut converted = Vec::with_capacity(self.as_ref().animationsCount.try_into().unwrap());
364
365 for n in 0..self.as_ref().animationsCount.try_into().unwrap() {
366 unsafe {
367 let inner = *(self.as_ref().animations.add(n));
368 converted.push(Animation {
369 inner,
370 _lifetime: PhantomData::<&'a ()>::default(),
371 });
372 }
373 }
374
375 converted
376 }
377
378 pub fn bones<'a>(&'a self) -> Vec<BoneData<'a>> {
379 let mut converted = Vec::with_capacity(self.as_ref().bonesCount.try_into().unwrap());
380
381 for n in 0..self.as_ref().bonesCount.try_into().unwrap() {
382 unsafe {
383 let inner = *(self.as_ref().bones.add(n));
384 converted.push(BoneData {
385 inner: inner.as_mut().unwrap(),
386 });
387 }
388 }
389
390 converted
391 }
392
393 pub fn strings(&self) -> Vec<&str> {
394 let mut converted = Vec::new();
395
396 for n in 0..self.as_ref().stringsCount.try_into().unwrap() {
397 unsafe {
398 converted.push(
399 CStr::from_ptr(*self.as_ref().strings.add(n))
400 .to_str()
401 .unwrap(),
402 );
403 }
404 }
405
406 converted
407 }
408
409 pub fn position(&self) -> (f32, f32) {
410 let r = self.inner.as_ref();
411 (r.x, r.y)
412 }
413
414 pub fn dimensions(&self) -> (f32, f32) {
415 let r = self.inner.as_ref();
416 (r.width, r.height)
417 }
418
419 pub fn set_position(&mut self, position: (f32, f32)) {
420 let r = self.inner.as_mut();
421 r.x = position.0;
422 r.y = position.1;
423 }
424
425 pub fn set_dimensions(&mut self, dimensions: (f32, f32)) {
426 let r = self.inner.as_mut();
427 r.width = dimensions.0;
428 r.height = dimensions.1;
429 }
430
431 pub fn from_binary_file<P>(path: P, atlas: Atlas) -> Result<Self, SpineError>
436 where
437 P: AsRef<Path>,
438 {
439 let path_str_c = CString::new(path.as_ref().to_str().ok_or_else(|| {
440 SpineError::FailLoadSkeleton("Failed to convert path to string".to_owned())
441 })?)
442 .map_err(|e| {
443 SpineError::FailLoadSkeleton(format!("Failed to convert path to string: {:?}", e))
444 })?;
445
446 unsafe {
447 let binary_data = ffi::spSkeletonBinary_create(atlas.inner.as_mut_ptr());
448 if binary_data.is_null() {
449 return Err(SpineError::FailLoadSkeleton(
450 "failed to begin binary data load".to_owned(),
451 ));
452 }
453 let data = ffi::spSkeletonBinary_readSkeletonDataFile(binary_data, path_str_c.as_ptr());
454 if data.is_null() {
455 ffi::spSkeletonBinary_dispose(binary_data);
456 return Err(SpineError::FailLoadSkeleton(
457 CStr::from_ptr((*binary_data).error)
458 .to_str()
459 .map_err(|e| {
460 SpineError::FailLoadSkeleton(format!(
461 "Failed to retrieve skeleton loading error: {:?}",
462 e
463 ))
464 })?
465 .to_owned(),
466 ));
467 }
468
469 ffi::spSkeletonBinary_dispose(binary_data);
470
471 Ok(Self {
472 inner: SpineMutPtr::new(data, Some(ffi::spSkeletonData_dispose)),
473 atlas: atlas.inner,
474 })
475 }
476 }
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::tests::TEST_CASES;
483
484 #[test]
485 fn load_skeleton() {
486 let test_case = &TEST_CASES[0];
487
488 let mut load = 0;
489
490 let atlas = Atlas::from_file(test_case.atlas(), |_, _| {
491 load += 1;
492 0
493 })
494 .unwrap();
495
496 assert_eq!(2, load);
497
498 let skeleton_data = SkeletonData::from_binary_file(test_case.binary(), atlas).unwrap();
499 let _skeleton = Skeleton::new(&skeleton_data);
500 println!("bones = {:?}", skeleton_data.bones());
501 }
502
503 #[test]
504 fn load_skeleton_drop_order() {
505 let test_case = &TEST_CASES[0];
506
507 let mut load = 0;
508
509 let skeleton_data = {
510 let atlas = Atlas::from_file(test_case.atlas(), |_, _| {
511 load += 1;
512 0
513 })
514 .unwrap();
515
516 assert_eq!(2, load);
517
518 SkeletonData::from_binary_file(test_case.binary(), atlas).unwrap()
519 };
520 let _skeleton = Skeleton::new(&skeleton_data);
521 println!("bones = {:?}", skeleton_data.bones());
522 }
523}