Sfoglia il codice sorgente

[flutter] More refactoring, rendering vertices. Memory corruption.

Mario Zechner 3 anni fa
parent
commit
45d9c0ede8

+ 58 - 34
spine-flutter/example/lib/main.dart

@@ -7,55 +7,79 @@ void main() {
   runApp(const MyApp());
 }
 
-class MyApp extends StatefulWidget {
-  const MyApp({Key? key}) : super(key: key);
+class SpineWidget extends StatefulWidget {
+  final String skeletonFile;
+  final String atlasFile;
 
-  @override
-  _MyAppState createState() => _MyAppState();
-}
-
-class SpinePainter extends CustomPainter {
-  @override
-  void paint(Canvas canvas, Size size) {
-    var paint = Paint()
-      ..color = Colors.teal
-      ..strokeWidth = 5
-      ..strokeCap = StrokeCap.round;
-    canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), paint);
-  }
+  const SpineWidget(this.skeletonFile, this.atlasFile, {super.key});
 
   @override
-  bool shouldRepaint(CustomPainter oldDelegate) {
-    return false;
-  }
+  State<SpineWidget> createState() => _SpineWidgetState();
 }
 
-class SpineWidget extends StatelessWidget {
-  String skeletonFile;
-  String atlasFile;
-  late spine_flutter.SpineSkeletonDrawable skeletonDrawable;
+class _SpineWidgetState extends State<SpineWidget> {
+  spine_flutter.SpineSkeletonDrawable? skeletonDrawable;
 
-  SpineWidget(this.skeletonFile, this.atlasFile) {
-    loadSkeleton();
+  @override
+  void initState() {
+    super.initState();
+    loadSkeleton(widget.skeletonFile, widget.atlasFile);
   }
 
-  void loadSkeleton() async {
+  void loadSkeleton(String skeletonFile, String atlasFile) async {
     final atlas = await spine_flutter.SpineAtlas.fromAsset(rootBundle, atlasFile);
     final skeletonData = skeletonFile.endsWith(".json") ?
-      spine_flutter.SpineSkeletonData.fromJson(atlas, await rootBundle.loadString(skeletonFile))
-    : spine_flutter.SpineSkeletonData.fromBinary(atlas, await rootBundle.load(skeletonFile));
+    spine_flutter.SpineSkeletonData.fromJson(atlas, await rootBundle.loadString(skeletonFile))
+        : spine_flutter.SpineSkeletonData.fromBinary(atlas, await rootBundle.load(skeletonFile));
     skeletonDrawable = spine_flutter.SpineSkeletonDrawable(atlas, skeletonData);
-    skeletonDrawable.update(0.016);
-    print("Loaded skeleton");
+    skeletonDrawable?.update(0.016);
+    setState(() {});
   }
 
   @override
   Widget build(BuildContext context) {
-    return CustomPaint(
-        painter: SpinePainter(),
-        child: Container()
+    if (skeletonDrawable != null) {
+      print("Skeleton loaded, creating painter");
+      return CustomPaint(
+          painter: _SpinePainter(this),
+          child: Container()
       );
+    } else {
+      print("Skeleton not loaded yet");
+      return Container();
+    }
+  }
+}
+
+class _SpinePainter extends CustomPainter {
+  final _SpineWidgetState state;
+
+  _SpinePainter(this.state);
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    print("painting");
+    final drawable = state.skeletonDrawable;
+    if (drawable == null) return;
+    final commands = drawable.render();
+    canvas.translate(size.width / 2, size.height);
+    for (final cmd in commands) {
+      canvas.drawVertices(cmd.vertices, BlendMode.srcOut, Paint()..color = Colors.white); //drawable.atlas.atlasPagePaints[cmd.atlasPageIndex]);
+    }
+    canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), Paint()..color = Colors.blue);
   }
+
+  @override
+  bool shouldRepaint(CustomPainter oldDelegate) {
+    return false;
+  }
+}
+
+class MyApp extends StatefulWidget {
+  const MyApp({Key? key}) : super(key: key);
+
+  @override
+  _MyAppState createState() => _MyAppState();
 }
 
 class _MyAppState extends State<MyApp> {
@@ -68,8 +92,8 @@ class _MyAppState extends State<MyApp> {
   Widget build(BuildContext context) {
     const textStyle = TextStyle(fontSize: 25);
     const spacerSmall = SizedBox(height: 10);
-    return MaterialApp(
-      home: SpineWidget("assets/skeleton.json", "assets/skeleton.atlas")
+    return const MaterialApp(
+      home: SpineWidget("assets/spineboy-pro.json", "assets/spineboy.atlas")
     );
   }
 }

+ 27 - 11
spine-flutter/lib/spine_flutter.dart

@@ -1,12 +1,11 @@
-
 import 'dart:ffi';
 import 'dart:io';
 import 'dart:typed_data';
 import 'dart:ui';
 import 'package:ffi/ffi.dart';
+import 'package:flutter/rendering.dart';
 
 import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
 import 'spine_flutter_bindings_generated.dart';
 import 'package:path/path.dart' as Path;
 
@@ -16,8 +15,9 @@ int minorVersion() => _bindings.spine_minor_version();
 class SpineAtlas {
   Pointer<spine_atlas> _atlas;
   List<Image> _atlasPages;
+  List<Paint> atlasPagePaints;
 
-  SpineAtlas(this._atlas, this._atlasPages);
+  SpineAtlas(this._atlas, this._atlasPages, this.atlasPagePaints);
 
   static Future<SpineAtlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
     final atlasData = await assetBundle.loadString(atlasFileName);
@@ -33,13 +33,19 @@ class SpineAtlas {
 
     final atlasDir = Path.dirname(atlasFileName);
     List<Image> atlasPages = [];
+    List<Paint> atlasPagePaints = [];
     for (int i = 0; i < atlas.ref.numImagePaths; i++) {
       final Pointer<Utf8> atlasPageFile = atlas.ref.imagePaths[i].cast();
       final imagePath = Path.join(atlasDir, atlasPageFile.toDartString());
-      atlasPages.add(Image(image: AssetImage(imagePath)));
+      var imageData = await assetBundle.load(imagePath);
+      final Codec codec = await instantiateImageCodec(imageData.buffer.asUint8List());
+      final FrameInfo frameInfo = await codec.getNextFrame();
+      final Image image = frameInfo.image;
+      atlasPages.add(image);
+      atlasPagePaints.add(Paint()..shader = ImageShader(image, TileMode.clamp, TileMode.clamp, Matrix4.identity().storage));
     }
 
-    return SpineAtlas(atlas, atlasPages);
+    return SpineAtlas(atlas, atlasPages, atlasPagePaints);
   }
 }
 
@@ -92,7 +98,8 @@ class SpineSkeletonDrawable {
     Pointer<spine_render_command> nativeCmd = _bindings.spine_skeleton_drawable_render(_drawable);
     List<SpineRenderCommand> commands = [];
     while(nativeCmd.address != nullptr.address) {
-      commands.add(SpineRenderCommand(nativeCmd));
+      final atlasPage = atlas._atlasPages[nativeCmd.ref.atlasPage];
+      commands.add(SpineRenderCommand(nativeCmd, atlasPage.width.toDouble(), atlasPage.height.toDouble()));
       nativeCmd = nativeCmd.ref.next;
     }
     return commands;
@@ -103,19 +110,28 @@ class SpineRenderCommand {
   late Vertices vertices;
   late int atlasPageIndex;
 
-  SpineRenderCommand(Pointer<spine_render_command> nativeCmd) {
+  SpineRenderCommand(Pointer<spine_render_command> nativeCmd, double pageWidth, double pageHeight) {
     atlasPageIndex = nativeCmd.ref.atlasPage;
     int numVertices = nativeCmd.ref.numVertices;
     int numIndices = nativeCmd.ref.numIndices;
+    final positions = Float32List.fromList(nativeCmd.ref.positions.asTypedList(numVertices * 2));
+    final uvs = Float32List.fromList(nativeCmd.ref.uvs.asTypedList(numVertices * 2));
+    final colors = Int32List.fromList(nativeCmd.ref.colors.asTypedList(numVertices));
+    final indices = Uint16List.fromList(nativeCmd.ref.indices.asTypedList(numIndices));
+    for (int i = 0; i < numVertices * 2; i += 2) {
+      uvs[i] *= pageWidth;
+      uvs[i+1] *= pageHeight;
+    }
     // We pass the native data as views directly to Vertices.raw. According to the sources, the data
     // is copied, so it doesn't matter that we free up the underlying memory on the next
     // render call. See the implementation of Vertices.raw() here:
     // https://github.com/flutter/engine/blob/5c60785b802ad2c8b8899608d949342d5c624952/lib/ui/painting/vertices.cc#L21
     vertices = Vertices.raw(VertexMode.triangles,
-        nativeCmd.ref.positions.asTypedList(numVertices * 2),
-        textureCoordinates: nativeCmd.ref.uvs.asTypedList(numVertices * 2),
-        colors: nativeCmd.ref.colors.asTypedList(numVertices),
-        indices: nativeCmd.ref.indices.asTypedList(numIndices));
+        positions,
+        textureCoordinates: uvs,
+        colors: colors,
+        indices: indices
+    );
   }
 }
 

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

@@ -37,6 +37,7 @@ FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) {
 }
 
 FFI_PLUGIN_EXPORT spine_skeleton_data *spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData) {
+    Bone::setYDown(true);
     if (!atlas) return nullptr;
     if (!atlas->atlas) return nullptr;
     if (!skeletonData) return nullptr;
@@ -51,6 +52,7 @@ FFI_PLUGIN_EXPORT spine_skeleton_data *spine_skeleton_data_load_json(spine_atlas
 }
 
 FFI_PLUGIN_EXPORT spine_skeleton_data* spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length) {
+    Bone::setYDown(true);
     if (!atlas) return nullptr;
     if (!atlas->atlas) return nullptr;
     if (!skeletonData) return nullptr;
@@ -76,6 +78,7 @@ FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_
     spine_skeleton_drawable *drawable = SpineExtension::calloc<spine_skeleton_drawable>(1, __FILE__, __LINE__);
     drawable->skeleton = new Skeleton((SkeletonData*)skeletonData->skeletonData);
     drawable->animationState = new AnimationState(new AnimationStateData((SkeletonData*)skeletonData->skeletonData));
+    drawable->clipping = new SkeletonClipping();
     return drawable;
 }
 
@@ -83,6 +86,7 @@ FFI_PLUGIN_EXPORT void spine_skeleton_drawable_update(spine_skeleton_drawable *d
     if (!drawable) return;
     if (!drawable->skeleton) return;
     if (!drawable->animationState) return;
+    if (!drawable->clipping) return;
 
     Skeleton *skeleton = (Skeleton*)drawable->skeleton;
     AnimationState *animationState = (AnimationState*)drawable->animationState;
@@ -132,7 +136,7 @@ FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_ske
     quadIndices.add(3);
     quadIndices.add(0);
     Vector<float> worldVertices;
-    SkeletonClipping clipper;
+    SkeletonClipping &clipper = *(SkeletonClipping*)drawable->clipping;
     Skeleton *skeleton = (Skeleton*)drawable->skeleton;
     spine_render_command *lastCommand = nullptr;
 
@@ -238,6 +242,7 @@ FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *
     if (!drawable) return;
     if (drawable->skeleton) delete (Skeleton*)drawable->skeleton;
     if (drawable->animationState) delete (AnimationState*)drawable->animationState;
+    if (drawable->clipping) delete (SkeletonClipping*)drawable->clipping;
     while (drawable->renderCommand) {
         spine_render_command *cmd = drawable->renderCommand;
         drawable->renderCommand = cmd->next;

+ 1 - 0
spine-flutter/src/spine_flutter.h

@@ -67,6 +67,7 @@ typedef struct spine_render_command {
 typedef struct spine_skeleton_drawable {
     void *skeleton;
     void *animationState;
+    void *clipping;
     spine_render_command *renderCommand;
 } spine_skeleton_drawable;