From dbc2e164f06e86906f1e9dc690caa6668241b9e6 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 24 Nov 2022 07:46:27 +0100 Subject: [PATCH] [flutter] Fix retention of drawable in SpineWidgetState, beginnings of debug renderer. --- .../example/lib/debug_rendering.dart | 26 ++++++++++++++++++ spine-flutter/example/lib/main.dart | 13 +++++++++ spine-flutter/lib/spine_flutter.dart | 27 +++++++++++++++---- spine-flutter/lib/spine_widget.dart | 15 +++++++---- 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 spine-flutter/example/lib/debug_rendering.dart diff --git a/spine-flutter/example/lib/debug_rendering.dart b/spine-flutter/example/lib/debug_rendering.dart new file mode 100644 index 000000000..2f2458089 --- /dev/null +++ b/spine-flutter/example/lib/debug_rendering.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:esotericsoftware_spine_flutter/spine_flutter.dart'; + +class DebugRendering extends StatelessWidget { + const DebugRendering({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + reportLeaks(); + + const debugRenderer = DebugRenderer(); + final controller = SpineWidgetController( + onInitialized: (controller) { + controller.animationState.setAnimationByName(0, "walk", true); + }, + onAfterPaint: (controller, canvas, commands) { + debugRenderer.render(controller.drawable, canvas, commands); + } + ); + + return Scaffold( + appBar: AppBar(title: const Text('Debug Renderer')), + body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller), + ); + } +} \ No newline at end of file diff --git a/spine-flutter/example/lib/main.dart b/spine-flutter/example/lib/main.dart index c62120172..37863dbfd 100644 --- a/spine-flutter/example/lib/main.dart +++ b/spine-flutter/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:esotericsoftware_spine_flutter/spine_flutter.dart'; +import 'package:spine_flutter_example/debug_rendering.dart'; import 'flame_example.dart'; import 'simple_animation.dart'; @@ -57,6 +58,18 @@ class ExampleSelector extends StatelessWidget { }, ), spacer, + ElevatedButton( + child: const Text('Debug Rendering'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => DebugRendering(), + ), + ); + }, + ), + spacer, ElevatedButton( child: const Text('Skins'), onPressed: () { diff --git a/spine-flutter/lib/spine_flutter.dart b/spine-flutter/lib/spine_flutter.dart index 32a268ea2..81718a0de 100644 --- a/spine-flutter/lib/spine_flutter.dart +++ b/spine-flutter/lib/spine_flutter.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:ui'; +import 'package:flutter/material.dart' as material; import 'package:flutter/rendering.dart' as rendering; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; @@ -3242,7 +3243,6 @@ class AnimationState { } } -// FIXME add callbacks for update, apply and updateWorldTransform. Pass through SpineWidgetController class SkeletonDrawable { final Atlas atlas; final SkeletonData skeletonData; @@ -3321,19 +3321,23 @@ class SkeletonDrawable { class RenderCommand { late final Vertices vertices; late final int atlasPageIndex; + late final Float32List positions; + late final Float32List uvs; + late final Int32List colors; + late final Uint16List indices; RenderCommand._(spine_render_command nativeCmd, double pageWidth, double pageHeight) { atlasPageIndex = _bindings.spine_render_command_get_atlas_page(nativeCmd); int numVertices = _bindings.spine_render_command_get_num_vertices(nativeCmd); int numIndices = _bindings.spine_render_command_get_num_indices(nativeCmd); - final positions = _bindings.spine_render_command_get_positions(nativeCmd).asTypedList(numVertices * 2); - final uvs = _bindings.spine_render_command_get_uvs(nativeCmd).asTypedList(numVertices * 2); + positions = _bindings.spine_render_command_get_positions(nativeCmd).asTypedList(numVertices * 2); + uvs = _bindings.spine_render_command_get_uvs(nativeCmd).asTypedList(numVertices * 2); for (int i = 0; i < numVertices * 2; i += 2) { uvs[i] *= pageWidth; uvs[i + 1] *= pageHeight; } - final colors = _bindings.spine_render_command_get_colors(nativeCmd).asTypedList(numVertices); - final indices = _bindings.spine_render_command_get_indices(nativeCmd).asTypedList(numIndices); + colors = _bindings.spine_render_command_get_colors(nativeCmd).asTypedList(numVertices); + indices = _bindings.spine_render_command_get_indices(nativeCmd).asTypedList(numIndices); if (!kIsWeb) { // We pass the native data as views directly to Vertices.raw. According to the sources, the data @@ -3356,4 +3360,17 @@ class RenderCommand { indices: indicesCopy); } } +} + +class DebugRenderer { + const DebugRenderer(); + + void render(SkeletonDrawable drawable, Canvas canvas, List commands) { + final bonePaint = Paint() + ..color = material.Colors.blue + ..style = PaintingStyle.fill; + for (final bone in drawable.skeleton.getBones()) { + canvas.drawRect(Rect.fromCenter(center: Offset(bone.getWorldX(), bone.getWorldY()), width: 5, height: 5), bonePaint); + } + } } \ No newline at end of file diff --git a/spine-flutter/lib/spine_widget.dart b/spine-flutter/lib/spine_widget.dart index 1ac5b0654..cffbbb077 100644 --- a/spine-flutter/lib/spine_widget.dart +++ b/spine-flutter/lib/spine_widget.dart @@ -14,7 +14,7 @@ class SpineWidgetController { final void Function(SpineWidgetController controller)? onBeforeUpdateWorldTransforms; final void Function(SpineWidgetController controller)? onAfterUpdateWorldTransforms; final void Function(SpineWidgetController controller, Canvas canvas)? onBeforePaint; - final void Function(SpineWidgetController controller, Canvas canvas)? onAfterPaint; + final void Function(SpineWidgetController controller, Canvas canvas, List commands)? onAfterPaint; SpineWidgetController({this.onInitialized, this.onBeforeUpdateWorldTransforms, this.onAfterUpdateWorldTransforms, this.onBeforePaint, this.onAfterPaint}); @@ -211,6 +211,7 @@ class SpineWidget extends StatefulWidget { class _SpineWidgetState extends State { late Bounds _computedBounds; + SkeletonDrawable? _drawable; @override void initState() { @@ -223,6 +224,7 @@ class _SpineWidgetState extends State { } void loadDrawable(SkeletonDrawable drawable) { + _drawable = drawable; _computedBounds = widget._boundsProvider.computeBounds(drawable); widget._controller._initialize(drawable); setState(() {}); @@ -246,8 +248,8 @@ class _SpineWidgetState extends State { @override Widget build(BuildContext context) { - if (widget._controller._drawable != null) { - return _SpineRenderObjectWidget(widget._controller._drawable!, widget._controller, widget._fit, widget._alignment, _computedBounds, widget._sizedByBounds); + if (_drawable != null) { + return _SpineRenderObjectWidget(_drawable!, widget._controller, widget._fit, widget._alignment, _computedBounds, widget._sizedByBounds); } else { return const SizedBox(); } @@ -461,8 +463,11 @@ class _SpineRenderObject extends RenderBox { _setCanvasTransform(canvas, offset); _controller.onBeforePaint?.call(_controller, canvas); - _skeletonDrawable.renderToCanvas(canvas); - _controller.onAfterPaint?.call(_controller, canvas); + var commands = _skeletonDrawable.render(); + for (final cmd in commands) { + canvas.drawVertices(cmd.vertices, rendering.BlendMode.modulate, _skeletonDrawable.atlas.atlasPagePaints[cmd.atlasPageIndex]); + } + _controller.onAfterPaint?.call(_controller, canvas, commands); canvas.restore(); SchedulerBinding.instance.scheduleFrameCallback(_beginFrame);