浏览代码

[flutter] Fix batching, invoke bounds provider once before controller initalization, finish SkinsAndAnimationBounds

Mario Zechner 2 年之前
父节点
当前提交
0b84fdbb32

+ 6 - 0
examples/export/runtimes.sh

@@ -95,10 +95,16 @@ cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-flutter/example/assets/"
 cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-flutter/example/assets/"
 cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-flutter/example/assets/"
 cp -f ../spineboy/export/spineboy.png "$ROOT/spine-flutter/example/assets/"
+
 cp -f ../mix-and-match/export/mix-and-match-pro.skel "$ROOT/spine-flutter/example/assets/"
 cp -f ../mix-and-match/export/mix-and-match.atlas "$ROOT/spine-flutter/example/assets/"
 cp -f ../mix-and-match/export/mix-and-match.png "$ROOT/spine-flutter/example/assets/"
 
+cp -f ../dragon/export/dragon-ess.skel "$ROOT/spine-flutter/example/assets/"
+cp -f ../dragon/export/dragon.atlas "$ROOT/spine-flutter/example/assets/"
+cp -f ../dragon/export/dragon.png "$ROOT/spine-flutter/example/assets/"
+cp -f ../dragon/export/dragon_*.png "$ROOT/spine-flutter/example/assets/"
+
 echo "spine-godot"
 rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.atlas
 rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.png

二进制
spine-flutter/example/assets/dragon-ess.skel


+ 123 - 0
spine-flutter/example/assets/dragon.atlas

@@ -0,0 +1,123 @@
+dragon.png
+	size: 1024, 1024
+	filter: Linear, Linear
+back
+	bounds: 564, 534, 190, 185
+chest
+	bounds: 2, 645, 136, 122
+chin
+	bounds: 140, 619, 214, 146
+front-toe-a
+	bounds: 2, 862, 29, 50
+	rotate: 90
+front-toe-b
+	bounds: 467, 835, 56, 57
+	rotate: 90
+head
+	bounds: 756, 398, 296, 260
+	rotate: 90
+left-front-leg
+	bounds: 599, 834, 84, 57
+left-front-thigh
+	bounds: 782, 819, 84, 72
+left-rear-leg
+	bounds: 356, 558, 206, 177
+left-rear-thigh
+	bounds: 216, 767, 91, 149
+	rotate: 90
+left-wing01
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing02
+	bounds: 2, 2, 264, 589
+	rotate: 90
+right-front-leg
+	bounds: 113, 769, 101, 89
+right-front-thigh
+	bounds: 758, 709, 108, 108
+right-rear-leg
+	bounds: 640, 721, 116, 100
+right-rear-thigh
+	bounds: 367, 742, 91, 149
+	rotate: 90
+right-rear-toe
+	bounds: 2, 781, 109, 77
+tail01
+	bounds: 868, 696, 120, 153
+	rotate: 90
+tail02
+	bounds: 518, 737, 95, 120
+	rotate: 90
+tail03
+	bounds: 868, 818, 73, 92
+	rotate: 90
+tail04
+	bounds: 526, 835, 56, 71
+	rotate: 90
+tail05
+	bounds: 406, 839, 52, 59
+	rotate: 90
+tail06
+	bounds: 685, 823, 95, 68
+thiagobrayner
+	bounds: 54, 860, 350, 31
+
+dragon_2.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing03
+	bounds: 2, 534, 264, 589
+	rotate: 90
+left-wing04
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing05
+	bounds: 593, 209, 264, 589
+left-wing06
+	bounds: 2, 2, 264, 589
+	rotate: 90
+
+dragon_3.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing07
+	bounds: 2, 694, 264, 589
+	rotate: 90
+left-wing08
+	bounds: 2, 428, 264, 589
+	rotate: 90
+left-wing09
+	bounds: 593, 369, 264, 589
+right-wing01
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_4.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing02
+	bounds: 2, 369, 365, 643
+right-wing03
+	bounds: 369, 369, 365, 643
+right-wing04
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_5.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing05
+	bounds: 2, 369, 365, 643
+right-wing06
+	bounds: 369, 369, 365, 643
+right-wing07
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_6.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing08
+	bounds: 2, 2, 365, 643
+right-wing09
+	bounds: 369, 2, 365, 643

二进制
spine-flutter/example/assets/dragon.png


二进制
spine-flutter/example/assets/dragon_2.png


二进制
spine-flutter/example/assets/dragon_3.png


二进制
spine-flutter/example/assets/dragon_4.png


二进制
spine-flutter/example/assets/dragon_5.png


二进制
spine-flutter/example/assets/dragon_6.png


+ 1 - 1
spine-flutter/example/lib/dress_up.dart

@@ -107,7 +107,7 @@ class DressUpState extends State<DressUp> {
                       ),
                     ),
                     Expanded(
-                      child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]),)
+                      child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),)
                     )
                   ]
               )

+ 2 - 2
spine-flutter/example/lib/pause_play_animation.dart

@@ -16,7 +16,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
   void initState() {
     super.initState();
     controller = SpineWidgetController(onInitialized: (controller) {
-      controller.animationState.setAnimationByName(0, "walk", true);
+      controller.animationState.setAnimationByName(0, "flying", true);
     });
     isPlaying = true;
   }
@@ -33,7 +33,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
 
     return Scaffold(
       appBar: AppBar(title: const Text('Play/Pause')),
-      body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
+      body: SpineWidget.asset("assets/dragon.atlas", "assets/dragon-ess.skel", controller, boundsProvider: SkinAndAnimationBounds(animation: "flying"),),
       floatingActionButton: FloatingActionButton(
         onPressed: _togglePlay,
         child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),

+ 1 - 1
spine-flutter/example/lib/skins.dart

@@ -70,7 +70,7 @@ class SkinsState extends State<Skins> {
                   )
               ),
               Expanded(
-                  child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]))
+                  child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]))
               )
             ]
         )

+ 45 - 25
spine-flutter/lib/spine_widget.dart

@@ -97,33 +97,54 @@ class RawBounds extends BoundsProvider {
 }
 
 class SkinAndAnimationBounds extends BoundsProvider {
-  final List<String> _skins;
-  final String? _animation;
+  final List<String> skins;
+  final String? animation;
+  final double stepTime;
 
-  SkinAndAnimationBounds(this._skins, [this._animation]);
+  SkinAndAnimationBounds({List<String>? skins, this.animation, this.stepTime = 0.1}) :
+        skins = skins == null || skins.isEmpty? ["default"] : skins;
 
   @override
   Bounds computeBounds(SkeletonDrawable drawable) {
     var data = drawable.skeletonData;
-    var oldSkin = drawable.skeleton.getSkin();
     var customSkin = Skin("custom-skin");
-    for (var skinName in _skins) {
+    for (var skinName in skins) {
       var skin = data.findSkin(skinName);
       if (skin == null) continue;
       customSkin.addSkin(skin);
     }
     drawable.skeleton.setSkin(customSkin);
     drawable.skeleton.setToSetupPose();
-    var bounds = drawable.skeleton.getBounds();
-    customSkin.dispose();
 
-    if (oldSkin == null) {
-      drawable.skeleton.setSkinByName("");
+    final animation = data.findAnimation(this.animation!);
+    double minX = double.infinity;
+    double minY = double.infinity;
+    double maxX = double.negativeInfinity;
+    double maxY = double.negativeInfinity;
+    if (animation == null) {
+      final bounds = drawable.skeleton.getBounds();
+      minX = bounds.x;
+      minY = bounds.y;
+      maxX = minX + bounds.width;
+      maxY = minY + bounds.height;
     } else {
-      drawable.skeleton.setSkin(oldSkin);
+      drawable.animationState.setAnimation(0, animation, false);
+      final steps = max(animation.getDuration() / stepTime, 1.0).toInt();
+      for (int i = 0; i < steps; i++) {
+        drawable.update(i > 0 ? stepTime : 0);
+        final bounds = drawable.skeleton.getBounds();
+        minX = min(minX, bounds.x);
+        minY = min(minY, bounds.y);
+        maxX = max(maxX, minX + bounds.width);
+        maxY = max(maxY, minY + bounds.height);
+      }
     }
+    customSkin.dispose();
+    drawable.skeleton.setSkinByName("default");
+    drawable.animationState.clearTracks();
     drawable.skeleton.setToSetupPose();
-    return bounds;
+    drawable.update(0);
+    return Bounds(minX, minY, maxX - minX, maxY - minY);
   }
 }
 
@@ -189,6 +210,7 @@ class SpineWidget extends StatefulWidget {
 }
 
 class _SpineWidgetState extends State<SpineWidget> {
+  late Bounds _computedBounds;
 
   @override
   void initState() {
@@ -201,6 +223,7 @@ class _SpineWidgetState extends State<SpineWidget> {
   }
 
   void loadDrawable(SkeletonDrawable drawable) {
+    _computedBounds = widget._boundsProvider.computeBounds(drawable);
     widget._controller._initialize(drawable);
     setState(() {});
   }
@@ -224,7 +247,7 @@ class _SpineWidgetState extends State<SpineWidget> {
   @override
   Widget build(BuildContext context) {
     if (widget._controller._drawable != null) {
-      return _SpineRenderObjectWidget(widget._controller._drawable!, widget._controller, widget._fit, widget._alignment, widget._boundsProvider, widget._sizedByBounds);
+      return _SpineRenderObjectWidget(widget._controller._drawable!, widget._controller, widget._fit, widget._alignment, _computedBounds, widget._sizedByBounds);
     } else {
       return const SizedBox();
     }
@@ -242,14 +265,14 @@ class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
   final SpineWidgetController _controller;
   final BoxFit _fit;
   final Alignment _alignment;
-  final BoundsProvider _boundsProvider;
+  final Bounds _bounds;
   final bool _sizedByBounds;
 
-  const _SpineRenderObjectWidget(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._boundsProvider, this._sizedByBounds);
+  const _SpineRenderObjectWidget(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
 
   @override
   RenderObject createRenderObject(BuildContext context) {
-    return _SpineRenderObject(_skeletonDrawable, _controller, _fit, _alignment, _boundsProvider, _sizedByBounds);
+    return _SpineRenderObject(_skeletonDrawable, _controller, _fit, _alignment, _bounds, _sizedByBounds);
   }
 
   @override
@@ -257,7 +280,7 @@ class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
     renderObject.skeletonDrawable = _skeletonDrawable;
     renderObject.fit = _fit;
     renderObject.alignment = _alignment;
-    renderObject.boundsProvider = _boundsProvider;
+    renderObject.bounds = _bounds;
     renderObject.sizedByBounds = _sizedByBounds;
   }
 }
@@ -269,16 +292,14 @@ class _SpineRenderObject extends RenderBox {
   final Stopwatch _stopwatch = Stopwatch();
   BoxFit _fit;
   Alignment _alignment;
-  BoundsProvider _boundsProvider;
-  bool _sizedByBounds;
   Bounds _bounds;
-  _SpineRenderObject(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._boundsProvider, this._sizedByBounds): _bounds = _boundsProvider.computeBounds(_skeletonDrawable);
+  bool _sizedByBounds;
+  _SpineRenderObject(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
 
   set skeletonDrawable(SkeletonDrawable skeletonDrawable) {
     if (_skeletonDrawable == skeletonDrawable) return;
 
     _skeletonDrawable = skeletonDrawable;
-    _bounds = _boundsProvider.computeBounds(_skeletonDrawable);
     markNeedsLayout();
     markNeedsPaint();
   }
@@ -303,12 +324,11 @@ class _SpineRenderObject extends RenderBox {
     }
   }
 
-  BoundsProvider get boundsProvider => _boundsProvider;
+  Bounds get bounds => _bounds;
 
-  set boundsProvider(BoundsProvider boundsProvider) {
-    if (boundsProvider != _boundsProvider) {
-      _boundsProvider = boundsProvider;
-      _bounds = boundsProvider.computeBounds(_skeletonDrawable);
+  set bounds(Bounds bounds) {
+    if (bounds != _bounds) {
+      _bounds = bounds;
       markNeedsLayout();
       markNeedsPaint();
     }

+ 60 - 27
spine-flutter/src/main.cpp

@@ -3,48 +3,81 @@
 
 using namespace spine;
 
+String blendMode(spine_blend_mode mode) {
+    switch(mode) {
+        case SPINE_BLEND_MODE_NORMAL:
+            return "normal";
+        case SPINE_BLEND_MODE_ADDITIVE:
+            return "additiev";
+        case SPINE_BLEND_MODE_MULTIPLY:
+            return "multiply";
+        case SPINE_BLEND_MODE_SCREEN:
+            return "screen";
+    }
+}
+
 int main(int argc, char** argv) {
     int atlasLength = 0;
-    void* atlasData = SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/spineboy.atlas", &atlasLength);
+    void* atlasData = SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/dragon.atlas", &atlasLength);
     uint8_t* cstringAtlas = SpineExtension::calloc<uint8_t>(atlasLength + 1, __FILE__, __LINE__);
     memcpy(cstringAtlas, atlasData, atlasLength);
     int dataLength = 0;
-    uint8_t* data = (uint8_t*)SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/spineboy-pro.skel", &dataLength);
+    uint8_t* data = (uint8_t*)SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/dragon-ess.skel", &dataLength);
 
     spine_atlas atlas = spine_atlas_load((const utf8*)cstringAtlas);
+    Vector<String> atlasPages;
+    for (int i = 0, n = spine_atlas_get_num_image_paths(atlas); i < n; i++) {
+        atlasPages.add(spine_atlas_get_image_path(atlas, i));
+    }
+
     spine_skeleton_data_result result = spine_skeleton_data_load_binary(atlas, data, dataLength);
     spine_skeleton_drawable drawable = spine_skeleton_drawable_create(spine_skeleton_data_result_get_data(result));
     spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
     spine_skeleton_update_world_transform(skeleton);
     spine_render_command cmd = spine_skeleton_drawable_render(drawable);
-    int numVertices = spine_render_command_get_num_vertices(cmd);
-    int numIndices = spine_render_command_get_num_indices(cmd);
-    float* positions = spine_render_command_get_positions(cmd);
-    float* uvs = spine_render_command_get_uvs(cmd);
-    int32_t *colors = spine_render_command_get_colors(cmd);
-    uint16_t *indices = spine_render_command_get_indices(cmd);
-    String str;
-    str.append(numVertices);
-    str.append("\n");
-    str.append(numIndices);
-    str.append("\n");
-    for (int i = 0; i < numVertices * 2; i++) {
-        str.append(positions[i]);
+
+    int batchId = 0;
+    while(cmd) {
+        int numVertices = spine_render_command_get_num_vertices(cmd);
+        int numIndices = spine_render_command_get_num_indices(cmd);
+        float *positions = spine_render_command_get_positions(cmd);
+        float *uvs = spine_render_command_get_uvs(cmd);
+        int32_t *colors = spine_render_command_get_colors(cmd);
+        uint16_t *indices = spine_render_command_get_indices(cmd);
+        String str;
+        int atlasPage = spine_render_command_get_atlas_page(cmd);
+        str.append(atlasPages[atlasPage]);
         str.append("\n");
-    }
-    for (int i = 0; i < numVertices * 2; i++) {
-        str.append(uvs[i]);
+        str.append(blendMode(spine_render_command_get_blend_mode(cmd)));
         str.append("\n");
-    }
-    for (int i = 0; i < numVertices; i++) {
-        str.append(colors[i]);
+        str.append(numVertices);
         str.append("\n");
-    }
-    for (int i = 0; i < numIndices; i++) {
-        str.append(indices[i]);
+        str.append(numIndices);
         str.append("\n");
+        for (int i = 0; i < numVertices * 2; i++) {
+            str.append(positions[i]);
+            str.append("\n");
+        }
+        for (int i = 0; i < numVertices * 2; i++) {
+            str.append(uvs[i]);
+            str.append("\n");
+        }
+        for (int i = 0; i < numVertices; i++) {
+            str.append(colors[i]);
+            str.append("\n");
+        }
+        for (int i = 0; i < numIndices; i++) {
+            str.append(indices[i]);
+            str.append("\n");
+        }
+        String outputFile = "";
+        outputFile.append("/Users/badlogic/Desktop/dragon-");
+        outputFile.append(batchId);
+        outputFile.append(".mesh");
+        FILE *file = fopen(outputFile.buffer(), "w");
+        fwrite(str.buffer(), str.length(), 1, file);
+        fclose(file);
+        batchId++;
+        cmd = spine_render_command_get_next(cmd);
     }
-    FILE *file = fopen("/Users/badlogic/Desktop/spineboy.mesh", "w");
-    fwrite(str.buffer(), str.length(), 1, file);
-    fclose(file);
 }

+ 1 - 1
spine-flutter/src/spine_flutter.cpp

@@ -657,7 +657,7 @@ void spine_skeleton_drawable_dispose(spine_skeleton_drawable drawable) {
 }
 
 static _spine_render_command *batch_sub_commands(BlockAllocator &allocator, Vector<_spine_render_command*> &commands, int first, int last, int numVertices, int numIndices) {
-	_spine_render_command *batched = spine_render_command_create(allocator, numVertices, numIndices, commands[0]->blendMode, commands[0]->atlasPage);
+	_spine_render_command *batched = spine_render_command_create(allocator, numVertices, numIndices, commands[first]->blendMode, commands[first]->atlasPage);
 	float *positions = batched->positions;
 	float *uvs = batched->uvs;
 	int32_t *colors = batched->colors;