jabuwu / rusty_spine

Spine runtime for Rust (and wasm!) transpiled from the official C Runtime.
https://docs.rs/rusty_spine
Other
46 stars 4 forks source link

Probably missing something regarding atlasses #18

Open lenscas opened 1 year ago

lenscas commented 1 year ago

So, to me it seems that when loading a skeleton, be that from a file or from an array of bytes I always need to pass an atlas. At least, sooner or later.

The callback set_read_file_cb mentions how this callback is not nesesairly as it is possible to avoid getting it called if loading from disk is not desirable.

Here is the problem I am running into though: I... don't see a way to actually avoid it?

When constructing an atlas I don't see a way to tell it "Here are the bytes of the image(s) you need". I appear to only ever be able to load the atlas file from disk or from bytes but then I still need to pass in a directory path.

So, how can I avoid having spine load the images for me and instead have me do it when I want it to? I know of set_create_texture_cb that I guess can be used to avoid it, but that requires global state at best.

Also, in my specific case the problem is that I want to keep everything async and I don't see a good way to check if an atlas has been fully loaded, so I would prefer a way to load the images myself + the atlas + the spine file first and then merge everything together instead of only loading an atlas + the spine file and have the spine runtime try to figure everything out from there.

jabuwu commented 1 year ago

I'm not sure I understand what you're trying to accomplish, but I'll try to give some insights. In order to create a SkeletonData using spine-c, you must provide either an atlas or a custom attachment loader. The Rust API I wrote does not support custom attachment loaders, and I haven't looked into what that would take.

The reason an atlas is required is because the default attachment loader requires an atlas. I'm not sure why you wouldn't be able to provide an atlas, unless you're rendering nothing at all, in which case passing in a blank atlas is probably sufficient (calling Atlas::new with no data). Maybe the reason is that you want to generate the atlas at runtime, in which case you could generate an atlas file in memory and pass it in. The format is pretty simple.

The directory you give Atlas::new doesn't matter. It could be blank. It gets prefixed to any paths passed to the set_create_texture_cb callback.

Also, in my specific case the problem is that I want to keep everything async and I don't see a good way to check if an atlas has been fully loaded

Then you must hold off creating SkeletonData until the skeleton and atlas are loaded. This is exactly how my bevy_spine crate works. All assets in Bevy are loaded asynchronously: the skeleton, atlas, and all the images. Bevy handles the asynchronous rendering of images automatically.

So, how can I avoid having spine load the images for me and instead have me do it when I want it to?

Spine doesn't load images. It tells you that it wants to load images via the set_create_texture_cb callback. The miniquad example doesn't load textures in this callback, it sets up some state to do it later. Unfortunately, you can't avoid the global callback here because it's how spine-c works.

lenscas commented 1 year ago

Then you must hold off creating SkeletonData until the skeleton and atlas are loaded.

Sorry, with fully loaded I meant that it also loaded the textures. Right now, I need to do that loop in the miniquad example over the combined renderables just to know if everything that needs to be loaded got actually loaded. What I would've liked is a more direct way to check but I guess that is just not possible with how the C part works.

Anyway, it sounds like I need some global mapping between paths and preloaded images and have set_create_texture_cb look into this global mapping to decide what to do. On the bright side, this also means that textures can be cached this way.