mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-06 18:56:54 +08:00
[flutte] Add controller, wrap AnimationState.
This commit is contained in:
parent
2e78d64a23
commit
1dd2c99c70
@ -35,9 +35,13 @@ class Spineboy extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final controller = SpineWidgetController((controller) {
|
||||||
|
controller.animationState?.setAnimation(0, "walk", true);
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Spineboy')),
|
appBar: AppBar(title: const Text('Spineboy')),
|
||||||
body: const SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas"),
|
body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
|
||||||
// body: const SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas"),
|
// body: const SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas"),
|
||||||
// body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
|
// body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,15 +16,15 @@ int majorVersion() => _bindings.spine_major_version();
|
|||||||
int minorVersion() => _bindings.spine_minor_version();
|
int minorVersion() => _bindings.spine_minor_version();
|
||||||
void reportLeaks() => _bindings.spine_report_leaks();
|
void reportLeaks() => _bindings.spine_report_leaks();
|
||||||
|
|
||||||
class SpineAtlas {
|
class Atlas {
|
||||||
Pointer<spine_atlas> _atlas;
|
final Pointer<spine_atlas> _atlas;
|
||||||
List<Image> atlasPages;
|
final List<Image> atlasPages;
|
||||||
List<Paint> atlasPagePaints;
|
final List<Paint> atlasPagePaints;
|
||||||
bool _disposed;
|
bool _disposed;
|
||||||
|
|
||||||
SpineAtlas(this._atlas, this.atlasPages, this.atlasPagePaints): _disposed = false;
|
Atlas(this._atlas, this.atlasPages, this.atlasPagePaints): _disposed = false;
|
||||||
|
|
||||||
static Future<SpineAtlas> _load(String atlasFileName, Future<Uint8List> Function(String name) loadFile) async {
|
static Future<Atlas> _load(String atlasFileName, Future<Uint8List> Function(String name) loadFile) async {
|
||||||
final atlasBytes = await loadFile(atlasFileName);
|
final atlasBytes = await loadFile(atlasFileName);
|
||||||
final atlasData = convert.utf8.decode(atlasBytes);
|
final atlasData = convert.utf8.decode(atlasBytes);
|
||||||
final atlasDataNative = atlasData.toNativeUtf8();
|
final atlasDataNative = atlasData.toNativeUtf8();
|
||||||
@ -54,18 +54,18 @@ class SpineAtlas {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpineAtlas(atlas, atlasPages, atlasPagePaints);
|
return Atlas(atlas, atlasPages, atlasPagePaints);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<SpineAtlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
|
static Future<Atlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
|
||||||
return _load(atlasFileName, (file) async => (await assetBundle.load(file)).buffer.asUint8List());
|
return _load(atlasFileName, (file) async => (await assetBundle.load(file)).buffer.asUint8List());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<SpineAtlas> fromFile(String atlasFileName) async {
|
static Future<Atlas> fromFile(String atlasFileName) async {
|
||||||
return _load(atlasFileName, (file) => File(file).readAsBytes());
|
return _load(atlasFileName, (file) => File(file).readAsBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<SpineAtlas> fromUrl(String atlasFileName) async {
|
static Future<Atlas> fromUrl(String atlasFileName) async {
|
||||||
return _load(atlasFileName, (file) async {
|
return _load(atlasFileName, (file) async {
|
||||||
return (await http.get(Uri.parse(file))).bodyBytes;
|
return (await http.get(Uri.parse(file))).bodyBytes;
|
||||||
});
|
});
|
||||||
@ -79,13 +79,13 @@ class SpineAtlas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpineSkeletonData {
|
class SkeletonData {
|
||||||
Pointer<spine_skeleton_data> _skeletonData;
|
final Pointer<spine_skeleton_data> _skeletonData;
|
||||||
bool _disposed;
|
bool _disposed;
|
||||||
|
|
||||||
SpineSkeletonData(this._skeletonData): _disposed = false;
|
SkeletonData(this._skeletonData): _disposed = false;
|
||||||
|
|
||||||
static SpineSkeletonData fromJson(SpineAtlas atlas, String json) {
|
static SkeletonData fromJson(Atlas atlas, String json) {
|
||||||
final jsonNative = json.toNativeUtf8();
|
final jsonNative = json.toNativeUtf8();
|
||||||
final skeletonData = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast());
|
final skeletonData = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast());
|
||||||
if (skeletonData.ref.error.address != nullptr.address) {
|
if (skeletonData.ref.error.address != nullptr.address) {
|
||||||
@ -94,10 +94,10 @@ class SpineSkeletonData {
|
|||||||
_bindings.spine_skeleton_data_dispose(skeletonData);
|
_bindings.spine_skeleton_data_dispose(skeletonData);
|
||||||
throw Exception("Couldn't load skeleton data: " + message);
|
throw Exception("Couldn't load skeleton data: " + message);
|
||||||
}
|
}
|
||||||
return SpineSkeletonData(skeletonData);
|
return SkeletonData(skeletonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SpineSkeletonData fromBinary(SpineAtlas atlas, Uint8List binary) {
|
static SkeletonData fromBinary(Atlas atlas, Uint8List binary) {
|
||||||
final Pointer<Uint8> binaryNative = malloc.allocate(binary.lengthInBytes);
|
final Pointer<Uint8> binaryNative = malloc.allocate(binary.lengthInBytes);
|
||||||
binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary);
|
binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary);
|
||||||
final skeletonData = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes);
|
final skeletonData = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes);
|
||||||
@ -108,7 +108,7 @@ class SpineSkeletonData {
|
|||||||
_bindings.spine_skeleton_data_dispose(skeletonData);
|
_bindings.spine_skeleton_data_dispose(skeletonData);
|
||||||
throw Exception("Couldn't load skeleton data: " + message);
|
throw Exception("Couldn't load skeleton data: " + message);
|
||||||
}
|
}
|
||||||
return SpineSkeletonData(skeletonData);
|
return SkeletonData(skeletonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -118,14 +118,127 @@ class SpineSkeletonData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpineSkeletonDrawable {
|
class Skeleton {
|
||||||
SpineAtlas atlas;
|
final spine_skeleton _skeleton;
|
||||||
SpineSkeletonData skeletonData;
|
|
||||||
late Pointer<spine_skeleton_drawable> _drawable;
|
Skeleton(this._skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrackEntry {
|
||||||
|
final spine_track_entry _entry;
|
||||||
|
|
||||||
|
TrackEntry(this._entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimationState {
|
||||||
|
final spine_animation_state _state;
|
||||||
|
|
||||||
|
AnimationState(this._state);
|
||||||
|
|
||||||
|
/// Increments the track entry times, setting queued animations as current if needed
|
||||||
|
/// @param delta delta time
|
||||||
|
void update(double delta) {
|
||||||
|
_bindings.spine_animation_state_update(_state, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
|
||||||
|
/// animation state can be applied to multiple skeletons to pose them identically.
|
||||||
|
void apply(Skeleton skeleton) {
|
||||||
|
_bindings.spine_animation_state_apply(_state, skeleton._skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all animations from all tracks, leaving skeletons in their previous pose.
|
||||||
|
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
||||||
|
/// rather than leaving them in their previous pose.
|
||||||
|
void clearTracks() {
|
||||||
|
_bindings.spine_animation_state_clear_tracks(_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all animations from the tracks, leaving skeletons in their previous pose.
|
||||||
|
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
||||||
|
/// rather than leaving them in their previous pose.
|
||||||
|
void clearTrack(int trackIndex) {
|
||||||
|
_bindings.spine_animation_state_clear_track(_state, trackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the current animation for a track, discarding any queued animations.
|
||||||
|
/// @param loop If true, the animation will repeat.
|
||||||
|
/// If false, it will not, instead its last frame is applied if played beyond its duration.
|
||||||
|
/// In either case TrackEntry.TrackEnd determines when the track is cleared.
|
||||||
|
/// @return
|
||||||
|
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept
|
||||||
|
/// after AnimationState.Dispose.
|
||||||
|
TrackEntry setAnimation(int trackIndex, String animationName, bool loop) {
|
||||||
|
final animation = animationName.toNativeUtf8();
|
||||||
|
final entry = _bindings.spine_animation_state_set_animation(_state, trackIndex, animation.cast(), loop ? -1 : 0);
|
||||||
|
calloc.free(animation);
|
||||||
|
if (entry.address == nullptr.address) throw Exception("Couldn't set animation $animationName");
|
||||||
|
return TrackEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an animation to be played delay seconds after the current or last queued animation
|
||||||
|
/// for a track. If the track is empty, it is equivalent to calling setAnimation.
|
||||||
|
/// @param delay
|
||||||
|
/// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
||||||
|
/// duration of the previous track minus any mix duration plus the negative delay.
|
||||||
|
///
|
||||||
|
/// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
|
||||||
|
/// after AnimationState.Dispose
|
||||||
|
TrackEntry addAnimation(int trackIndex, String animationName, bool loop, double delay) {
|
||||||
|
final animation = animationName.toNativeUtf8();
|
||||||
|
final entry = _bindings.spine_animation_state_add_animation(_state, trackIndex, animation.cast(), loop ? -1 : 0, delay);
|
||||||
|
calloc.free(animation);
|
||||||
|
if (entry.address == nullptr.address) throw Exception("Couldn't add animation $animationName");
|
||||||
|
return TrackEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration.
|
||||||
|
TrackEntry setEmptyAnimation(int trackIndex, double mixDuration) {
|
||||||
|
final entry = _bindings.spine_animation_state_set_empty_animation(_state, trackIndex, mixDuration);
|
||||||
|
return TrackEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the
|
||||||
|
/// specified mix duration.
|
||||||
|
/// @return
|
||||||
|
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose.
|
||||||
|
///
|
||||||
|
/// @param trackIndex Track number.
|
||||||
|
/// @param mixDuration Mix duration.
|
||||||
|
/// @param delay Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
||||||
|
/// duration of the previous track minus any mix duration plus the negative delay.
|
||||||
|
TrackEntry addEmptyAnimation(int trackIndex, double mixDuration, double delay) {
|
||||||
|
final entry = _bindings.spine_animation_state_add_empty_animation(_state, trackIndex, mixDuration, delay);
|
||||||
|
return TrackEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration.
|
||||||
|
void setEmptyAnimations(double mixDuration) {
|
||||||
|
_bindings.spine_animation_state_set_empty_animations(_state, mixDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
double getTimeScale() {
|
||||||
|
return _bindings.spine_animation_state_get_time_scale(_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTimeScale(double timeScale) {
|
||||||
|
_bindings.spine_animation_state_set_time_scale(_state, timeScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkeletonDrawable {
|
||||||
|
final Atlas atlas;
|
||||||
|
final SkeletonData skeletonData;
|
||||||
|
late final Pointer<spine_skeleton_drawable> _drawable;
|
||||||
|
late final Skeleton skeleton;
|
||||||
|
late final AnimationState animationState;
|
||||||
|
final bool _ownsData;
|
||||||
bool _disposed;
|
bool _disposed;
|
||||||
|
|
||||||
SpineSkeletonDrawable(this.atlas, this.skeletonData): _disposed = false {
|
SkeletonDrawable(this.atlas, this.skeletonData, this._ownsData): _disposed = false {
|
||||||
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData);
|
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData);
|
||||||
|
skeleton = Skeleton(_drawable.ref.skeleton);
|
||||||
|
animationState = AnimationState(_drawable.ref.animationState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(double delta) {
|
void update(double delta) {
|
||||||
@ -133,13 +246,13 @@ class SpineSkeletonDrawable {
|
|||||||
_bindings.spine_skeleton_drawable_update(_drawable, delta);
|
_bindings.spine_skeleton_drawable_update(_drawable, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SpineRenderCommand> render() {
|
List<RenderCommand> render() {
|
||||||
if (_disposed) return [];
|
if (_disposed) return [];
|
||||||
Pointer<spine_render_command> nativeCmd = _bindings.spine_skeleton_drawable_render(_drawable);
|
Pointer<spine_render_command> nativeCmd = _bindings.spine_skeleton_drawable_render(_drawable);
|
||||||
List<SpineRenderCommand> commands = [];
|
List<RenderCommand> commands = [];
|
||||||
while(nativeCmd.address != nullptr.address) {
|
while(nativeCmd.address != nullptr.address) {
|
||||||
final atlasPage = atlas.atlasPages[nativeCmd.ref.atlasPage];
|
final atlasPage = atlas.atlasPages[nativeCmd.ref.atlasPage];
|
||||||
commands.add(SpineRenderCommand(nativeCmd, atlasPage.width.toDouble(), atlasPage.height.toDouble()));
|
commands.add(RenderCommand(nativeCmd, atlasPage.width.toDouble(), atlasPage.height.toDouble()));
|
||||||
nativeCmd = nativeCmd.ref.next;
|
nativeCmd = nativeCmd.ref.next;
|
||||||
}
|
}
|
||||||
return commands;
|
return commands;
|
||||||
@ -148,17 +261,19 @@ class SpineSkeletonDrawable {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
atlas.dispose();
|
if (_ownsData) {
|
||||||
skeletonData.dispose();
|
atlas.dispose();
|
||||||
|
skeletonData.dispose();
|
||||||
|
}
|
||||||
_bindings.spine_skeleton_drawable_dispose(_drawable);
|
_bindings.spine_skeleton_drawable_dispose(_drawable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpineRenderCommand {
|
class RenderCommand {
|
||||||
late Vertices vertices;
|
late final Vertices vertices;
|
||||||
late int atlasPageIndex;
|
late final int atlasPageIndex;
|
||||||
|
|
||||||
SpineRenderCommand(Pointer<spine_render_command> nativeCmd, double pageWidth, double pageHeight) {
|
RenderCommand(Pointer<spine_render_command> nativeCmd, double pageWidth, double pageHeight) {
|
||||||
atlasPageIndex = nativeCmd.ref.atlasPage;
|
atlasPageIndex = nativeCmd.ref.atlasPage;
|
||||||
int numVertices = nativeCmd.ref.numVertices;
|
int numVertices = nativeCmd.ref.numVertices;
|
||||||
int numIndices = nativeCmd.ref.numIndices;
|
int numIndices = nativeCmd.ref.numIndices;
|
||||||
|
|||||||
@ -208,6 +208,225 @@ class SpineFlutterBindings {
|
|||||||
late final _spine_skeleton_drawable_dispose =
|
late final _spine_skeleton_drawable_dispose =
|
||||||
_spine_skeleton_drawable_disposePtr
|
_spine_skeleton_drawable_disposePtr
|
||||||
.asFunction<void Function(ffi.Pointer<spine_skeleton_drawable>)>();
|
.asFunction<void Function(ffi.Pointer<spine_skeleton_drawable>)>();
|
||||||
|
|
||||||
|
void spine_animation_state_update(
|
||||||
|
spine_animation_state state,
|
||||||
|
double delta,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_update(
|
||||||
|
state,
|
||||||
|
delta,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_updatePtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(spine_animation_state,
|
||||||
|
ffi.Float)>>('spine_animation_state_update');
|
||||||
|
late final _spine_animation_state_update = _spine_animation_state_updatePtr
|
||||||
|
.asFunction<void Function(spine_animation_state, double)>();
|
||||||
|
|
||||||
|
void spine_animation_state_apply(
|
||||||
|
spine_animation_state state,
|
||||||
|
spine_skeleton skeleton,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_apply(
|
||||||
|
state,
|
||||||
|
skeleton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_applyPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(spine_animation_state,
|
||||||
|
spine_skeleton)>>('spine_animation_state_apply');
|
||||||
|
late final _spine_animation_state_apply = _spine_animation_state_applyPtr
|
||||||
|
.asFunction<void Function(spine_animation_state, spine_skeleton)>();
|
||||||
|
|
||||||
|
void spine_animation_state_clear_tracks(
|
||||||
|
spine_animation_state state,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_clear_tracks(
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_clear_tracksPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(spine_animation_state)>>(
|
||||||
|
'spine_animation_state_clear_tracks');
|
||||||
|
late final _spine_animation_state_clear_tracks =
|
||||||
|
_spine_animation_state_clear_tracksPtr
|
||||||
|
.asFunction<void Function(spine_animation_state)>();
|
||||||
|
|
||||||
|
void spine_animation_state_clear_track(
|
||||||
|
spine_animation_state state,
|
||||||
|
int trackIndex,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_clear_track(
|
||||||
|
state,
|
||||||
|
trackIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_clear_trackPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(spine_animation_state,
|
||||||
|
ffi.Int32)>>('spine_animation_state_clear_track');
|
||||||
|
late final _spine_animation_state_clear_track =
|
||||||
|
_spine_animation_state_clear_trackPtr
|
||||||
|
.asFunction<void Function(spine_animation_state, int)>();
|
||||||
|
|
||||||
|
spine_track_entry spine_animation_state_set_animation(
|
||||||
|
spine_animation_state state,
|
||||||
|
int trackIndex,
|
||||||
|
ffi.Pointer<ffi.Int8> animationName,
|
||||||
|
int loop,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_set_animation(
|
||||||
|
state,
|
||||||
|
trackIndex,
|
||||||
|
animationName,
|
||||||
|
loop,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_set_animationPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
spine_track_entry Function(
|
||||||
|
spine_animation_state,
|
||||||
|
ffi.Int32,
|
||||||
|
ffi.Pointer<ffi.Int8>,
|
||||||
|
ffi.Int32)>>('spine_animation_state_set_animation');
|
||||||
|
late final _spine_animation_state_set_animation =
|
||||||
|
_spine_animation_state_set_animationPtr.asFunction<
|
||||||
|
spine_track_entry Function(
|
||||||
|
spine_animation_state, int, ffi.Pointer<ffi.Int8>, int)>();
|
||||||
|
|
||||||
|
spine_track_entry spine_animation_state_add_animation(
|
||||||
|
spine_animation_state state,
|
||||||
|
int trackIndex,
|
||||||
|
ffi.Pointer<ffi.Int8> animationName,
|
||||||
|
int loop,
|
||||||
|
double delay,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_add_animation(
|
||||||
|
state,
|
||||||
|
trackIndex,
|
||||||
|
animationName,
|
||||||
|
loop,
|
||||||
|
delay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_add_animationPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
spine_track_entry Function(
|
||||||
|
spine_animation_state,
|
||||||
|
ffi.Int32,
|
||||||
|
ffi.Pointer<ffi.Int8>,
|
||||||
|
ffi.Int32,
|
||||||
|
ffi.Float)>>('spine_animation_state_add_animation');
|
||||||
|
late final _spine_animation_state_add_animation =
|
||||||
|
_spine_animation_state_add_animationPtr.asFunction<
|
||||||
|
spine_track_entry Function(spine_animation_state, int,
|
||||||
|
ffi.Pointer<ffi.Int8>, int, double)>();
|
||||||
|
|
||||||
|
spine_track_entry spine_animation_state_set_empty_animation(
|
||||||
|
spine_animation_state state,
|
||||||
|
int trackIndex,
|
||||||
|
double mixDuration,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_set_empty_animation(
|
||||||
|
state,
|
||||||
|
trackIndex,
|
||||||
|
mixDuration,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_set_empty_animationPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
spine_track_entry Function(spine_animation_state, ffi.Int32,
|
||||||
|
ffi.Float)>>('spine_animation_state_set_empty_animation');
|
||||||
|
late final _spine_animation_state_set_empty_animation =
|
||||||
|
_spine_animation_state_set_empty_animationPtr.asFunction<
|
||||||
|
spine_track_entry Function(spine_animation_state, int, double)>();
|
||||||
|
|
||||||
|
spine_track_entry spine_animation_state_add_empty_animation(
|
||||||
|
spine_animation_state state,
|
||||||
|
int trackIndex,
|
||||||
|
double mixDuration,
|
||||||
|
double delay,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_add_empty_animation(
|
||||||
|
state,
|
||||||
|
trackIndex,
|
||||||
|
mixDuration,
|
||||||
|
delay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_add_empty_animationPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
spine_track_entry Function(
|
||||||
|
spine_animation_state,
|
||||||
|
ffi.Int32,
|
||||||
|
ffi.Float,
|
||||||
|
ffi.Float)>>('spine_animation_state_add_empty_animation');
|
||||||
|
late final _spine_animation_state_add_empty_animation =
|
||||||
|
_spine_animation_state_add_empty_animationPtr.asFunction<
|
||||||
|
spine_track_entry Function(
|
||||||
|
spine_animation_state, int, double, double)>();
|
||||||
|
|
||||||
|
void spine_animation_state_set_empty_animations(
|
||||||
|
spine_animation_state state,
|
||||||
|
double mixDuration,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_set_empty_animations(
|
||||||
|
state,
|
||||||
|
mixDuration,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_set_empty_animationsPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(spine_animation_state,
|
||||||
|
ffi.Float)>>('spine_animation_state_set_empty_animations');
|
||||||
|
late final _spine_animation_state_set_empty_animations =
|
||||||
|
_spine_animation_state_set_empty_animationsPtr
|
||||||
|
.asFunction<void Function(spine_animation_state, double)>();
|
||||||
|
|
||||||
|
double spine_animation_state_get_time_scale(
|
||||||
|
spine_animation_state state,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_get_time_scale(
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_get_time_scalePtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Float Function(spine_animation_state)>>(
|
||||||
|
'spine_animation_state_get_time_scale');
|
||||||
|
late final _spine_animation_state_get_time_scale =
|
||||||
|
_spine_animation_state_get_time_scalePtr
|
||||||
|
.asFunction<double Function(spine_animation_state)>();
|
||||||
|
|
||||||
|
void spine_animation_state_set_time_scale(
|
||||||
|
spine_animation_state state,
|
||||||
|
double timeScale,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_set_time_scale(
|
||||||
|
state,
|
||||||
|
timeScale,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_set_time_scalePtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(spine_animation_state,
|
||||||
|
ffi.Float)>>('spine_animation_state_set_time_scale');
|
||||||
|
late final _spine_animation_state_set_time_scale =
|
||||||
|
_spine_animation_state_set_time_scalePtr
|
||||||
|
.asFunction<void Function(spine_animation_state, double)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class spine_atlas extends ffi.Struct {
|
class spine_atlas extends ffi.Struct {
|
||||||
@ -259,11 +478,15 @@ class spine_render_command extends ffi.Struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class spine_skeleton_drawable extends ffi.Struct {
|
class spine_skeleton_drawable extends ffi.Struct {
|
||||||
external ffi.Pointer<ffi.Void> skeleton;
|
external spine_skeleton skeleton;
|
||||||
|
|
||||||
external ffi.Pointer<ffi.Void> animationState;
|
external spine_animation_state animationState;
|
||||||
|
|
||||||
external ffi.Pointer<ffi.Void> clipping;
|
external ffi.Pointer<ffi.Void> clipping;
|
||||||
|
|
||||||
external ffi.Pointer<spine_render_command> renderCommand;
|
external ffi.Pointer<spine_render_command> renderCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef spine_skeleton = ffi.Pointer<ffi.Void>;
|
||||||
|
typedef spine_animation_state = ffi.Pointer<ffi.Void>;
|
||||||
|
typedef spine_track_entry = ffi.Pointer<ffi.Void>;
|
||||||
|
|||||||
@ -9,78 +9,114 @@ import 'package:http/http.dart' as http;
|
|||||||
|
|
||||||
import 'spine_flutter.dart';
|
import 'spine_flutter.dart';
|
||||||
|
|
||||||
abstract class SpineWidgetController {
|
class SpineWidgetController {
|
||||||
|
Atlas? _atlas;
|
||||||
|
SkeletonData? _data;
|
||||||
|
SkeletonDrawable? _drawable;
|
||||||
|
final void Function(SpineWidgetController controller)? onInitialized;
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
SpineWidgetController([this.onInitialized]);
|
||||||
|
|
||||||
|
void _initialize(Atlas atlas, SkeletonData data, SkeletonDrawable drawable) {
|
||||||
|
_atlas = atlas;
|
||||||
|
_data = data;
|
||||||
|
_drawable = drawable;
|
||||||
|
initialized = true;
|
||||||
|
onInitialized?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Atlas? get atlas => _atlas;
|
||||||
|
SkeletonData? get skeletonData => _data;
|
||||||
|
AnimationState? get animationState => _drawable?.animationState;
|
||||||
|
Skeleton? get skeleton => _drawable?.skeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AssetType {
|
enum AssetType {
|
||||||
Asset,
|
Asset,
|
||||||
File,
|
File,
|
||||||
Http
|
Http,
|
||||||
|
Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpineWidget extends StatefulWidget {
|
class SpineWidget extends StatefulWidget {
|
||||||
final String skeletonFile;
|
final String? skeletonFile;
|
||||||
final String atlasFile;
|
final String? atlasFile;
|
||||||
|
final SkeletonData? skeletonData;
|
||||||
|
final Atlas? atlas;
|
||||||
|
final SpineWidgetController controller;
|
||||||
final AssetType _assetType;
|
final AssetType _assetType;
|
||||||
|
|
||||||
const SpineWidget.asset(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.Asset;
|
const SpineWidget.asset(this.skeletonFile, this.atlasFile, this.controller, {super.key}): _assetType = AssetType.Asset, atlas = null, skeletonData = null;
|
||||||
const SpineWidget.file(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.File;
|
const SpineWidget.file(this.skeletonFile, this.atlasFile, this.controller, {super.key}): _assetType = AssetType.File, atlas = null, skeletonData = null;
|
||||||
const SpineWidget.http(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.Http;
|
const SpineWidget.http(this.skeletonFile, this.atlasFile, this.controller, {super.key}): _assetType = AssetType.Http, atlas = null, skeletonData = null;
|
||||||
|
const SpineWidget.raw(this.skeletonData, this.atlas, this.controller, {super.key}): _assetType = AssetType.Raw, atlasFile = null, skeletonFile = null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SpineWidget> createState() => _SpineWidgetState();
|
State<SpineWidget> createState() => _SpineWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SpineWidgetState extends State<SpineWidget> {
|
class _SpineWidgetState extends State<SpineWidget> {
|
||||||
SpineSkeletonDrawable? skeletonDrawable;
|
SkeletonDrawable? skeletonDrawable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
loadSkeleton(widget.skeletonFile, widget.atlasFile, widget._assetType);
|
if (widget._assetType == AssetType.Raw) {
|
||||||
|
loadRaw(widget.skeletonData!, widget.atlas!);
|
||||||
|
} else {
|
||||||
|
loadFromAsset(widget.skeletonFile!, widget.atlasFile!, widget._assetType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadSkeleton(String skeletonFile, String atlasFile, AssetType assetType) async {
|
void loadRaw(SkeletonData skeletonData, Atlas atlas) {
|
||||||
late SpineAtlas atlas;
|
skeletonDrawable = SkeletonDrawable(atlas, skeletonData, false);
|
||||||
late SpineSkeletonData skeletonData;
|
skeletonDrawable?.update(0.016);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromAsset(String skeletonFile, String atlasFile, AssetType assetType) async {
|
||||||
|
late Atlas atlas;
|
||||||
|
late SkeletonData skeletonData;
|
||||||
|
|
||||||
switch (assetType) {
|
switch (assetType) {
|
||||||
case AssetType.Asset:
|
case AssetType.Asset:
|
||||||
atlas = await SpineAtlas.fromAsset(rootBundle, atlasFile);
|
atlas = await Atlas.fromAsset(rootBundle, atlasFile);
|
||||||
skeletonData = skeletonFile.endsWith(".json")
|
skeletonData = skeletonFile.endsWith(".json")
|
||||||
? SpineSkeletonData.fromJson(
|
? SkeletonData.fromJson(
|
||||||
atlas, await rootBundle.loadString(skeletonFile))
|
atlas, await rootBundle.loadString(skeletonFile))
|
||||||
: SpineSkeletonData.fromBinary(
|
: SkeletonData.fromBinary(
|
||||||
atlas, (await rootBundle.load(skeletonFile)).buffer.asUint8List());
|
atlas, (await rootBundle.load(skeletonFile)).buffer.asUint8List());
|
||||||
break;
|
break;
|
||||||
case AssetType.File:
|
case AssetType.File:
|
||||||
atlas = await SpineAtlas.fromFile(atlasFile);
|
atlas = await Atlas.fromFile(atlasFile);
|
||||||
skeletonData = skeletonFile.endsWith(".json")
|
skeletonData = skeletonFile.endsWith(".json")
|
||||||
? SpineSkeletonData.fromJson(
|
? SkeletonData.fromJson(
|
||||||
atlas, utf8.decode(await File(skeletonFile).readAsBytes()))
|
atlas, utf8.decode(await File(skeletonFile).readAsBytes()))
|
||||||
: SpineSkeletonData.fromBinary(
|
: SkeletonData.fromBinary(
|
||||||
atlas, await File(skeletonFile).readAsBytes());
|
atlas, await File(skeletonFile).readAsBytes());
|
||||||
break;
|
break;
|
||||||
case AssetType.Http:
|
case AssetType.Http:
|
||||||
atlas = await SpineAtlas.fromUrl(atlasFile);
|
atlas = await Atlas.fromUrl(atlasFile);
|
||||||
skeletonData = skeletonFile.endsWith(".json")
|
skeletonData = skeletonFile.endsWith(".json")
|
||||||
? SpineSkeletonData.fromJson(
|
? SkeletonData.fromJson(
|
||||||
atlas, utf8.decode((await http.get(Uri.parse(skeletonFile))).bodyBytes))
|
atlas, utf8.decode((await http.get(Uri.parse(skeletonFile))).bodyBytes))
|
||||||
: SpineSkeletonData.fromBinary(
|
: SkeletonData.fromBinary(
|
||||||
atlas, (await http.get(Uri.parse(skeletonFile))).bodyBytes);
|
atlas, (await http.get(Uri.parse(skeletonFile))).bodyBytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
skeletonDrawable = SpineSkeletonDrawable(atlas, skeletonData);
|
skeletonDrawable = SkeletonDrawable(atlas, skeletonData, true);
|
||||||
skeletonDrawable?.update(0.016);
|
skeletonDrawable?.update(0.016);
|
||||||
setState(() {});
|
widget.controller._initialize(atlas, skeletonData, skeletonDrawable!);
|
||||||
|
setState(() {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (skeletonDrawable != null) {
|
if (skeletonDrawable != null) {
|
||||||
print("Skeleton loaded, rebuilding painter");
|
print("Skeleton loaded, rebuilding painter");
|
||||||
return _SpineRenderObjectWidget(skeletonDrawable!);
|
return _SpineRenderObjectWidget(skeletonDrawable!, widget.controller);
|
||||||
} else {
|
} else {
|
||||||
print("Skeleton not loaded yet");
|
print("Skeleton not loaded yet");
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
@ -95,30 +131,32 @@ class _SpineWidgetState extends State<SpineWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
|
class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
|
||||||
final SpineSkeletonDrawable skeletonDrawable;
|
final SkeletonDrawable _skeletonDrawable;
|
||||||
|
final SpineWidgetController _controller;
|
||||||
|
|
||||||
_SpineRenderObjectWidget(this.skeletonDrawable);
|
_SpineRenderObjectWidget(this._skeletonDrawable, this._controller);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
return _SpineRenderObject(skeletonDrawable);
|
return _SpineRenderObject(_skeletonDrawable, _controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context,
|
void updateRenderObject(BuildContext context,
|
||||||
covariant _SpineRenderObject renderObject) {
|
covariant _SpineRenderObject renderObject) {
|
||||||
renderObject.skeletonDrawable = skeletonDrawable;
|
renderObject.skeletonDrawable = _skeletonDrawable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SpineRenderObject extends RenderBox {
|
class _SpineRenderObject extends RenderBox {
|
||||||
SpineSkeletonDrawable _skeletonDrawable;
|
SkeletonDrawable _skeletonDrawable;
|
||||||
|
SpineWidgetController _controller;
|
||||||
double _deltaTime = 0;
|
double _deltaTime = 0;
|
||||||
final Stopwatch _stopwatch = Stopwatch();
|
final Stopwatch _stopwatch = Stopwatch();
|
||||||
|
|
||||||
_SpineRenderObject(this._skeletonDrawable);
|
_SpineRenderObject(this._skeletonDrawable, this._controller);
|
||||||
|
|
||||||
set skeletonDrawable(SpineSkeletonDrawable skeletonDrawable) {
|
set skeletonDrawable(SkeletonDrawable skeletonDrawable) {
|
||||||
if (_skeletonDrawable == skeletonDrawable) return;
|
if (_skeletonDrawable == skeletonDrawable) return;
|
||||||
|
|
||||||
_skeletonDrawable = skeletonDrawable;
|
_skeletonDrawable = skeletonDrawable;
|
||||||
|
|||||||
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
using namespace spine;
|
using namespace spine;
|
||||||
|
|
||||||
|
spine::SpineExtension *spine::getDefaultExtension() {
|
||||||
|
return new spine::DebugExtension(new spine::DefaultSpineExtension());
|
||||||
|
}
|
||||||
|
|
||||||
FFI_PLUGIN_EXPORT int32_t spine_major_version() {
|
FFI_PLUGIN_EXPORT int32_t spine_major_version() {
|
||||||
return SPINE_MAJOR_VERSION;
|
return SPINE_MAJOR_VERSION;
|
||||||
}
|
}
|
||||||
@ -15,11 +19,11 @@ FFI_PLUGIN_EXPORT int32_t spine_minor_version() {
|
|||||||
|
|
||||||
FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData) {
|
FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData) {
|
||||||
if (!atlasData) return nullptr;
|
if (!atlasData) return nullptr;
|
||||||
int length = strlen(atlasData);
|
int length = (int)strlen(atlasData);
|
||||||
auto atlas = new Atlas(atlasData, length, "", (TextureLoader*)nullptr, false);
|
auto atlas = new Atlas(atlasData, length, "", (TextureLoader*)nullptr, false);
|
||||||
spine_atlas *result = SpineExtension::calloc<spine_atlas>(1, __FILE__, __LINE__);
|
spine_atlas *result = SpineExtension::calloc<spine_atlas>(1, __FILE__, __LINE__);
|
||||||
result->atlas = atlas;
|
result->atlas = atlas;
|
||||||
result->numImagePaths = atlas->getPages().size();
|
result->numImagePaths = (int32_t)atlas->getPages().size();
|
||||||
result->imagePaths = SpineExtension::calloc<char *>(result->numImagePaths, __FILE__, __LINE__);
|
result->imagePaths = SpineExtension::calloc<char *>(result->numImagePaths, __FILE__, __LINE__);
|
||||||
for (int i = 0; i < result->numImagePaths; i++) {
|
for (int i = 0; i < result->numImagePaths; i++) {
|
||||||
result->imagePaths[i] = strdup(atlas->getPages()[i]->texturePath.buffer());
|
result->imagePaths[i] = strdup(atlas->getPages()[i]->texturePath.buffer());
|
||||||
@ -213,10 +217,10 @@ FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_ske
|
|||||||
worldVertices.setSize(mesh->getWorldVerticesLength(), 0);
|
worldVertices.setSize(mesh->getWorldVerticesLength(), 0);
|
||||||
pageIndex = ((AtlasRegion *) mesh->getRendererObject())->page->index;
|
pageIndex = ((AtlasRegion *) mesh->getRendererObject())->page->index;
|
||||||
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2);
|
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2);
|
||||||
verticesCount = mesh->getWorldVerticesLength() >> 1;
|
verticesCount = (int)(mesh->getWorldVerticesLength() >> 1);
|
||||||
uvs = &mesh->getUVs();
|
uvs = &mesh->getUVs();
|
||||||
indices = &mesh->getTriangles();
|
indices = &mesh->getTriangles();
|
||||||
indicesCount = indices->size();
|
indicesCount = (int)indices->size();
|
||||||
|
|
||||||
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
|
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
|
||||||
ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment();
|
ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment();
|
||||||
@ -234,10 +238,10 @@ FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_ske
|
|||||||
if (clipper.isClipping()) {
|
if (clipper.isClipping()) {
|
||||||
clipper.clipTriangles(worldVertices, *indices, *uvs, 2);
|
clipper.clipTriangles(worldVertices, *indices, *uvs, 2);
|
||||||
vertices = &clipper.getClippedVertices();
|
vertices = &clipper.getClippedVertices();
|
||||||
verticesCount = clipper.getClippedVertices().size() >> 1;
|
verticesCount = (int)(clipper.getClippedVertices().size() >> 1);
|
||||||
uvs = &clipper.getClippedUVs();
|
uvs = &clipper.getClippedUVs();
|
||||||
indices = &clipper.getClippedTriangles();
|
indices = &clipper.getClippedTriangles();
|
||||||
indicesCount = clipper.getClippedTriangles().size();
|
indicesCount = (int)(clipper.getClippedTriangles().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
spine_render_command *cmd = spine_render_command_create(verticesCount, indicesCount, (spine_blend_mode)slot.getData().getBlendMode(), pageIndex);
|
spine_render_command *cmd = spine_render_command_create(verticesCount, indicesCount, (spine_blend_mode)slot.getData().getBlendMode(), pageIndex);
|
||||||
@ -261,6 +265,68 @@ FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_ske
|
|||||||
return drawable->renderCommand;
|
return drawable->renderCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
spine::SpineExtension *spine::getDefaultExtension() {
|
FFI_PLUGIN_EXPORT void spine_animation_state_update(spine_animation_state state, float delta) {
|
||||||
return new spine::DebugExtension(new spine::DefaultSpineExtension());
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->update(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_apply(spine_animation_state state, spine_skeleton skeleton) {
|
||||||
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->apply(*(Skeleton*)skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_clear_tracks(spine_animation_state state) {
|
||||||
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->clearTracks();
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_clear_track(spine_animation_state state, int32_t trackIndex) {
|
||||||
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->clearTrack(trackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_set_animation(spine_animation_state state, int32_t trackIndex, const char* animationName, int32_t loop) {
|
||||||
|
if (state == nullptr) return nullptr;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
return _state->setAnimation(trackIndex, animationName, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_add_animation(spine_animation_state state, int32_t trackIndex, const char* animationName, int32_t loop, float delay) {
|
||||||
|
if (state == nullptr) return nullptr;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
return _state->addAnimation(trackIndex, animationName, loop, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_set_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration) {
|
||||||
|
if (state == nullptr) return nullptr;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
return _state->setEmptyAnimation(trackIndex, mixDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_add_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration, float delay) {
|
||||||
|
if (state == nullptr) return nullptr;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
return _state->addEmptyAnimation(trackIndex, mixDuration, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_set_empty_animations(spine_animation_state state, float mixDuration) {
|
||||||
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->setEmptyAnimations(mixDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT float spine_animation_state_get_time_scale(spine_animation_state state) {
|
||||||
|
if (state == nullptr) return 0;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
return _state->getTimeScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_set_time_scale(spine_animation_state state, float timeScale) {
|
||||||
|
if (state == nullptr) return;
|
||||||
|
AnimationState *_state = (AnimationState*)state;
|
||||||
|
_state->setTimeScale(timeScale);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,9 +65,13 @@ typedef struct spine_render_command {
|
|||||||
struct spine_render_command *next;
|
struct spine_render_command *next;
|
||||||
} spine_render_command;
|
} spine_render_command;
|
||||||
|
|
||||||
|
typedef void* spine_skeleton;
|
||||||
|
typedef void* spine_animation_state;
|
||||||
|
typedef void* spine_track_entry;
|
||||||
|
|
||||||
typedef struct spine_skeleton_drawable {
|
typedef struct spine_skeleton_drawable {
|
||||||
void *skeleton;
|
spine_skeleton skeleton;
|
||||||
void *animationState;
|
spine_animation_state animationState;
|
||||||
void *clipping;
|
void *clipping;
|
||||||
spine_render_command *renderCommand;
|
spine_render_command *renderCommand;
|
||||||
} spine_skeleton_drawable;
|
} spine_skeleton_drawable;
|
||||||
@ -77,3 +81,14 @@ FFI_PLUGIN_EXPORT void spine_skeleton_drawable_update(spine_skeleton_drawable *d
|
|||||||
FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_skeleton_drawable *drawable);
|
FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_skeleton_drawable *drawable);
|
||||||
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *drawable);
|
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *drawable);
|
||||||
|
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_update(spine_animation_state state, float delta);
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_apply(spine_animation_state state, spine_skeleton skeleton);
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_clear_tracks(spine_animation_state state);
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_clear_track(spine_animation_state state, int32_t trackIndex);
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_set_animation(spine_animation_state state, int32_t trackIndex, const char* animationName, int32_t loop);
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_add_animation(spine_animation_state state, int32_t trackIndex, const char* animationName, int32_t loop, float delay);
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_set_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration);
|
||||||
|
FFI_PLUGIN_EXPORT spine_track_entry spine_animation_state_add_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration, float delay);
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_set_empty_animations(spine_animation_state state, float mixDuration);
|
||||||
|
FFI_PLUGIN_EXPORT float spine_animation_state_get_time_scale(spine_animation_state state);
|
||||||
|
FFI_PLUGIN_EXPORT void spine_animation_state_set_time_scale(spine_animation_state state, float timeScale);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user