Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a capability for creating attachments. #19

Open
brandon-reinhart opened this issue Oct 22, 2023 · 4 comments
Open

Add a capability for creating attachments. #19

brandon-reinhart opened this issue Oct 22, 2023 · 4 comments

Comments

@brandon-reinhart
Copy link
Collaborator

Sometimes it is useful to create attachments programmatically. If a spine has many possible attachments, or if those attachments have unusual properties. A concrete example would be wanting to add an attachment to a slot that uses a different texture atlas than the one the skeleton is exported with.

There are two levels of this work:

  1. Creating and adding attachments that do not originate in the skeleton file.
  2. Providing some API for creating custom attachment loaders (for example, the user could provide a custom region attachment loader that loads from a single image file).
@brandon-reinhart
Copy link
Collaborator Author

brandon-reinhart commented Oct 22, 2023

I looked at the code, but at the moment this work is probably a bit beyond me. I don't have experience with writing unsafe rust that interfaces with a C library.

There are also some spine-c concepts I don't yet understand, like Sequence. Still reading the code..

My concrete goal is to allow for weapon and gear attachments that aren't specified by the spine project, so that a mod author could create a new weapon. These attachments would load from an atlas other than the one used by the skel file, but would attach to a slot specified in the spine project.

@brandon-reinhart
Copy link
Collaborator Author

This hacky test shows progress:

    let attachment = unsafe {
        let atlas_attachment_loader = c::spAtlasAttachmentLoader_create(atlas.c_ptr());
        if atlas_attachment_loader.is_null() {
            println!("failed to create attachment loader");
            return;
        } else {
            println!("Attachment loader is valid.");
        }

        let attachment_loader = &mut (*atlas_attachment_loader).super_0;

        let c_name = std::ffi::CString::new(name).unwrap();
        let c_path = std::ffi::CString::new(path).unwrap();
        let attachment = c::spAttachmentLoader_createAttachment(
            attachment_loader,
            std::ptr::null_mut(),
            c::SP_ATTACHMENT_REGION,
            c_name.as_ptr(),
            c_path.as_ptr(),
            std::ptr::null_mut(),
        );

        if attachment.is_null() {
            let error1: &std::ffi::CStr =
                std::ffi::CStr::from_ptr(attachment_loader.error1 as *const i8);
            let error2: &std::ffi::CStr =
                std::ffi::CStr::from_ptr(attachment_loader.error2 as *const i8);

            println!("Failed to create attachment. {:?} {:?}", error1, error2);
            return;
        } else {
            println!("Attachment is valid.");
        }

        let c_region = attachment as *mut c::spRegionAttachment;
        let region = &mut (*c_region);

        region.path = c_path.as_ptr();
        region.x = 0.0;
        region.y = 0.0;
        region.scaleX = 1.0;
        region.scaleY = 1.0;
        region.rotation = 0.0;
        region.width = 8.0;
        region.height = 8.0;
        region.color.r = 0.5;
        region.color.g = 0.5;
        region.color.b = 0.5;
        region.color.a = 0.5;
        region.sequence = std::ptr::null_mut();

        c::spRegionAttachment_updateRegion(region);
        c::spAttachmentLoader_configureAttachment(attachment_loader, attachment);

        Attachment::new_from_ptr(attachment)
    };
    unsafe {
        let Some(front_thigh_slot) = skeleton_controller.skeleton.find_slot_mut("front-thigh")
        else {
            println!("Failed to find slot.");
            return;
        };

        println!("TEST: {:?}", attachment.as_region());
        //front_thigh_slot.as_mut().set_attachment(Some(attachment));
        c::spSlot_setAttachment(front_thigh_slot.c_ptr(), attachment.c_ptr());
        println!("Custom attachment set!");
    }
  front-thigh
    4 Vertices / UVs
    6 Indices
    Normal Blend Mode
    Color { r: 0.5, g: 0.5, b: 0.5, a: 0.5 }
    No Premultiplied Alpha

Here front-thigh was rendered with our custom color that came from the injected attachment.

@brandon-reinhart
Copy link
Collaborator Author

head_on_head

Lol, got it working. Here is spineboy with a dynamic attachment of a small head occupying his front-shin slot.

I will need help with the pointer management -- this crashes when things are freed.

Not sure what you'll want the API to look like. My brief experiment so far is:

        let attachment_loader = AttachmentLoader::new_atlas_loader(&atlas);
        let attachment = attachment_loader
            .create_attachment(None, AttachmentType::Region, "test", "head")
            .unwrap();

Things get real sad if you drop attachment after setting it on a slot.

@jabuwu
Copy link
Owner

jabuwu commented Nov 15, 2023

We merged #20 which adds support for region attachments, but I'm going to leave this PR open, since I'd like to look into creating the other attachments types as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants