[flutter] Refactor skeleton data type hierarchy, implement Skeleton Dart side.

This commit is contained in:
Mario Zechner 2022-08-28 11:55:31 +02:00
parent 7adc7a2919
commit 6d50351b44
5 changed files with 234 additions and 66 deletions

View File

@ -12,12 +12,24 @@ class ExampleSelector extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
child: const Text('Spineboy'),
child: const Text('Simple Animation'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const Spineboy(),
builder: (context) => const SimpleAnimation(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Animation State Listener'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const AnimationStateEvents(),
),
);
},
@ -30,8 +42,8 @@ class ExampleSelector extends StatelessWidget {
}
}
class Spineboy extends StatelessWidget {
const Spineboy({Key? key}) : super(key: key);
class SimpleAnimation extends StatelessWidget {
const SimpleAnimation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -52,8 +64,13 @@ class AnimationStateEvents extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = SpineWidgetController((controller) {
for (final bone in controller.skeleton!.getBones()) {
print(bone);
}
controller.skeleton?.setScaleX(0.5);
controller.skeleton?.setScaleY(0.5);
controller.skeleton?.setColor(Color(1, 1, 0, 1));
final trackEntry = controller.animationState?.setAnimation(0, "walk", true);
controller.skeleton.setColor(1, 0, 0, 1);
});
return Scaffold(

View File

@ -80,41 +80,41 @@ class Atlas {
}
class SkeletonData {
final Pointer<spine_skeleton_data_result> _skeletonData;
final spine_skeleton_data _skeletonData;
bool _disposed;
SkeletonData(this._skeletonData): _disposed = false;
static SkeletonData fromJson(Atlas atlas, String json) {
final jsonNative = json.toNativeUtf8();
final skeletonData = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast());
if (skeletonData.ref.error.address != nullptr.address) {
final Pointer<Utf8> error = skeletonData.ref.error.cast();
final result = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast());
if (result.error.address != nullptr.address) {
final Pointer<Utf8> error = result.error.cast();
final message = error.toDartString();
_bindings.spine_skeleton_data_result_dispose(skeletonData);
calloc.free(error);
throw Exception("Couldn't load skeleton data: $message");
}
return SkeletonData(skeletonData);
return SkeletonData(result.skeletonData);
}
static SkeletonData fromBinary(Atlas atlas, Uint8List binary) {
final Pointer<Uint8> binaryNative = malloc.allocate(binary.lengthInBytes);
binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary);
final skeletonData = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes);
final result = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes);
malloc.free(binaryNative);
if (skeletonData.ref.error.address != nullptr.address) {
final Pointer<Utf8> error = skeletonData.ref.error.cast();
if (result.error.address != nullptr.address) {
final Pointer<Utf8> error = result.error.cast();
final message = error.toDartString();
_bindings.spine_skeleton_data_result_dispose(skeletonData);
calloc.free(error);
throw Exception("Couldn't load skeleton data: $message");
}
return SkeletonData(skeletonData);
return SkeletonData(result.skeletonData);
}
void dispose() {
if (_disposed) return;
_disposed = true;
_bindings.spine_skeleton_data_result_dispose(_skeletonData);
_bindings.spine_skeleton_data_dispose(_skeletonData);
}
}
@ -136,6 +136,21 @@ class Attachment {
Attachment(this._attachment);
}
class Skin {
final spine_skin _skin;
Skin(this._skin);
}
class Color {
double r;
double g;
double b;
double a;
Color(this.r, this.g, this.b, this.a);
}
class IkConstraint {
final spine_ik_constraint _constraint;
@ -154,6 +169,15 @@ class PathConstraint {
PathConstraint(this._constraint);
}
class Bounds {
double x;
double y;
double width;
double height;
Bounds(this.x, this.y, this.width, this.height);
}
class Skeleton {
final spine_skeleton _skeleton;
@ -266,6 +290,140 @@ class Skeleton {
if (constraint.address == nullptr.address) return null;
return PathConstraint(constraint);
}
/// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
/// @param outX The horizontal distance between the skeleton origin and the left side of the AABB.
/// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB.
/// @param outWidth The width of the AABB
/// @param outHeight The height of the AABB.
/// @param outVertexBuffer Reference to hold a Vector of floats. This method will assign it with new floats as needed.
Bounds getBounds() {
final nativeBounds = _bindings.spine_skeleton_get_bounds(_skeleton);
return Bounds(nativeBounds.x, nativeBounds.y, nativeBounds.width, nativeBounds.height);
}
Bone? getRootBone() {
final bone = _bindings.spine_skeleton_get_root_bone(_skeleton);
if (bone.address == nullptr.address) return null;
return Bone(bone);
}
SkeletonData? getData() {
final data = _bindings.spine_skeleton_get_data(_skeleton);
if (data.address == nullptr.address) return null;
return SkeletonData(data);
}
List<Bone> getBones() {
final List<Bone> bones = [];
final numBones = _bindings.spine_skeleton_get_num_bones(_skeleton);
final nativeBones = _bindings.spine_skeleton_get_bones(_skeleton);
for (int i = 0; i < numBones; i++) {
bones.add(Bone(nativeBones[i]));
}
return bones;
}
List<Slot> getSlots() {
final List<Slot> slots = [];
final numSlots = _bindings.spine_skeleton_get_num_slots(_skeleton);
final nativeSlots = _bindings.spine_skeleton_get_slots(_skeleton);
for (int i = 0; i < numSlots; i++) {
slots.add(Slot(nativeSlots[i]));
}
return slots;
}
List<Slot> getDrawOrder() {
final List<Slot> slots = [];
final numSlots = _bindings.spine_skeleton_get_num_draw_order(_skeleton);
final nativeDrawOrder = _bindings.spine_skeleton_get_draw_order(_skeleton);
for (int i = 0; i < numSlots; i++) {
slots.add(Slot(nativeDrawOrder[i]));
}
return slots;
}
List<IkConstraint> getIkConstraints() {
final List<IkConstraint> constraints = [];
final numConstraints = _bindings.spine_skeleton_get_num_ik_constraints(_skeleton);
final nativeConstraints = _bindings.spine_skeleton_get_ik_constraints(_skeleton);
for (int i = 0; i < numConstraints; i++) {
constraints.add(IkConstraint(nativeConstraints[i]));
}
return constraints;
}
List<PathConstraint> getPathConstraints() {
final List<PathConstraint> constraints = [];
final numConstraints = _bindings.spine_skeleton_get_num_path_constraints(_skeleton);
final nativeConstraints = _bindings.spine_skeleton_get_path_constraints(_skeleton);
for (int i = 0; i < numConstraints; i++) {
constraints.add(PathConstraint(nativeConstraints[i]));
}
return constraints;
}
List<TransformConstraint> getTransformConstraints() {
final List<TransformConstraint> constraints = [];
final numConstraints = _bindings.spine_skeleton_get_num_transform_constraints(_skeleton);
final nativeConstraints = _bindings.spine_skeleton_get_transform_constraints(_skeleton);
for (int i = 0; i < numConstraints; i++) {
constraints.add(TransformConstraint(nativeConstraints[i]));
}
return constraints;
}
Skin? getSkin() {
final skin = _bindings.spine_skeleton_get_skin(_skeleton);
if (skin.address == nullptr.address) return null;
return Skin(skin);
}
Color getColor() {
final color = _bindings.spine_skeleton_get_color(_skeleton);
return Color(color.r, color.g, color.b, color.a);
}
void setColor(Color color) {
_bindings.spine_skeleton_set_color(_skeleton, color.r, color.g, color.b, color.a);
}
void setPosition(double x, double y) {
_bindings.spine_skeleton_set_position(_skeleton, x, y);
}
double getX() {
return _bindings.spine_skeleton_get_x(_skeleton);
}
void setX(double x) {
_bindings.spine_skeleton_set_x(_skeleton, x);
}
double getY() {
return _bindings.spine_skeleton_get_x(_skeleton);
}
void setY(double y) {
_bindings.spine_skeleton_set_y(_skeleton, y);
}
double getScaleX() {
return _bindings.spine_skeleton_get_scale_x(_skeleton);
}
void setScaleX(double scaleX) {
_bindings.spine_skeleton_set_scale_x(_skeleton, scaleX);
}
double getScaleY() {
return _bindings.spine_skeleton_get_scale_x(_skeleton);
}
void setScaleY(double scaleY) {
_bindings.spine_skeleton_set_scale_y(_skeleton, scaleY);
}
}
class Animation {
@ -659,7 +817,7 @@ class SkeletonDrawable {
bool _disposed;
SkeletonDrawable(this.atlas, this.skeletonData, this._ownsData): _disposed = false {
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData.ref.skeletonData.cast());
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData);
skeleton = Skeleton(_drawable.ref.skeleton);
animationState = AnimationState(_drawable.ref.animationState);
}

View File

@ -82,7 +82,7 @@ class SpineFlutterBindings {
late final _spine_atlas_dispose = _spine_atlas_disposePtr
.asFunction<void Function(ffi.Pointer<spine_atlas>)>();
ffi.Pointer<spine_skeleton_data_result> spine_skeleton_data_load_json(
spine_skeleton_data_result spine_skeleton_data_load_json(
ffi.Pointer<spine_atlas> atlas,
ffi.Pointer<ffi.Int8> skeletonData,
) {
@ -94,15 +94,14 @@ class SpineFlutterBindings {
late final _spine_skeleton_data_load_jsonPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<spine_skeleton_data_result> Function(
ffi.Pointer<spine_atlas>,
spine_skeleton_data_result Function(ffi.Pointer<spine_atlas>,
ffi.Pointer<ffi.Int8>)>>('spine_skeleton_data_load_json');
late final _spine_skeleton_data_load_json =
_spine_skeleton_data_load_jsonPtr.asFunction<
ffi.Pointer<spine_skeleton_data_result> Function(
spine_skeleton_data_result Function(
ffi.Pointer<spine_atlas>, ffi.Pointer<ffi.Int8>)>();
ffi.Pointer<spine_skeleton_data_result> spine_skeleton_data_load_binary(
spine_skeleton_data_result spine_skeleton_data_load_binary(
ffi.Pointer<spine_atlas> atlas,
ffi.Pointer<ffi.Uint8> skeletonData,
int length,
@ -116,33 +115,31 @@ class SpineFlutterBindings {
late final _spine_skeleton_data_load_binaryPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<spine_skeleton_data_result> Function(
spine_skeleton_data_result Function(
ffi.Pointer<spine_atlas>,
ffi.Pointer<ffi.Uint8>,
ffi.Int32)>>('spine_skeleton_data_load_binary');
late final _spine_skeleton_data_load_binary =
_spine_skeleton_data_load_binaryPtr.asFunction<
ffi.Pointer<spine_skeleton_data_result> Function(
spine_skeleton_data_result Function(
ffi.Pointer<spine_atlas>, ffi.Pointer<ffi.Uint8>, int)>();
void spine_skeleton_data_result_dispose(
ffi.Pointer<spine_skeleton_data_result> skeletonData,
void spine_skeleton_data_dispose(
spine_skeleton_data skeletonData,
) {
return _spine_skeleton_data_result_dispose(
return _spine_skeleton_data_dispose(
skeletonData,
);
}
late final _spine_skeleton_data_result_disposePtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<spine_skeleton_data_result>)>>(
'spine_skeleton_data_result_dispose');
late final _spine_skeleton_data_result_dispose =
_spine_skeleton_data_result_disposePtr
.asFunction<void Function(ffi.Pointer<spine_skeleton_data_result>)>();
late final _spine_skeleton_data_disposePtr =
_lookup<ffi.NativeFunction<ffi.Void Function(spine_skeleton_data)>>(
'spine_skeleton_data_dispose');
late final _spine_skeleton_data_dispose = _spine_skeleton_data_disposePtr
.asFunction<void Function(spine_skeleton_data)>();
ffi.Pointer<spine_skeleton_drawable> spine_skeleton_drawable_create(
ffi.Pointer<spine_skeleton_data> skeletonData,
spine_skeleton_data skeletonData,
) {
return _spine_skeleton_drawable_create(
skeletonData,
@ -150,14 +147,12 @@ class SpineFlutterBindings {
}
late final _spine_skeleton_drawable_createPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<spine_skeleton_drawable> Function(
ffi.Pointer<spine_skeleton_data>)>>(
'spine_skeleton_drawable_create');
ffi.NativeFunction<
ffi.Pointer<spine_skeleton_drawable> Function(
spine_skeleton_data)>>('spine_skeleton_drawable_create');
late final _spine_skeleton_drawable_create =
_spine_skeleton_drawable_createPtr.asFunction<
ffi.Pointer<spine_skeleton_drawable> Function(
ffi.Pointer<spine_skeleton_data>)>();
ffi.Pointer<spine_skeleton_drawable> Function(spine_skeleton_data)>();
ffi.Pointer<spine_render_command> spine_skeleton_drawable_render(
ffi.Pointer<spine_skeleton_drawable> drawable,

View File

@ -61,42 +61,40 @@ FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) {
SpineExtension::free(atlas, __FILE__, __LINE__);
}
FFI_PLUGIN_EXPORT spine_skeleton_data_result *spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData) {
FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData) {
spine_skeleton_data_result result = { nullptr, nullptr };
Bone::setYDown(true);
if (!atlas) return nullptr;
if (!atlas->atlas) return nullptr;
if (!skeletonData) return nullptr;
if (!atlas) return result;
if (!atlas->atlas) return result;
if (!skeletonData) return result;
SkeletonJson json((Atlas*)atlas->atlas);
SkeletonData *data = json.readSkeletonData(skeletonData);
spine_skeleton_data_result *result = SpineExtension::calloc<spine_skeleton_data_result>(1, __FILE__, __LINE__);
result->skeletonData = data;
result.skeletonData = data;
if (!json.getError().isEmpty()) {
result->error = strdup(json.getError().buffer());
result.error = strdup(json.getError().buffer());
}
return result;
}
FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length) {
FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length) {
spine_skeleton_data_result result = { nullptr, nullptr };
Bone::setYDown(true);
if (!atlas) return nullptr;
if (!atlas->atlas) return nullptr;
if (!skeletonData) return nullptr;
if (length <= 0) return nullptr;
if (!atlas) return result;
if (!atlas->atlas) return result;
if (!skeletonData) return result;
if (length <= 0) return result;
SkeletonBinary binary((Atlas*)atlas->atlas);
SkeletonData *data = binary.readSkeletonData(skeletonData, length);
spine_skeleton_data_result *result = SpineExtension::calloc<spine_skeleton_data_result>(1, __FILE__, __LINE__);
result->skeletonData = data;
result.skeletonData = data;
if (!binary.getError().isEmpty()) {
result->error = strdup(binary.getError().buffer());
result.error = strdup(binary.getError().buffer());
}
return result;
}
FFI_PLUGIN_EXPORT void spine_skeleton_data_result_dispose(spine_skeleton_data_result *skeletonData) {
FFI_PLUGIN_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data skeletonData) {
if (!skeletonData) return;
if (skeletonData->skeletonData) delete (SkeletonData*)skeletonData->skeletonData;
if (skeletonData->error) free(skeletonData->error);
SpineExtension::free(skeletonData, __FILE__, __LINE__);
delete (SkeletonData*)skeletonData;
}
spine_render_command *spine_render_command_create(int32_t numVertices, int32_t numIndices, spine_blend_mode blendMode, int pageIndex) {
@ -122,7 +120,7 @@ void spine_render_command_dispose(spine_render_command *cmd) {
SpineExtension::free(cmd, __FILE__, __LINE__);
}
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData) {
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data skeletonData) {
spine_skeleton_drawable *drawable = SpineExtension::calloc<spine_skeleton_drawable>(1, __FILE__, __LINE__);
drawable->skeleton = new Skeleton((SkeletonData*)skeletonData);
AnimationState *state = new AnimationState(new AnimationStateData((SkeletonData*)skeletonData));

View File

@ -108,11 +108,11 @@ FFI_PLUGIN_EXPORT void spine_report_leaks();
FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData);
FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas);
FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData);
FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length);
FFI_PLUGIN_EXPORT void spine_skeleton_data_result_dispose(spine_skeleton_data_result *skeletonData);
FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData);
FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length);
FFI_PLUGIN_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data skeletonData);
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData);
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data skeletonData);
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);