Skip to content

Commit

Permalink
custom attachments (#20)
Browse files Browse the repository at this point in the history
* Add mutable string accessor macro.

* Add an attachment loader wrapper.

* temp patch spine_c to refcount

obv temporary, see EsotericSoftware/spine-runtimes#2399

* Convenience fn for creating region attachments.

* return nulerror

* clippy

* fix doc string refs

* move unsafe to interior

As far as I can tell, this is safe to do if the attachment is valid, once esoteric updates spine with the ref count increment in spSlot_setAttachment

* undo

* mark unsafe again

* clippy

* Update src/attachment_loader.rs

* Update slot.rs

---------

Co-authored-by: jabu <[email protected]>
  • Loading branch information
brandon-reinhart and jabuwu authored Nov 12, 2023
1 parent 495a2f0 commit 2199add
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 9 deletions.
129 changes: 129 additions & 0 deletions src/attachment_loader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::{
c::{
spAtlasAttachmentLoader_create, spAttachmentLoader, spAttachmentLoader_createAttachment,
spAttachmentLoader_dispose,
},
c_interface::{NewFromPtr, SyncPtr},
Atlas, Attachment, AttachmentType, RegionProps, Skin,
};

/// Error types related to [`AttachmentLoader`](`crate::AttachmentLoader`).
#[derive(Debug)]
pub enum AttachmentLoaderError {
/// Creating an attachment failed.
/// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information.
CreateAttachmentFailed,
InvalidArgument {
field: &'static str,
},
}

#[derive(Debug)]
pub struct AttachmentLoader {
c_attachment_loader: SyncPtr<spAttachmentLoader>,
}

impl NewFromPtr<spAttachmentLoader> for AttachmentLoader {
unsafe fn new_from_ptr(c_attachment_loader: *mut spAttachmentLoader) -> Self {
Self {
c_attachment_loader: SyncPtr(c_attachment_loader),
}
}
}

impl AttachmentLoader {
/// The spine runtime offers a default [`AttachmentLoader`](`crate::AttachmentLoader`) that
/// loads attachments from an [`Atlas`](`crate::Atlas`).
pub fn new_atlas_loader(atlas: &Atlas) -> Self {
unsafe {
let atlas_attachment_loader = spAtlasAttachmentLoader_create(atlas.c_ptr());
let attachment_loader = &mut (*atlas_attachment_loader).super_0;
Self::new_from_ptr(attachment_loader)
}
}

/// Creates an [`Attachment`](`crate::Attachment`) of a specified type.
///
/// # Errors
///
/// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed.
/// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information.
/// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte.
pub fn create_attachment(
&self,
skin: Option<Skin>,
attachment_type: AttachmentType,
name: &str,
path: &str,
) -> Result<Attachment, AttachmentLoaderError> {
let c_name = std::ffi::CString::new(name)
.map_err(|_| AttachmentLoaderError::InvalidArgument { field: "name" })?;
let c_path = std::ffi::CString::new(path)
.map_err(|_| AttachmentLoaderError::InvalidArgument { field: "path" })?;

unsafe {
let c_name = c_name.as_ptr();
let c_path = c_path.as_ptr();
let c_skin = skin.map_or(std::ptr::null_mut(), |skin| skin.c_ptr());
let c_sequence = std::ptr::null_mut(); // What is this for?

let attachment = spAttachmentLoader_createAttachment(
self.c_ptr(),
c_skin,
attachment_type as u32,
c_name,
c_path,
c_sequence,
);

if attachment.is_null() {
Err(AttachmentLoaderError::CreateAttachmentFailed)
} else {
Ok(Attachment::new_from_ptr(attachment))
}
}
}

/// Convenience function for creating a [`RegionAttachment`](`crate::RegionAttachment`).
///
/// # Errors
///
/// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed.
/// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information.
/// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte.
pub fn create_region_attachment(
&self,
skin: Option<Skin>,
name: &str,
path: &str,
props: &RegionProps,
) -> Result<Attachment, AttachmentLoaderError> {
let attachment = self.create_attachment(skin, AttachmentType::Region, name, path)?;

let Some(mut region) = attachment.as_region() else {
return Err(AttachmentLoaderError::CreateAttachmentFailed);
};

region.update_from_props(props);

Ok(attachment)
}

c_accessor_string!(error1, error1);
c_accessor_string!(error2, error2);
c_ptr!(c_attachment_loader, spAttachmentLoader);
}

impl Clone for AttachmentLoader {
fn clone(&self) -> Self {
unsafe { AttachmentLoader::new_from_ptr(self.c_ptr()) }
}
}

impl Drop for AttachmentLoader {
fn drop(&mut self) {
unsafe {
spAttachmentLoader_dispose(self.c_ptr());
}
}
}
1 change: 1 addition & 0 deletions src/c/spine_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24449,6 +24449,7 @@ pub unsafe extern "C" fn spSlot_setAttachment(
if attachment == (*self_0).attachment {
return;
}

if isVertexAttachment(attachment) == 0
|| isVertexAttachment((*self_0).attachment) == 0
|| (*(attachment as *mut spVertexAttachment)).timelineAttachment
Expand Down
28 changes: 28 additions & 0 deletions src/c_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,34 @@ macro_rules! c_accessor_string {
};
}

macro_rules! c_accessor_string_mut {
($(#[$($attrss:tt)*])* $rust:ident, $rust_set:ident, $c:ident) => {
$(#[$($attrss)*])*
#[must_use]
pub fn $rust(&self) -> &str {
unsafe {
if !self.c_ptr_ref().$c.is_null() {
crate::c_interface::from_c_str(std::ffi::CStr::from_ptr(self.c_ptr_ref().$c))
} else {
""
}
}
}

/// # Errors
///
/// Returns [`std::ffi::NulError`] if an interior nul byte is found.
$(#[$($attrss)*])*
pub fn $rust_set(&mut self, value: String) -> Result<(), std::ffi::NulError> {
let c_str = std::ffi::CString::new(value)?;
unsafe {
self.c_ptr_mut().$c = c_str.into_raw();
}
Ok(())
}
};
}

macro_rules! c_accessor_string_optional {
($(#[$($attrss:tt)*])* $rust:ident, $c:ident) => {
$(#[$($attrss)*])*
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod animation_state_data;
#[path = "atlas.rs"]
mod atlas_mod;
mod attachment;
mod attachment_loader;
mod bone;
mod bounding_box_attachment;
mod clipping_attachment;
Expand All @@ -73,6 +74,7 @@ pub use animation_state::*;
pub use animation_state_data::*;
pub use atlas_mod::{atlas, Atlas};
pub use attachment::*;
pub use attachment_loader::*;
pub use bone::*;
pub use bounding_box_attachment::*;
pub use clipping_attachment::*;
Expand Down
50 changes: 41 additions & 9 deletions src/region_attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@ use crate::{
c_interface::SyncPtr,
slot::Slot,
texture_region::TextureRegion,
Color,
};

#[cfg(feature = "mint")]
use mint::Vector2;

#[derive(Debug)]
pub struct RegionProps {
pub x: f32,
pub y: f32,
pub scale_x: f32,
pub scale_y: f32,
pub rotation: f32,
pub width: f32,
pub height: f32,
pub color: Color,
}

/// An attachment which draws a texture.
///
/// [Spine API Reference](http://esotericsoftware.com/spine-api-reference#RegionAttachment)
Expand Down Expand Up @@ -57,52 +70,71 @@ impl RegionAttachment {
}
}

pub fn update_from_props(&mut self, props: &RegionProps) {
self.set_x(props.x);
self.set_y(props.y);
self.set_scale_x(props.scale_x);
self.set_scale_y(props.scale_y);
self.set_rotation(props.rotation);
self.set_width(props.width);
self.set_height(props.height);
*self.color_mut() = props.color;
self.update_region();
}

c_attachment_accessors!();
c_accessor_string!(path, path);
c_accessor!(
c_accessor_string_mut!(path, set_path, path);
c_accessor_mut!(
/// The local x translation.
x,
set_x,
x,
f32
);
c_accessor!(
c_accessor_mut!(
/// The local y translation.
y,
set_y,
y,
f32
);
c_accessor!(
c_accessor_mut!(
/// The local scaleX.
scale_x,
set_scale_x,
scaleX,
f32
);
c_accessor!(
c_accessor_mut!(
/// The local scaleY.
scale_y,
set_scale_y,
scaleY,
f32
);
// TODO: docs: in degrees? counter-clockwise?
c_accessor!(
c_accessor_mut!(
/// The local rotation.
rotation,
set_rotation,
rotation,
f32
);
c_accessor!(
c_accessor_mut!(
/// The width of the region attachment in Spine.
width,
set_width,
width,
f32
);
c_accessor!(
c_accessor_mut!(
/// The height of the region attachment in Spine.
height,
set_height,
height,
f32
);
c_accessor_color!(color, color);
c_accessor_color_mut!(color, color_mut, color);
c_accessor_passthrough!(uvs, uvs, [c_float; 8]);
c_accessor_passthrough!(offset, offset, [c_float; 8]);
c_accessor_renderer_object!();
Expand Down

0 comments on commit 2199add

Please sign in to comment.