From 92ed5f6fa705c1ea74486b7b01ad988591e70918 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 22 Sep 2023 17:30:00 +0200 Subject: [PATCH 01/20] [flutter] Fix path composition when loading from assets. Requires forward slash, path.join() does not work on Windows. --- spine-flutter/example/assets/dragon-ess.json | 1079 +++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 2 +- spine-flutter/example/lib/flame_example.dart | 32 +- spine-flutter/example/lib/main.dart | 14 +- .../example/lib/pause_play_animation.dart | 2 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- spine-flutter/example/pubspec.lock | 18 +- spine-flutter/lib/spine_flutter.dart | 2 +- 8 files changed, 1140 insertions(+), 11 deletions(-) create mode 100644 spine-flutter/example/assets/dragon-ess.json diff --git a/spine-flutter/example/assets/dragon-ess.json b/spine-flutter/example/assets/dragon-ess.json new file mode 100644 index 000000000..274ef6306 --- /dev/null +++ b/spine-flutter/example/assets/dragon-ess.json @@ -0,0 +1,1079 @@ +{ +"skeleton": { + "hash": "aPoA1GjXkVI", + "spine": "4.1.17", + "x": -366.31, + "y": -327.81, + "width": 660.39, + "height": 643, + "images": "./images/", + "audio": "././" +}, +"bones": [ + { "name": "root", "y": -176.12 }, + { "name": "center", "parent": "root", "y": 176.12, "color": "ffe300ff" }, + { + "name": "back", + "parent": "center", + "length": 115.38, + "rotation": 151.83, + "x": 16.04, + "y": 27.94, + "color": "ffe400ff" + }, + { + "name": "chest", + "parent": "center", + "length": 31.24, + "rotation": 161.7, + "x": 52.53, + "y": 15.35, + "color": "ffe400ff" + }, + { + "name": "neck", + "parent": "center", + "length": 41.37, + "rotation": 39.06, + "x": 64.76, + "y": 11.98, + "color": "ffe400ff" + }, + { + "name": "chin", + "parent": "neck", + "length": 153.16, + "rotation": -69.07, + "x": 64.63, + "y": -6.99, + "color": "ffe400ff" + }, + { + "name": "head", + "parent": "neck", + "length": 188.84, + "rotation": 8.07, + "x": 69.96, + "y": 2.5, + "color": "ffe400ff" + }, + { + "name": "left-front-thigh", + "parent": "chest", + "length": 67.42, + "rotation": 138.94, + "x": -45.59, + "y": 7.93, + "color": "ff0000ff" + }, + { + "name": "left-front-leg", + "parent": "left-front-thigh", + "length": 51.58, + "rotation": 43.36, + "x": 67.42, + "y": 0.03, + "color": "ff0000ff" + }, + { + "name": "left-front-toe1", + "parent": "left-front-leg", + "length": 51.45, + "rotation": -98.01, + "x": 45.54, + "y": 2.43, + "color": "ff0000ff" + }, + { + "name": "left-front-toe2", + "parent": "left-front-leg", + "length": 61.98, + "rotation": -55.26, + "x": 51.58, + "y": -0.13, + "color": "ff0000ff" + }, + { + "name": "left-front-toe3", + "parent": "left-front-leg", + "length": 45.65, + "rotation": -11.14, + "x": 54.19, + "y": 0.6, + "scaleX": 1.135, + "color": "ff0000ff" + }, + { + "name": "left-front-toe4", + "parent": "left-front-leg", + "length": 53.47, + "rotation": 19.43, + "x": 50.61, + "y": 7.09, + "scaleX": 1.135, + "color": "ff0000ff" + }, + { + "name": "right-rear-thigh", + "parent": "back", + "length": 123.47, + "rotation": 104.88, + "x": 65.31, + "y": 59.89, + "color": "29ff00ff" + }, + { + "name": "left-rear-thigh", + "parent": "right-rear-thigh", + "length": 88.06, + "rotation": 28.35, + "x": -8.59, + "y": 30.19, + "color": "ff0000ff" + }, + { + "name": "left-rear-leg", + "parent": "left-rear-thigh", + "length": 103.74, + "rotation": -122.41, + "x": 96.04, + "y": -0.97, + "color": "ff0000ff" + }, + { + "name": "left-wing", + "parent": "chest", + "length": 301.12, + "rotation": -75.51, + "x": -7.25, + "y": -24.66, + "color": "ff0000ff" + }, + { + "name": "right-front-thigh", + "parent": "chest", + "length": 81.64, + "rotation": 67.97, + "x": -10.89, + "y": 28.25, + "color": "29ff00ff" + }, + { + "name": "right-front-leg", + "parent": "right-front-thigh", + "length": 66.53, + "rotation": 92.7, + "x": 83.05, + "y": -0.31, + "color": "29ff00ff" + }, + { + "name": "right-front-toe1", + "parent": "right-front-leg", + "length": 46.66, + "rotation": 8.59, + "x": 70.03, + "y": 5.31, + "color": "29ff00ff" + }, + { + "name": "right-front-toe2", + "parent": "right-front-leg", + "length": 53.67, + "rotation": -35.02, + "x": 66.53, + "y": 0.34, + "color": "29ff00ff" + }, + { + "name": "right-front-toe3", + "parent": "right-front-leg", + "length": 58.39, + "rotation": -74.67, + "x": 62.1, + "y": -0.79, + "color": "29ff00ff" + }, + { + "name": "right-rear-leg", + "parent": "right-rear-thigh", + "length": 91.06, + "rotation": -129.04, + "x": 123.47, + "y": -0.27, + "color": "29ff00ff" + }, + { + "name": "right-rear-toe1", + "parent": "right-rear-leg", + "length": 95, + "rotation": 141.98, + "x": 90.07, + "y": 2.12, + "color": "29ff00ff" + }, + { + "name": "right-rear-toe2", + "parent": "right-rear-leg", + "length": 99.29, + "rotation": 125.32, + "x": 89.6, + "y": 1.52, + "color": "29ff00ff" + }, + { + "name": "right-rear-toe3", + "parent": "right-rear-leg", + "length": 103.46, + "rotation": 112.27, + "x": 91.06, + "y": -0.35, + "color": "29ff00ff" + }, + { + "name": "right-wing", + "parent": "head", + "length": 359.5, + "rotation": 83.21, + "x": -74.68, + "y": 20.91, + "color": "29ff00ff" + }, + { + "name": "tail1", + "parent": "back", + "length": 65.65, + "rotation": 44.32, + "x": 115.38, + "y": -0.2, + "color": "ffe400ff" + }, + { + "name": "tail2", + "parent": "tail1", + "length": 54.5, + "rotation": 12, + "x": 65.65, + "y": 0.23, + "color": "ffe400ff" + }, + { + "name": "tail3", + "parent": "tail2", + "length": 41.78, + "rotation": 1.8, + "x": 54.5, + "y": 0.37, + "color": "ffe400ff" + }, + { + "name": "tail4", + "parent": "tail3", + "length": 34.19, + "rotation": -1.8, + "x": 41.78, + "y": 0.16, + "color": "ffe400ff" + }, + { + "name": "tail5", + "parent": "tail4", + "length": 32.33, + "rotation": -3.15, + "x": 34.19, + "y": -0.19, + "color": "ffe400ff" + }, + { + "name": "tail6", + "parent": "tail5", + "length": 80.08, + "rotation": -29.55, + "x": 32.33, + "y": -0.23, + "color": "ffe400ff" + } +], +"slots": [ + { "name": "left-rear-leg", "bone": "left-rear-leg", "attachment": "left-rear-leg" }, + { "name": "left-rear-thigh", "bone": "left-rear-thigh", "attachment": "left-rear-thigh" }, + { "name": "left-wing", "bone": "left-wing", "attachment": "left-wing" }, + { "name": "tail6", "bone": "tail6", "attachment": "tail06" }, + { "name": "tail5", "bone": "tail5", "attachment": "tail05" }, + { "name": "tail4", "bone": "tail4", "attachment": "tail04" }, + { "name": "tail3", "bone": "tail3", "attachment": "tail03" }, + { "name": "tail2", "bone": "tail2", "attachment": "tail02" }, + { "name": "tail1", "bone": "tail1", "attachment": "tail01" }, + { "name": "back", "bone": "back", "attachment": "back" }, + { "name": "left-front-thigh", "bone": "left-front-thigh", "attachment": "left-front-thigh" }, + { "name": "left-front-leg", "bone": "left-front-leg", "attachment": "left-front-leg" }, + { "name": "left-front-toe1", "bone": "left-front-toe1", "attachment": "front-toe-a" }, + { "name": "left-front-toe4", "bone": "left-front-toe4", "attachment": "front-toe-b" }, + { "name": "left-front-toe3", "bone": "left-front-toe3", "attachment": "front-toe-b" }, + { "name": "left-front-toe2", "bone": "left-front-toe2", "attachment": "front-toe-b" }, + { "name": "chest", "bone": "chest", "attachment": "chest" }, + { "name": "right-rear-toe1", "bone": "right-rear-toe1", "attachment": "right-rear-toe" }, + { "name": "right-rear-toe2", "bone": "right-rear-toe2", "attachment": "right-rear-toe" }, + { "name": "right-rear-toe3", "bone": "right-rear-toe3", "attachment": "right-rear-toe" }, + { "name": "right-rear-leg", "bone": "right-rear-leg", "attachment": "right-rear-leg" }, + { "name": "right-rear-thigh", "bone": "right-rear-thigh", "attachment": "right-rear-thigh" }, + { "name": "right-front-toe1", "bone": "right-front-toe1", "attachment": "front-toe-b" }, + { "name": "right-front-thigh", "bone": "right-front-thigh", "attachment": "right-front-thigh" }, + { "name": "right-front-leg", "bone": "right-front-leg", "attachment": "right-front-leg" }, + { "name": "right-front-toe2", "bone": "right-front-toe2", "attachment": "front-toe-b" }, + { "name": "right-front-toe3", "bone": "right-front-toe3", "attachment": "front-toe-b" }, + { "name": "chin", "bone": "chin", "attachment": "chin" }, + { "name": "right-wing", "bone": "right-wing", "attachment": "right-wing" }, + { "name": "head", "bone": "head", "attachment": "head" }, + { "name": "thiagobrayner", "bone": "root", "attachment": "thiagobrayner" } +], +"skins": [ + { + "name": "default", + "attachments": { + "back": { + "back": { "x": 35.85, "y": 19.99, "rotation": -151.83, "width": 190, "height": 185 } + }, + "chest": { + "chest": { "x": -14.6, "y": 24.79, "rotation": -161.7, "width": 136, "height": 122 } + }, + "chin": { + "chin": { "x": 66.55, "y": 7.32, "rotation": 30.01, "width": 214, "height": 146 } + }, + "head": { + "head": { "x": 76.69, "y": 32.21, "rotation": -47.13, "width": 296, "height": 260 } + }, + "left-front-leg": { + "left-front-leg": { "x": 14.69, "y": 0.49, "rotation": 16, "width": 84, "height": 57 } + }, + "left-front-thigh": { + "left-front-thigh": { "x": 27.66, "y": -11.59, "rotation": 58.66, "width": 84, "height": 72 } + }, + "left-front-toe1": { + "front-toe-a": { "x": 31.93, "y": 0.61, "rotation": 109.56, "width": 29, "height": 50 } + }, + "left-front-toe2": { + "front-toe-b": { "x": 26.84, "y": -4.95, "rotation": 109.51, "width": 56, "height": 57 } + }, + "left-front-toe3": { + "front-toe-b": { + "x": 18.22, + "y": -7.22, + "scaleX": 0.8811, + "scaleY": 0.9409, + "rotation": 99.71, + "width": 56, + "height": 57 + } + }, + "left-front-toe4": { + "front-toe-b": { "x": 23.21, "y": -11.69, "scaleX": 0.8811, "rotation": 79.89, "width": 56, "height": 57 } + }, + "left-rear-leg": { + "left-rear-leg": { "x": 67.29, "y": 12.63, "rotation": -162.65, "width": 206, "height": 177 } + }, + "left-rear-thigh": { + "left-rear-thigh": { "x": 56.03, "y": 27.39, "rotation": 74.94, "width": 91, "height": 149 } + }, + "left-wing": { + "left-wing": { + "x": -36.32, + "y": -44.53, + "rotation": -83.7, + "width": 264, + "height": 589, + "sequence": { "count": 9, "digits": 2 } + } + }, + "right-front-leg": { + "right-front-leg": { "x": 17.8, "y": 4.23, "rotation": 37.63, "width": 101, "height": 89 } + }, + "right-front-thigh": { + "right-front-thigh": { "x": 35.29, "y": 2.11, "rotation": 130.33, "width": 108, "height": 108 } + }, + "right-front-toe1": { + "front-toe-b": { "x": 24.5, "y": -2.61, "rotation": 104.18, "width": 56, "height": 57 } + }, + "right-front-toe2": { + "front-toe-b": { "x": 26.39, "y": 1.17, "rotation": 104.58, "width": 56, "height": 57 } + }, + "right-front-toe3": { + "front-toe-b": { "x": 30.67, "y": -0.07, "rotation": 112.3, "width": 56, "height": 57 } + }, + "right-rear-leg": { + "right-rear-leg": { "x": 60.88, "y": -5.73, "rotation": -127.67, "width": 116, "height": 100 } + }, + "right-rear-thigh": { + "right-rear-thigh": { "x": 53.25, "y": 12.58, "rotation": 103.29, "width": 91, "height": 149 } + }, + "right-rear-toe1": { + "right-rear-toe": { "x": 54.76, "y": -5.72, "rotation": 134.79, "width": 109, "height": 77 } + }, + "right-rear-toe2": { + "right-rear-toe": { "x": 57.03, "y": -7.23, "rotation": 134.43, "width": 109, "height": 77 } + }, + "right-rear-toe3": { + "right-rear-toe": { "x": 47.46, "y": -7.64, "rotation": 134.34, "width": 109, "height": 77 } + }, + "right-wing": { + "right-wing": { + "x": 35.09, + "y": 78.11, + "rotation": -130.34, + "width": 365, + "height": 643, + "sequence": { "count": 9, "digits": 2 } + } + }, + "tail1": { + "tail01": { "x": 22.6, "y": -4.5, "rotation": 163.85, "width": 120, "height": 153 } + }, + "tail2": { + "tail02": { "x": 18.12, "y": -1.75, "rotation": 151.85, "width": 95, "height": 120 } + }, + "tail3": { + "tail03": { "x": 16.94, "y": -2.01, "rotation": 150.04, "width": 73, "height": 92 } + }, + "tail4": { + "tail04": { "x": 15.35, "y": -2.18, "rotation": 151.85, "width": 56, "height": 71 } + }, + "tail5": { + "tail05": { "x": 15.06, "y": -3.57, "rotation": 155, "width": 52, "height": 59 } + }, + "tail6": { + "tail06": { "x": 28.02, "y": -16.83, "rotation": -175.45, "width": 95, "height": 68 } + }, + "thiagobrayner": { + "thiagobrayner": { "y": -95, "width": 350, "height": 31 } + } + } + } +], +"animations": { + "flying": { + "bones": { + "back": { + "rotate": [ + { + "curve": [ 0.042, 2.86, 0.143, 10.41 ] + }, + { + "time": 0.1667, + "value": 11.59, + "curve": [ 0.245, 15.58, 0.383, 22.41 ] + }, + { + "time": 0.5, + "value": 22.4, + "curve": [ 0.593, 22.39, 0.75, -3.18 ] + }, + { + "time": 0.8333, + "value": -3.18, + "curve": [ 0.875, -3.18, 0.942, -2.19 ] + }, + { "time": 1 } + ] + }, + "neck": { + "rotate": [ + { + "curve": [ 0.092, -0.1, 0.304, -21.22 ] + }, + { + "time": 0.3333, + "value": -23.16, + "curve": [ 0.386, -26.7, 0.442, -31.89 ] + }, + { + "time": 0.5333, + "value": -31.93, + "curve": [ 0.644, -31.97, 0.888, -0.1 ] + }, + { "time": 1 } + ] + }, + "right-rear-leg": { + "rotate": [ + { + "curve": [ 0.054, -7.16, 0.227, -10.75 ] + }, + { + "time": 0.3333, + "value": -10.75, + "curve": [ 0.417, -10.75, 0.583, 23.32 ] + }, + { + "time": 0.6667, + "value": 23.32, + "curve": [ 0.792, 23.32, 0.881, 13.94 ] + }, + { "time": 1 } + ] + }, + "right-rear-toe3": { + "rotate": [ + { + "value": -7.31, + "curve": [ 0.051, -3.08, 0.097, 0 ] + }, + { + "time": 0.1333, + "curve": [ 0.258, 0, 0.508, -36.06 ] + }, + { + "time": 0.6333, + "value": -36.06, + "curve": [ 0.723, -36.06, 0.875, -17.83 ] + }, + { "time": 1, "value": -7.31 } + ] + }, + "right-rear-toe2": { + "rotate": [ + { + "value": -1.41, + "curve": [ 0.025, -0.6, 0.047, 0 ] + }, + { + "time": 0.0667, + "curve": [ 0.192, 0, 0.442, -20.32 ] + }, + { + "time": 0.5667, + "value": -20.32, + "curve": [ 0.673, -20.32, 0.868, -5.88 ] + }, + { "time": 1, "value": -1.41 } + ] + }, + "right-rear-toe1": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, -18.71 ] + }, + { + "time": 0.5, + "value": -18.71, + "curve": [ 0.625, -18.71, 0.875, 0 ] + }, + { "time": 1 } + ] + }, + "head": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, 1.04 ] + }, + { + "time": 0.5, + "value": 1.04, + "curve": [ 0.625, 1.04, 0.875, 0 ] + }, + { "time": 1 } + ] + }, + "chin": { + "rotate": [ + { + "curve": [ 0.136, -10.78, 0.217, -12.01 ] + }, + { + "time": 0.3333, + "value": -11.71, + "curve": [ 0.443, -11.42, 0.508, 21.91 ] + }, + { + "time": 0.6667, + "value": 22.01, + "curve": [ 0.847, 22.13, 0.95, 5.91 ] + }, + { "time": 1 } + ] + }, + "left-front-thigh": { + "rotate": [ + { + "value": -0.02, + "curve": [ 0.068, -0.02, 0.348, -32.09 ] + }, + { + "time": 0.5, + "value": -32.02, + "curve": [ 0.653, -31.95, 0.906, 0 ] + }, + { "time": 1 } + ] + }, + "right-front-thigh": { + "rotate": [ + { + "curve": [ 0.053, -5.52, 0.125, -12.96 ] + }, + { + "time": 0.1667, + "value": -12.96, + "curve": [ 0.25, -12.96, 0.417, 16.2 ] + }, + { + "time": 0.5, + "value": 16.2, + "curve": [ 0.625, 16.2, 0.866, 9.48 ] + }, + { "time": 1 } + ] + }, + "left-front-leg": { + "rotate": [ + { + "curve": [ 0.09, -12.24, 0.131, -21.37 ] + }, + { + "time": 0.2, + "value": -21.49, + "curve": [ 0.319, -21.94, 0.5, 77.25 ] + }, + { + "time": 0.6, + "value": 77.25, + "curve": [ 0.7, 77.25, 0.908, 12.37 ] + }, + { "time": 1 } + ] + }, + "left-front-toe1": { + "rotate": [ + { + "curve": [ 0.071, -4.48, 0.2, -12.68 ] + }, + { + "time": 0.2667, + "value": -12.68, + "curve": [ 0.375, -12.68, 0.592, 6.99 ] + }, + { + "time": 0.7, + "value": 6.99, + "curve": [ 0.775, 6.99, 0.904, 3.55 ] + }, + { "time": 1 } + ] + }, + "left-front-toe2": { + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.331, 0.125, 1, 0.375, 1.029 ] + }, + { + "time": 0.5, + "x": 1.331, + "y": 1.029, + "curve": [ 0.625, 1.331, 0.875, 1, 0.625, 1.029, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "left-front-toe4": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, 26.52 ] + }, + { + "time": 0.5, + "value": 26.52, + "curve": [ 0.625, 26.52, 0.875, 0 ] + }, + { "time": 1 } + ], + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.211, 0.125, 1, 0.375, 0.993 ] + }, + { + "time": 0.5, + "x": 1.211, + "y": 0.993, + "curve": [ 0.625, 1.211, 0.875, 1, 0.625, 0.993, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "left-front-toe3": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, 16.99 ] + }, + { + "time": 0.5, + "value": 16.99, + "curve": [ 0.625, 16.99, 0.875, 0 ] + }, + { "time": 1 } + ], + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.355, 0.125, 1, 0.375, 1.008 ] + }, + { + "time": 0.5, + "x": 1.355, + "y": 1.008, + "curve": [ 0.625, 1.355, 0.875, 1, 0.625, 1.008, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "right-front-leg": { + "rotate": [ + { + "curve": [ 0.11, -8.79, 0.179, -18.3 ] + }, + { + "time": 0.2667, + "value": -18.31, + "curve": [ 0.352, -18.33, 0.55, 15.48 ] + }, + { + "time": 0.6333, + "value": 15.48, + "curve": [ 0.758, 15.48, 0.875, 7.9 ] + }, + { "time": 1 } + ] + }, + "right-front-toe1": { + "rotate": [ + { + "curve": [ 0.042, 0, 0.147, 8.28 ] + }, + { + "time": 0.1667, + "value": 10.52, + "curve": [ 0.238, 18.41, 0.406, 35.06 ] + }, + { + "time": 0.5, + "value": 34.84, + "curve": [ 0.577, 34.84, 0.669, 29.65 ] + }, + { + "time": 0.7667, + "value": 20.24, + "curve": [ 0.852, 11.97, 0.941, 0 ] + }, + { "time": 1 } + ], + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.412, 0.125, 1, 0.375, 1 ] + }, + { + "time": 0.5, + "x": 1.412, + "curve": [ 0.625, 1.412, 0.875, 1, 0.625, 1, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "right-front-toe2": { + "rotate": [ + { + "curve": [ 0.042, 0, 0.125, 24.7 ] + }, + { + "time": 0.1667, + "value": 24.7, + "curve": [ 0.25, 24.7, 0.417, 7.35 ] + }, + { + "time": 0.5, + "value": 7.35, + "curve": [ 0.544, 7.35, 0.671, 33.55 ] + }, + { + "time": 0.7333, + "value": 32.36, + "curve": [ 0.853, 30.19, 0.919, 0 ] + }, + { "time": 1 } + ], + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.407, 0.125, 1, 0.375, 1.058 ] + }, + { + "time": 0.5, + "x": 1.407, + "y": 1.058, + "curve": [ 0.625, 1.407, 0.875, 1, 0.625, 1.058, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "right-front-toe3": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.1, 11.01 ] + }, + { + "time": 0.1333, + "value": 11.01, + "curve": [ 0.208, 11.01, 0.358, -5.41 ] + }, + { + "time": 0.4333, + "value": -5.41, + "curve": [ 0.508, -5.41, 0.658, 20.3 ] + }, + { + "time": 0.7333, + "value": 20.3, + "curve": [ 0.8, 20.3, 0.933, 0 ] + }, + { "time": 1 } + ], + "scale": [ + { + "curve": [ 0.125, 1, 0.375, 1.33, 0.125, 1, 0.375, 1.182 ] + }, + { + "time": 0.5, + "x": 1.33, + "y": 1.182, + "curve": [ 0.625, 1.33, 0.875, 1, 0.625, 1.182, 0.875, 1 ] + }, + { "time": 1 } + ] + }, + "right-wing": { + "rotate": [ + { + "value": -9.59, + "curve": [ 0.131, -9.59, 0.191, 22.41 ] + }, + { + "time": 0.3333, + "value": 22.81, + "curve": [ 0.417, 23.05, 0.533, 7.5 ] + }, + { + "time": 0.5667, + "value": 2.07, + "curve": [ 0.588, -1.48, 0.639, -8.35 ] + }, + { + "time": 0.7, + "value": -8.29, + "curve": [ 0.743, -8.24, 0.792, 5.28 ] + }, + { + "time": 0.8333, + "value": 5.02, + "curve": [ 0.904, 4.59, 0.957, -9.72 ] + }, + { "time": 1, "value": -9.59 } + ] + }, + "left-wing": { + "rotate": [ + { + "curve": [ 0.039, 0, 0.084, -21.1 ] + }, + { + "time": 0.1333, + "value": -21.1, + "curve": [ 0.292, -21.1, 0.505, 4.34 ] + }, + { + "time": 0.6333, + "value": 4.48, + "curve": [ 0.679, 4.48, 0.699, -6.03 ] + }, + { + "time": 0.7667, + "value": -5.95, + "curve": [ 0.857, -5.84, 0.936, 0 ] + }, + { "time": 1 } + ] + }, + "left-rear-leg": { + "rotate": [ + { + "curve": [ 0.08, 5.97, 0.25, 15.82 ] + }, + { + "time": 0.3333, + "value": 15.82, + "curve": [ 0.425, 15.82, 0.608, -17.33 ] + }, + { + "time": 0.7, + "value": -17.33, + "curve": [ 0.775, -17.33, 0.871, -8.98 ] + }, + { "time": 1 } + ] + }, + "center": { + "rotate": [ + { + "curve": [ 0.072, 6.07, 0.213, 17.69 ] + }, + { + "time": 0.3333, + "value": 17.69, + "curve": [ 0.454, 17.68, 0.583, -15 ] + }, + { + "time": 0.6667, + "value": -15, + "curve": [ 0.804, -15, 0.966, -3.62 ] + }, + { "time": 1 } + ], + "translate": [ + { + "curve": [ 0.125, 0, 0.375, -0.01, 0.162, 0, 0.288, 144.81 ] + }, + { + "time": 0.5, + "x": -0.01, + "y": 144.61, + "curve": [ 0.625, -0.01, 0.875, 0, 0.718, 145.5, 0.832, 0 ] + }, + { "time": 1 } + ] + }, + "tail1": { + "rotate": [ + { + "value": -1.88, + "curve": [ 0.125, -1.88, 0.375, -67.4 ] + }, + { + "time": 0.5, + "value": -67.4, + "curve": [ 0.625, -67.4, 0.875, -1.88 ] + }, + { "time": 1, "value": -1.88 } + ] + }, + "tail2": { + "rotate": [ + { + "value": -27.45, + "curve": [ 0.125, -27.45, 0.375, 2.68 ] + }, + { + "time": 0.5, + "value": 2.68, + "curve": [ 0.625, 2.68, 0.875, -27.45 ] + }, + { "time": 1, "value": -27.45 } + ] + }, + "tail3": { + "rotate": [ + { + "value": -7.63, + "curve": [ 0.125, -7.63, 0.375, 24.54 ] + }, + { + "time": 0.5, + "value": 24.54, + "curve": [ 0.625, 24.54, 0.875, -7.63 ] + }, + { "time": 1, "value": -7.63 } + ] + }, + "tail4": { + "rotate": [ + { + "value": -10.04, + "curve": [ 0.125, -10.04, 0.375, 1.03 ] + }, + { + "time": 0.5, + "value": 1.03, + "curve": [ 0.625, 1.03, 0.875, -10.04 ] + }, + { "time": 1, "value": -10.04 } + ] + }, + "tail5": { + "rotate": [ + { + "value": -11.26, + "curve": [ 0.152, -11.21, 0.334, 9.91 ] + }, + { + "time": 0.5, + "value": 9.93, + "curve": [ 0.662, 9.95, 0.844, -11.17 ] + }, + { "time": 1, "value": -11.26 } + ] + }, + "tail6": { + "rotate": [ + { + "value": 26.68, + "curve": [ 0.194, 26.72, 0.328, 35.84 ] + }, + { + "time": 0.5, + "value": 35.84, + "curve": [ 0.678, 35.84, 0.818, 26.62 ] + }, + { "time": 1, "value": 26.68 } + ] + }, + "right-rear-thigh": { + "rotate": [ + { + "curve": [ 0.055, -6.4, 0.166, -22.66 ] + }, + { + "time": 0.2667, + "value": -22.66, + "curve": [ 0.384, -22.66, 0.517, 19.08 ] + }, + { + "time": 0.6333, + "value": 19.32, + "curve": [ 0.746, 19.56, 0.91, 9.17 ] + }, + { "time": 1 } + ] + } + }, + "attachments": { + "default": { + "left-wing": { + "left-wing": { + "sequence": [ + { "mode": "loop", "delay": 0.0667 }, + { "time": 0.6 }, + { "time": 0.7333, "mode": "loop", "index": 1 }, + { "time": 0.8, "mode": "loop", "index": 2, "delay": 0.0333 }, + { "time": 0.9667, "index": 7 } + ] + } + }, + "right-wing": { + "right-wing": { + "sequence": [ + { "mode": "loop", "delay": 0.0667 }, + { "time": 0.6 }, + { "time": 0.7333, "mode": "loop", "index": 1 }, + { "time": 0.8, "mode": "loop", "index": 2, "delay": 0.0333 }, + { "time": 0.9667, "index": 7 } + ] + } + } + } + } + } +} +} \ No newline at end of file diff --git a/spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj b/spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj index e5637925c..664dc88cf 100644 --- a/spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/spine-flutter/example/lib/flame_example.dart b/spine-flutter/example/lib/flame_example.dart index fef9edc0d..53e5df667 100644 --- a/spine-flutter/example/lib/flame_example.dart +++ b/spine-flutter/example/lib/flame_example.dart @@ -114,7 +114,7 @@ class SimpleFlameExample extends FlameGame { // Load the Spineboy atlas and skeleton data from asset files // and create a SpineComponent from them, scaled down and // centered on the screen - spineboy = await SpineComponent.fromAssets("assets/spineboy.atlas", "assets/spineboy-pro.skel", + spineboy = await SpineComponent.fromAssets("assets/spineboy.atlas", "assets/spineboy-pro.json", scale: Vector2(0.4, 0.4), anchor: Anchor.center, position: Vector2(size.x / 2, size.y / 2)); // Set the "walk" animation on track 0 in looping mode @@ -129,6 +129,36 @@ class SimpleFlameExample extends FlameGame { } } +class DragonExample extends FlameGame { + late final Atlas cachedAtlas; + late final SkeletonData cachedSkeletonData; + late final SpineComponent dragon; + + @override + Future onLoad() async { + cachedAtlas = await Atlas.fromAsset("assets/dragon.atlas"); + cachedSkeletonData = await SkeletonData.fromAsset(cachedAtlas, "assets/dragon-ess.json"); + final drawable = SkeletonDrawable(cachedAtlas, cachedSkeletonData, false); + dragon = SpineComponent( + drawable, + scale: Vector2(0.4, 0.4), + anchor: Anchor.center, + position: Vector2(size.x / 2, size.y / 2 - 150), + ); + // Set the "walk" animation on track 0 in looping mode + dragon.animationState.setAnimationByName(0, "flying", true); + await add(dragon); + } + + @override + void onDetach() { + // Dispose the native resources that have been loaded for spineboy. + dragon.dispose(); + cachedSkeletonData.dispose(); + cachedAtlas.dispose(); + } +} + class PreloadAndShareSpineDataExample extends FlameGame { late final SkeletonData cachedSkeletonData; late final Atlas cachedAtlas; diff --git a/spine-flutter/example/lib/main.dart b/spine-flutter/example/lib/main.dart index e0ec04ee0..0c5b2bc2c 100644 --- a/spine-flutter/example/lib/main.dart +++ b/spine-flutter/example/lib/main.dart @@ -143,7 +143,19 @@ class ExampleSelector extends StatelessWidget { ); }, ), - spacer + spacer, + ElevatedButton( + child: const Text('Flame: Dragon Example'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SpineFlameGameWidget(DragonExample()), + ), + ); + }, + ), + spacer, ]))); } } diff --git a/spine-flutter/example/lib/pause_play_animation.dart b/spine-flutter/example/lib/pause_play_animation.dart index 96b55375f..912394def 100644 --- a/spine-flutter/example/lib/pause_play_animation.dart +++ b/spine-flutter/example/lib/pause_play_animation.dart @@ -65,7 +65,7 @@ class PlayPauseAnimationState extends State { appBar: AppBar(title: const Text('Play/Pause')), body: SpineWidget.fromAsset( "assets/dragon.atlas", - "assets/dragon-ess.skel", + "assets/dragon-ess.json", controller, boundsProvider: SkinAndAnimationBounds(animation: "flying"), ), diff --git a/spine-flutter/example/macos/Runner.xcodeproj/project.pbxproj b/spine-flutter/example/macos/Runner.xcodeproj/project.pbxproj index 06f931d0a..d04bbf466 100644 --- a/spine-flutter/example/macos/Runner.xcodeproj/project.pbxproj +++ b/spine-flutter/example/macos/Runner.xcodeproj/project.pbxproj @@ -203,7 +203,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/spine-flutter/example/pubspec.lock b/spine-flutter/example/pubspec.lock index 458916f82..d1522f504 100644 --- a/spine-flutter/example/pubspec.lock +++ b/spine-flutter/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" crypto: dependency: transitive description: @@ -114,10 +114,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -202,6 +202,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_ffi_fork: dependency: transitive description: @@ -211,5 +219,5 @@ packages: source: hosted version: "0.7.4" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.5" diff --git a/spine-flutter/lib/spine_flutter.dart b/spine-flutter/lib/spine_flutter.dart index 0da5432d2..a4afda089 100644 --- a/spine-flutter/lib/spine_flutter.dart +++ b/spine-flutter/lib/spine_flutter.dart @@ -126,7 +126,7 @@ class Atlas { final numImagePaths = _bindings.spine_atlas_get_num_image_paths(atlas); for (int i = 0; i < numImagePaths; i++) { final Pointer atlasPageFile = _bindings.spine_atlas_get_image_path(atlas, i).cast(); - final imagePath = path.join(atlasDir, atlasPageFile.toDartString()); + final imagePath = atlasDir + "/" + atlasPageFile.toDartString(); var imageData = await loadFile(imagePath); final Codec codec = await instantiateImageCodec(imageData); final FrameInfo frameInfo = await codec.getNextFrame(); From 6fbe065fe035a7ae89babc5dfc89a21014912bc7 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 22 Sep 2023 17:32:00 +0200 Subject: [PATCH 02/20] [flutter] Update changelog --- spine-flutter/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spine-flutter/CHANGELOG.md b/spine-flutter/CHANGELOG.md index 3ead16391..01855ad48 100644 --- a/spine-flutter/CHANGELOG.md +++ b/spine-flutter/CHANGELOG.md @@ -1,3 +1,6 @@ +# 4.1.6 +* Fixed bug in path handling on Windows. + # 4.1.5 * Updated http dependency to 1.1.0 From 68c87192f8d14602a0aab04b0d83cc29b3274f0f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 22 Sep 2023 17:33:09 +0200 Subject: [PATCH 03/20] [flutter] 4.1.6 release --- spine-flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-flutter/pubspec.yaml b/spine-flutter/pubspec.yaml index c83a75d50..9fd54a24f 100644 --- a/spine-flutter/pubspec.yaml +++ b/spine-flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: spine_flutter description: The official Spine Flutter Runtime to load, display and interact with Spine animations. -version: 4.1.5 +version: 4.1.6 homepage: https://esotericsoftware.com repository: https://github.com/esotericsoftware/spine-runtimes issue_tracker: https://github.com/esotericsoftware/spine-runtimes/issues From b89d5507110c24afa15624b867f71a5c2427c23e Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 22 Sep 2023 22:11:25 +0200 Subject: [PATCH 04/20] [flutter] Closes #2383, fix allocations of temp structs for Windows. Dart FFIs MallocAllocator does in fact not use malloc... # Conflicts: # spine-flutter/lib/assets/libspine_flutter.wasm --- .../lib/assets/libspine_flutter.wasm | Bin 387533 -> 391173 bytes spine-flutter/lib/spine_flutter.dart | 4 ---- spine-flutter/src/spine_flutter.cpp | 10 ++++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/spine-flutter/lib/assets/libspine_flutter.wasm b/spine-flutter/lib/assets/libspine_flutter.wasm index ccef18809e6f48dd10ab4c303292bea92cc206c7..3a5a2fc77b15c1d24145da765bf358adaa41dda5 100755 GIT binary patch delta 120574 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

lI1ub-nZ0VD*;Ih}W5%iWyj@K@Z?s*! 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 Ax&QzG delta 117458 zcmeEv349gR_5a+Nc}wzMlF0&uB#?P80RjY#m_n+knJS2airQMNTBU+QKtVu@t?k4e zcj~C46%{N}v{tF2SQo^)lqxDJRjjCJ-B(=e?*Dtvz4P9@f)pf4*E_@`u0yf7Y#7m7#(|9kC5uU!O^^sk8E-x9Oj zH2axm|Nd1~h4Fa2kY3S$al90<1{6q&5^6~#>5un!!%hpXO4U(DK67hvBqGUzNF?GF znHT6Nz0mAyQW!+VCyp47Jj59qIMec`*`g`B53Gz`OZ6HNL-wu@?*xCncOJq&lhPP;T=Ot zjA!tSKCHR3CmlQIsF|~mIsBNJlc#7Kz4wOH8PAG(dg%=vT53Eer&Vy|%o#IhYtJJ^ zZ)OdRPS_+Wy^!;cP$v8CMNt~6cl@l$ljcs*{w-48 zn?qB&_LBGd(81zmue@=fc*WbPu~xk5{j9OZcnv+@+1S_AUdPA}ZS3pa*jOmu@NRD$ zAl~#gHC7pKi3ZQ!p_jMourk2o?M$jdyyG1>>?lm>j$wVp0Po3R1NK-Ytk4i>cqeLi z=j73JPSjQh*w0g>a#2$+;&fkcfE1jr^ROP*zJET z-t+F={!H<{H?FB#|3G+6O_jjs54}^HUK1aAGj`}}d<=x7v98{s0Au}6CY3PW0V?86 zB3S!RWLn|EiQ1=La(H$B&vL-4KtJioxzkRVqJ1vBNyB@4hYU}NFTCFjuM}T;mkb{# zzVcoi-c`BktaiIQ`@NVC6fY{(|+ObY=*S!~Ztn~Ud7aAw)1H7gmRF=whK796+qX~Ma=-W}Q zH@&%!ajM>rpJ)BR1TbzDl&ZngN`9*JD4+V{d{D}h!rT4_eLZVrq%c?vbiVx$1{xbg zooXqm?@Nz0-sL~|HRvft&jr7nHW_62FW?V#jb;m{PMLP})VUxm%Bu0&N5qWNxG=x= z&fhg^oTkgl68}%v?Vz%^c|^oGLzj(NBNO_WI*)7C4$;x z-cchDFy`yEL0;te6Xs}Vc^gMIi?hAbmK}_90IcUm)_5ozCzP9`ovVAtwA2~r=?%e) z#0gVo&z*K8-kq;|ceM;OF3`8Fw~tN@F9S@!n3oOL8HO%DL-cD82z4z}QzGiqBDq5g*6c>5#?vxT2d!;+q8JA$zC|5Wq zn5U2CYP?_W+-zK`Q>U_twL9-^T&9}XepI3OJu27470fS3qS8BQL_%Mvd)JSu_BM|) zy{kt}6jykkj~b|7seAkGQtO?)YoU1F+h>;{#wL*NySw!E7VKhyIWOL&UYzBJEXim~bwy14a`u#CW}Yx5L{i!xy|UdJ#WHWV-TG8t ztM~5E5QuK}G_2xv-f_F_QTr#oD)e5xJACrAIkPY??RszhZok&C-V<7T>lf(W)K**n zv+kYUTGRUmU2#($#F4Y75I4QiyR&tKv0U%(9qLqhCC;Gu)3I7^-+OPb-qG2Ur_6>_ z`I$2;{ug~nkl%~>{Z0BnZ}IMZz19QGo@bJ-`NShRQ1uXLfuGUOr!Vo5pT$&eP^?PmN|&o)eM1gZ?Ydj7eE|qW9d`MsMa`F|Ty5 zp#@W?OqvY={T}dguM{Dxw{9;>f0vR!FgA-up2oVpVrY6c661#AVNN#pL~SnTLVO+X zIpc=o?fU``DIR t}v&bVg%!*Fg9ir?$Cj@u4QjJrP}R3+{ejgN_6V&DV3oyRAQ z`@oh7e#heZex18cLJ68N6Iz}205b40oAGyunf=FBXVC7#eFhnKg1V6BS^JvaUi(~2 z2$kBm2FVif6kypXHe9$*$}2f2>doDEkXVI8?3iiuNR_#8pMKuE`^6zPS7SwG8}W=z z@wCc&cfSI!b-$SLuWT9bnEh7iYjjUcsPPt#DF(tcc#j=Wn}aa#9SDR0z$L;Ap!W)4 z26+1)SgR1Gk@IW!FE;)`b4C7sc)k^!;IRV+7D#RLL8R!-4f_`v59vd=#UA%mkVY z=A!59nIIgQaNtmAYH}WN!jO6tts@Mnr(zL?qPO_Kfxyt?Ymn8uFklBz)lNRBs$eFw z&^y5pkn-mJ_+n!fWFnrE;Mdcb={*Id0yVwyKPiG@lH!6V{G?H=LdTWf9X}}%ck|PB z2gM5&Ev=TV(~?4Ispab19aM+~;$FR<95liB3@n(MU3E~g_}qK=pguJZ>w$@;#4*b$ zwsIc1@TX1UQE!KzjzG6lf7&9}d5eGgBk>si_VPac=}i4m-MeREZ?ApgVd8P`xrw9n zJB0WA#H#2C!=Z=)^M2Ns9#_J;+BC7RH}~M7XlK#E^`z}BKDf8H{AZ@O>EHr=10_>l z@@E5$RiZza-S=mMj3>a(P%lukZblL<2FVrrATBxVkOFVh&!WJSL0;=2X{Dy!3SETy z9CnB$9`trSWT<$^Ydd6n_YbiG2=ODQA2v2Zu*ALt+I@9F=HeWsscoNQcdv>TTg zU+cc{m42!5rGA<5h5mcv3gb%Sa^ouFBmHWVo);NcbKW0}Ym7e{AL`c{*BO5@t~dT{ z++f^<^m1dBakp`^vC_EHxC=S!_0`6`#(l>9#skLRjWx#e`a{OU#y^ZlTw|@V&Uo7R zm+_eKxbc*+!FbYm77aXOY%-oVo-?--|>ZyRTrZy9HrZyNK> zH;l8)*NwBy*Nk(_MdkwYT=RU>GcPiKXD&3aFn#kf^Y`ZE=96geO7lZ~3I1PZ-eBHn zuGg2Fe=%<|Z#LcA%)go|%$4R{<|^}c^G@>~^Ka(e=HJbKn2(r`nh%-}nQP65(ZoN^ zjph^PljgJLbLL~_^X7VUllhMMuKAYvp828qk@>#)AM-Qw3-e3!EAwl!J#up7Q}dL_ zC+4Y9A{R&e$a+`5Byx3RapVsX0_&2Y3hRBVPn*G`7m+U`Uq!x-v`0^lo)SGHdS-NfbXoMe=vmQoqvu4=1D0J7 z{ay6Z=;hJhM;Au_6umxrcJ$BD1<@O#mql-kE|2~tdS~>m=-tuP(FNu`(R-u+h&~iu zi|2=yUx}vl$zaac{zU!E|22)3g1YO&1I6Mbmi;sg3=e4Knp1Qh&;%+AkK)2enjZ zKAzTBJMFG4b`g?Zv9swCZek%)H`h4JWNHagOA8&;rRJ|k>e)gEy;JHoq)w@E*2#CP zk$Nvh&9A}J#hE&I?X2z>w1<&7r`D@FE)Cmxy)J0tHKhJtNWD<=n~}OA5u`pr>iSHo z{Sx7PSr80sKBY2UEI?{w%E5T3l}nMjBU5h?Qg3J8xtOGLErVn^p5D!X@it0j0Jj>c zRhiTpq~6KEU>#EDCIX0TK+5YEOa}mUE-faAyoRUs8JKQH>K;nDJite5I@@&}Lf0#VmI6GG5?{J=8?hT!CK zi>4yB?yMcke1%)GbsJ!dNo6C{A;tmjeQF3`6N>HPl#xJ7R-0{DAA85mIG}fdwKO(D zM=vzc20OvYX7A}4XJCet!ABt)y77~2>o1P3tH~e@awE=9HF<< z174gY^XDS93|KQu=FX70laczQX@q`+%so`*{#fNsz;EkxYDBZW-^{KlY9(YOv`=9Y z-qP6v4+9|p1{-Z67hpgsUrEjOWh+8kXZfJKC%W*!U`&StGGg<*hOJuznn(6e4)wTew+7rf< zQ>B|)^qO#E^G?`7_0bs4zT<>R!}AoU>UM4~s+#O&-(PMtP|X`QZ(OMy;vPU`iMocj zd0H>;%6Wz05r7mRSE<-n2ACkx#aK~VW}PaKf z!V64u!D#~cgx~qz$tRBNH6J+K0Mt&!z+o|* zLK_3}7bVSJJ75Te@~Tb@gBU@$uh}t2pW6nGfOj-@&ZS1Y@?Y&#J2tJ|L`0uMqcn^G{0Q?{z0t^~G2T(II~B&^XD*-Y)Z91pz1_DLxcgviYHLwSa z?;azi*(ULWdQb#0MjO9kC#?p#E_grvvwF9Y;A>neiMJbCXl^D^tZADm4WnDGPfthc z(Gu8nIt~f8C+IdV*ED9wiPTOP3NfTG(46`UJG&UWu%3;k%a|QIeMXS1Qp0Ed8+R(H(`K%xBrEu217Q93;K7* zvV6qqI?u{S&Drb&2{bSP+1?@@K#jA$0OW$W8=)`JT|%3P9VZ&V2*GQ49SOP8QQ5Ct zOXm3ifE@*wucA6AzD#$heGRjUICdNUEYjPoo1&Q>?U;m|j+sj_n#Bwfql;XWhY(d2 zY)OED$d%w@p+(J8LDG(1gDzXu1xexDUg>Do(@{_} zztldNx*SZ@*3~jb8{7a-6LySNAWjIh0=x1Ftn~`Ad`Pm!(otI7xFpCZfI|&n zfeSNuOC@d-b`<1FQ=|nZIPqYft_}_6`kcYI>F{Xs4Q81d%(5O1=K9Wq380K$C|AHU zYpDx{=db|D;dTmHS8TjbjL-nl;9r8~GT|8e5vUpv9FW$BX`0i#XOXb+1dPX@J&ORe zVVa)8)aXYK`jy}dFo9v`GG9aixxA2-(LIX{pcxFkg55RzWBn`IqF*F9>+X0q>r$Du zq%xbeP-eZA$Yw2)S&t-8Bmt{N5pQ*9S!nv>lhSCZ!eP38Fb>=vsDFM^@S~$y#u)>!T zsnbXoVHxD0fX-kbOOUpf9!dgF5VbSWbwq+?SV^t~CPgX;bB&Pd%(Iv5uGyB84=IP< ztW$fr8EybgTdJn5REv6*93e~P1XL@`4`|FlI6bWxFcz~DpqY(GE<(&G3C%n1Y|0P| z2&_A#`%)fi5zaY95=+o;1Du=b7;%wYmIh%EW@#eBMAl2Xb)EGX3kh?8on0r)MTqJ% zTm-r=u!aGL5*yH>EoDc*MZn+QOoG3q3Ng0|G=~Z)6e0d&$yRk5V&yoZOD7H~soxBY z-?ZCB{?Wq`F|qH8L}ex3)PSK;U35>2kR*QPA2HFabWyt^r8B>>_5FxhVu9Q_ZK)(Z zO?Qmr(or%sQ!#@{9||359a8KO0?~K~k77i@8myC~W9(I=p)?glHhQR0e_Kg^cKgoRT7q*&I!kMn~AwY$v@J z)>gCCNU{-}=Y4}EqEs4yaSiFD5ZHfLN3kHq=zk&nS_hzjI%GGyBgswKUGNLI7Uc)<9RBb;4omWc}>-4aDo zt&oVOO3;jCovsK{!bj1gvtA>CeFq6_pc8ygwOBFp>1ZnI?Rrgr7wZHaQ>{}mDQ#+< ziW#s>7bwsGMGcxogIeRNNIAA!2Jo^va7` zVA>Lc%8H;MzlQU(awy2RIsd{wS;b%;=IlU743;FAD`SN9F}RSxz?@t9$c!ona@2XXcaSoWI@MMw zJby)Ria=cnH046|K^JTLU{N+kchW{7@ugyXfFWReD*z9z{N^Z&oC;gtr>GZ`Gng27 z0BM5Y7g!aDdLTKx2gCp{#4_h1Vq0l)0Z@eoumQ=k_98|_uH**$=W(YIRF1{L3bM_^ zN)%dl{$A-kr;%#_^ktRmtYT9$C`iqKUxBAV`=%1$Kj3OS5<3>+gZ>f2v28^RumOe9 zQ`Zczz9$T@5n$>u18fX4K$%bs(3L600A(US0|YEVSW#xIk4nuli~0aM>#^(ytCz|> zE3kzb3QO9B848mmK@{euQKG>nb2Ep;vK+Dyq}F2&S;+D*mqqr%1HONT25TF1H3^^7NQl>PlEF(E&%bvrZL4wyj7 z0yEn}>qXK;adVyv)mNvy27hi80=ts<#WR^C6z$%;pR>w;Uas1GG&tBjA+< zM9Hp5ndlSAt4SW3J86rF`2&LD5zWp5!I!`e3se^Xk5)$F zK(H{b7TGrF)sdzP*U}d-sV$&GU5m~f*7`TZrEu;nBmoF?CudMa*W?K)40I=*HJRbm zqbL25i4XSv!DSfea_@04iy&cHmcLv;#6g zW^`5$W`HZhliefrUQKWib&O2@~yI88;5EIxblfoPn!8U7HVF+whrIKfD)n+^DvN9G3 z#s-^A)f|O^1Q77>12fx44ki)`Nf9AstaPGs9kMb626<{RRMBUm*2N|#m1b&PV&a`E z4Pm?iJBD`-bYZ2Vq)tNrBcHDzg^&$o^dt=cQie0yq!dhnHc8bbUfQHh3bK%?1W*X_ zPHK~~b+)RbH~W)@i&}q|U+9TS)fXcd3R7PtsD)6frIjv*SCfj85)5s2A5au&?qpAw zKzYM}>=@hBuro=KgHeOm88jhN6(b;(8dFS4^S30hr>P*asggpIPy)LV^v0F1pkims zk#L4RoiWfc_jt&t$2{Nx2;qfJx?318^zO4Kmrb*zOF$ z?7%tiku9!rhLbFvY8Mc-;Axg!KzxE?B{V#OPZZct-OY4y#^!#tPasuYs&P_hsd^HS zx*gaCa_P3weNv88{iIt`)jw)D&c4C+?i=*jwNRbc6QSBJwneI9%FvQLcGd;x)860oy2QLjtb^kl}E-_cO_%5lrpvt*pV*A3Kg82K(f|tR zsa@nz0iYL~Y1ho?0M}(LYg9QGTbCUj@8?39au)PZ3E;S-tZh9X)k$J0-#a|?us8BL z->Wj+b*%x+7d=q{7HpFWfC-0G0F{CYz?zauDiuJb@~Qx2Tq_k|9jgFee?tq~4Yq?} zVrO|h?~lM-%4=btz0E2>9t#^`$Vvq$Rw}@M*TOCYFaN)%0{qWg*ktp6ixzfKcT|A9 z`#W8!0L!I>r|q5VvU30GF64el?7Jgr@8-WO_d{ZzHjKapdbrK=4ds3U2Bb2xUAD8o zTe<(i7R>CZPD|QjxqrjJt()0cWwMknx1A>w9K`3Gl7gR^qTDHmJG_Mu5-%eKn0Ust(-#?hja@>ASF2O-6$IIDwz>QXym_uLe;f-SapR1ONOQgF<6;yM!DFXAPx5kF7iC;hk1Kv_jrqVGiq( znirUgDA526#?v}ah63T- ziftYF)(4*P{lTM<<0V!Dhkfd}a%({Xp@SV8C0aw^QjS}gqPcn`u)fa5BYc{TM|e-g zBOvf@%Z}miSYWx|c3eWw1eV^<#fh&M5&1y&2s;eobnr#0c7^QSaSIWE65pqq0!|R^ z25%R(#5wK|==kWQ{ZyldikfdX06tuXL>I$E)7{noNIMx<-E|%<< z@cFzfe?C3SpTGS7`20QR&ty>HEbKmRQ4u|kvP(%vv9W2Rg|vI;IkqHO6~(JovNubd z#cX8piAPADc)Cbx)kWBe>xNPdNgCQxce}lzsI=-23~VIl)2g>@4%iqS^nZ?G(Vgom zv6Hl_7U~1MBk5wY#|0TPQN1<$FN!=^?cCpo-WJj?Ec;gIY`4g0cGBf;%s#G>(Plbw z!6wN3Ht=}4j7Y;VcISfv&c)fRux9V#GzO{mee=kcs;+49tbyBW+Rhr$-^p2d^63DU zI3!QrlaB};R7a|?ZXDsvQ8Hn7l-uF7MG$seNFg+#uw(2*lOR!1#}u}tc7OSFCAOw< z;-?2;9~nWS?4VpF2Z0UW$ zIZ}u~Ei!5Ghr&HzO{n4UW=7C8Rl?~rIH$?YhsB@{uuUr0DbOABffeEOTdt%|5Xtjm zVa!#C+oOHdw+C>n5-IktNECHR6M8O-xliW7+~Uk~g)z4a#4YX_;^JH-T)NwWx%ss7 zd=TF95hT!G=nPU3JMV>OG* z*xJfP=83eQ6lIjkARN3Yd5#lR05x@|^PXfS=Z6mu*_;zTJXFs)VJ~A1=kSSGSNjn* z9Bp-)32`KjJQYj1Dd}xQ=sk8CN9c3$Q)k!X&{HFdg-`wn>wX+OQzw7GN@f91g>?_Y zzlij4av5~RWK&-n(dbNUmOZ#z;SZ9EzdQEe(qe6aKWrO&AYVjA6fc4!txNXchHT5c z*qe)iop{POWe=`of9ecPXa4YAvj;j?y=B4n-Li)s9j8eu_JD2jp7DqIe8v~|qCte` zGKfPVFSfuTz%meqo68|O(%u8vQ+L>l0By;m?x0}rh5J(t%ZPJyNz^L@ZD0^xdyiew z*Bya_;wXL#A4Xt}MRz{u6U7{pPl5NKWGijTKx%M>8SvRr;62Gj^3#(5#Dw5;6T!JW z$@GFqYVZdNyr*VTCR}n31>RE;s0bw%F&bQ|A%g>iSNr@9)jShN3<+~fLSOfB@QQc0>Ysw zK1Q8NmK{zGng=U**?Yg=-wm$=uJW76D1I$3J6DV zD7yqgbUKD1fq;Q`0zx$bL6x@)hv+r9nvXXMFr*L=C}acJXx+k?upACa@c4Ua1#JW+ zw9DuloRbq!@O0<&B8n~Lqt$z0vc*;4RyK{fbS)3j`!ue*s!(psYzQq6wA-Y7|0k34n12L zy%w3=}4d@Ib z?;6;cGOH|-Bxrkq8j;i`UxEaXWoBS(=UO-+4F+u~0|%Tbvyf)!s0>Qb+1d^kPBbt= zfc(^ZX+c)IuxTkWAi~lpxPo%IsJwB8ng?`%axZJ1`(wk zv{?hx$|a=FwhSEXNNy`|$ZylEPC2m*=oaU&w;4FP^QjFof-8=eJ>t|W=e4wUyI^eI z=TFJ&Q$%nchXRGDx~ko6&ILf_5~(85jsX9%6TlH`E4e1wo(Y?UBE(nbP-E7)q0YXu z1EY%xbzoeD@LhoWW<@TE4fYlZ>nT%q(g#NknL|TiA{vJF#2h+v>0$*0~zW;r>Rp)4}!nrYXW=1zCVX7`2F#Zx`Gbiv)?Em2vJ z5Sn&>9}VIW!=?4WjXrJG2CQxf<~(!-ayQQ{K>4omQ!xL7gf7AS@+{1~5X|kZ!(4eX z8RRm;;J*v=Fvx*_Tk?(e4Ca71X)1JNRt(eXagMG9kGgd~f<~Cc8gw>VbQmq1lhScI z0(gY|d%0kECMLnYXL91o<49PG^RA2Qx+0|Z#-;Se{P%GHCVv6%!C6DPynvz_PtKfWY|$$Km6&r89k(C>LBO!nN%EiO zUXKO;X;|HlxjBj&to@21hHq3EB<>2ZY?i{{F!VUUh0`aF2xy=l!&K$ z(UU>W8ONLrYuIq$HlW8MJkrSxJeK1Tw`S5Kj@_;X8_-fFTtvNyCUMO;OU5Q37V~lL zzL}O6cl?s~xB!v7xEz?hQgqPPdBG)_88VwuqQx!%FTick%>k;SI+D0IQ~xo#0N4+e zIcspotPM<4{~d-aso~Knet*9m>y!voidyF#9gPg?CAJc2|w+ zxL5#ViPHT7QM*)f*d#`~mBHwe1`_yz?@wKHph2%OoFvSd90g zOccOCT8l#02`>Z22W|jkTG?`J-P#}`g3J)Q%XR?$E*I&6^t(c&2hlGs4{oqNAhuf| z-KIKYNL{MlE<^-1c85Fm0#1ZkqL+h7ZgMw3fuys#ihQTc!_e+Yslkk204c7b2E`C# zt8f<6Ne3h#Wvs;}u_Mgdjxsjc_4WX?tXUiU7ZPI7k0tynN~6I|09f@Q@Lr|{^sMFf z-O%ztv~0?j2OKm&txdWaM%4rB<#dgtP9i)hOV|t2@L{Kt-PaG-QEzD3@!Nyz;QINM%(S*8W zC|!5X?*8=Qq8FcVFU}6PMoF3k!Uv9&H6lc4+0m9~s0#!mJv0e_(Pw&9n8Zl&P=Y4GGz67yOC-jxnZ&-V z3zOKr!DyWrk=PfK!~&flmj)Dx>CP*VJ%NA#qkz^5LRQ!B;myrNR#7NW1XlQL{~AtS&6Zw$!>5WLx519mft) zRu_me676+A?RxB!do10l@xfR>OG@A^H9obcjW43choJEa)wtXB$Y=CSzOSaHhob4q zZpnAm?~yR`w#ppKZA(ddeq!lQ5+4qm`cxhCI=Up5Rp_jWo0rLacP#Of9RBiBhQCC6 z&R47~xJ!fLF3e@XU50e#F1T*BEAFDk(M|3$blbR#qv;A0LH|Slw{n+nlm2_c zTzX9Z?H!rRw@3eB*3u2-=>HV`OZ^5;9_$M7HBc!F$^e3vQ@9n1m4aeBnl5p~^fazp z?S&7{k@H15ePH0M6}(+!qB8FEVl|jQPr%LMbTsU0RD)Lq+!aj}lL`cR6}5}$%MXYx znGR0`g?Lj?NOF3cBaTB;sJ&KTPvp+6vHo33Xc@c#u$r^sDkVyNH}kCA2}V z+X?uLp~%PvP>HJO@c779$iO8B60(p%)wrOot44xqr~yp|elKzhP$P)2qLzwstGPwe z6vEhwr6Yf6@y+7pjK~M_34u*7b;e#jGCR4?v`_WYH zM=Vg*{RpQBk55`P?CXKSR|)M9E5d=+fKw%Ubh~OCBajY9+?uLP0Sg(Uv`RUBu%?0QCPC0J)1}_H4{KZw9*ff#6ia{;ut2h4B4=zC zOv)L{(13qPIlc&20>@X_SxRe#Q4;QCeX%7BW$Sk6;|j!0E|q9WcWYiSd|j6NY;J%A zi5w|ZD^(71EU~S0ajFoLD7FjJ*tVITF5IWcVq&C_4e2cHNQ)F1+>sbj4I|AZMr=Y8 zF_zmUM2v+@jOcO8#F(UROLOU=RFM&mSrg^B|5ak_M2jU9;Q`PATmdoC26YpmUuKQX zV1runE=G1mj2j254QkU_AKVv%I}Ahj#bnVbFV(r<5!rQ@>WDplTVw}c5a|m77KtX4eb^mbkTZ1KAk92KSlTp1Dk1qCEh%dlr#&k}f>c~B^ zpX<>r!m2AnyNKCB){;J}I{^jKmA{0h@~8i0Q(0G> zgJZV@Q;|6K8r|j-VqV{O9J?oXVg4G-1~~R+-l=?lam>q|%3YzUyz{?mDo|75eaa}E zIMJQaI`R1(Qk_FuXDQ4z>`#*V$9^lNbz(b)1QBcbA*Hi`OfJ?{4Q#2B5t*?7k*k!> zBJzWwoKia7tO*G1K^bqwFhB5tg8W)1%t}NGk-1r57lkcA)S()%BM}*?!K5uhGl)xo zU74%87PQ&i2tcvc6_{Dr^yF4x5>ivex%G;b$_q1BT7WS9Iw+l;EkNB-7N7)I5Qa#f zguiZJ1Ke68G@NXA{vc0=(@FQsgG1uF^j$&TM2NDV~iGyq&tPm~2e`T%<%eUJc!`FjK# zqt$riPZ4aS6EXY=0$c%q9}=`q%s0Z@R`qn6B-AbK=y&QCd$wpxhB=V0fjzirFUgJ= zYB?O=P>|Weg(FF-_%u$FwucaHNPG6YJ*l30VOW_=dP!jg34i;jsCK*EhuS1EDbgXD zHmMo($e4-ei?WX@n=>iOAhefErc4TVA*k4xhSb0T_8_bS?HuSS+LfjYq$KxdcZAxY z#G%LLGU8+cY?X~CxH4?hKY4o`Rf2U3^a|o%2s_wXf(MRpN4bT9Ty6w_!W)E#Fm(ZK z)Tu_SK{a7vb(bVXi_0Vxc&D0l7YmnhPlf$1pk3v6krVF3fKY&9;5ztS6iMN(s178h z+%AOFG9XI=)(u=x9Z70lhNRjBqqM1Lif6BA3TL#4rf_f+9>IX7hywWIN1tWsiav0F zK%uC0o=INBEP}gqC|-B6*LkNX9Ok~X)Z4IEN?TmG#ajCL^+aTaM+soL}#|( z%DAVFeRBB4cIcC7Hp2q4%pm(j^i12d-S0EpCBnvuS@Jwv!Cr=;Ped){;YFS2nZqHr zGS6_I*)Ci02{zgfH)T9iH|Cjdce^Xt=I^tera2T{Vrw{>cDvE%;JmQSx0?BTTtqz% zL66ukRtz}9e!Zp_Bi@9~#S`l6raawXaftP%i!#(Uz?NjCNQxDT|5?u4n>cT8;=HAJ z3X~aA)^oN4LVj*Hhypcm77!7h+vw<43>TM~Q7p9bsCLKC?Q#e#2-0Hy&o&2~+vMmj z2eeZa;8`VhIUL-ZGm`bWsXBx+|okTkblc$8I>f zdy!I=-7o5u>*(&DpWFSGcAYvqx(RwYer_CHfh-LcM{YXRsPEo_2(T{@c647kD7=-C zi$5J4+o4^ftj*B%J_$@0DX%LqL|2}+?9yU@u2H$Q%@Rw7QD>&s9lYZz7`O}dxdiA3 zrFlNWiHUyjxBwx;Ks*xm7HJnD1=>uNgoM5NG(5-q$}b+GEu1ESPn?><6CK;Z5Bb8M z;}2p3vrVZP++JWkFjBh1ORRSzEZ65+pU)^)>3SrgT-B4J zG|+m2;@+_4$_@bv)TIVNO=^IKJ=h)yRgBK$>)5f)2`$ah-bnT8sIF1-sez>G1_}^i z8w*URWVkhWPaq7Q!Zu1rT}0EAn%7XKFx_<;IQ z9>g6^e8^pC<%sA*45JURjv71#vb!4iR|EDz7g{L>2UzAemcE(n{?JIJgDyJnQRD1U zCvS~3mS}kKbU!vI`h{IiAuZ#ZD;X}`wWI9Q5&9k&C?xNAnX?9LX9SM9$-_C&?xhwA zCAiKNgdIF7UHcrRO@urItfsQ5mN=sc<=AQ5@PuaZwqsIL=BTE#so#Xq38=ii_&P zDUP$X5`mRd9MB3PF?#3GQ2R)i4=bqZRyh@l@n)`unu9~%qJ|n$Q1iuZ5Jr4=8mgTr z63G%AO(e4E?SiF&TG+rV0si5^f~ta_EcJ9}=HwC%Vwh;PmF5rX%F)f`%ne7L`h($s zd)yExBP|E@rQptn!>6FNYz$ODK)KJ(50st(#O(x;jsSs#BtAy~L9|r}AY$ti1weFJO9^&4 zSDg3?V5DyK%>&E?8!xFkNJ!mjB89vFJU3yzjT?G_u_ewOasC8hst(T~y}2IAOKj&$ z8wXLgE&$xnp=dbm^WkuQe{hNON!+1p2C&&@IN%@k3N~04iO!J_o5|S04Un#P^UBG) zSfGSt@wi2b`3Az{0;q0;!Stt|^mw{Eo6s0f5{&~tv~3rRP8G1l%vfwQvNMEHQaPn1 zO$I(`b;%aS4f^Z;z*qI=(Evz9$Ki1!+>@>zA^Ph$ZVry*aYI=q@}u+Y@&=cp(J8km z@4!)14ICRLU?!ag4pQkf?!2Tz06zz3sI!;fI3!bws#8TY0aq#~EjSq&%%BtGJI$aQ zkl*SApuW=t2LeB4oweCi zvw=trd8|kelQJuOSAY&OPU!pYoVwA1>8@*p&H<|79rr5I7AUg6nHVfe+2yqTr;tIj`%)O_<6yk< zLIOV7kNJ)w7&Vi92Z`pu{eZF2y`@sJ!;a)zHNX%v7NKfWM`1Gds9D^#D&?NMe{YnW zK8!FyGLL&;e<8qtZL!{{)s`wy5r?$wWhGhh9K^QI7%FzQ0(&HYgDY% zVw|I((D8&=jaQw&!LCHGR-O`M9Ht~ERSd+Sy3GJ?F?DL!(><%SHmPFf)@UC^qz*@4 zgfLw$Mnm8mSvf45ioj2~7c5IziGYmJvmEOh%*5?GK$-6dAt;mAQQi();(P#%A?b_# zWE{|l)72JPN8%w^RjKyLDS-m)f^pRZ2LcC$VjyD-zCMf(bn)29B}8La_AT0!P8}LL zIn_{}S3G}9utVvFwQN&76x=`Fxxs|p3$00DVwt(wQ8>y$t@DNi&UjF3uxZ9%OCApx zAK6d^R0(*{1}O7tUl=r>#%Cmpqybb4=W?AS>4zfnPBz%mXd|p30a7KQqpwI}x=Fj5 zv6}H6+nO!G;bbufGUDB2IQwA69RZ74GPxMkgGQ`>MN;KD^cSoj(tOVg5ULk&o8bw1=KENX z`lN}htWswM-^5hN*Yc5_1E|-?Qm~e|lxu;fV7Hd1*QBwT?J93x+%@Rb8;PNInRsoDpj3Z<<=q_znH4f^_ zG!8Zl5(xMMH%{#kw;}Q-nr9Z#MgzhaGu#5DaB+^DR}tNX%^V*bnV116P#GiGjLk6S z9|Dn8PsAFtqi><;KU8d{ic)aE?ZJ&YbXiPM_PoBUdQjY#cH&7+D2bAtN+3=s1P6){ zx5aD~g=sDXqF^pqZeDVweDhL)6O!u)C;z9DF+!8V1s5sPHNb7{;(Z{aNRxp7hq`Zb zpz||d+8Yai@E#iqFCw0ff+l3QBV4`<4T6^CFy=CfRvDuc2tqG2Cg9bprUjl zYTT`Kz|x*l#sMn<$<0Gjpr(ylXaW-3l(9Am^1$LwBN;(PPEYc{Cg^^t66BINp!VPh zMX$-g0hp32i$qI9?pLAKMVVG1C#+578^YzK_L$d( zEa3BG1jS^Wh_!%ju){inFkQ@@h`L%5d`+qdfJVlcB2u1}uvEB6PGubk0WvKhSl9(b zXUUA1XN+kEUg14t;XXy&#VASdywk|lp*F#<1qO>IlD^j@c6XU{F%V)m2u~b~;c;UX zU~$0xy{d_j8}yp2RsctMxLpNtH98gFC_rhTneimciS8H$YH&deO&oF!KalE}ZyWUU z{aBDwIX>PgotyF;UI}_aHYP+VOg?#kc%_W*& zR{Zk8sx1WrwnAEL!9)mY&0UR$rbfO}_CxX6rF?=K^CiH6Q`80m^Ai@BwInbfG@xhU zDV{E7p;mz9DW>leGkYW6!%-%voqsbdv{yi(c7eMy#D5@@5L_+MKYL_TMY4Qk5T?mK z2!5Hn+^8{0VX~>f*%7EaK^P0-#WS(w2$ zBY>1hIz9b#?k+)R6zYz{oOFQzgK`i3=5d&7j)vj6yu)UNPgfHjRZ3mipna1(cK$ax z1RY5@4Qy>E$lAt7=|hafv>t@|4+8oNY!o6dx^Wd3WFhBdTz57^uRwG{i%zg8#*M2~ zhFr3c2~e$S()`h?s62{Yg7^gx!0Cc47Pb;78lCS8)Jcb}WGl>ZE9n9=zBLf88`vte zP)XP_5{~Bz;{B-3ssKvV09pAssfH3#)4(7aAOL8P;fqdf&Y_D=@edEXo=h(Q)OwW9 zldz~xlVmBDjF{OU+vuGyYl63;xGQG~@sJQFB-ui$be@X=nHYnYjPh7svc{FeM6zHg zzdYzvoekZY6gV(7Sb)5R%pw*bvAjsS9=Y{DuXd>*cPi_Xd7f~ZXpucyWbh5X;5Wmm zy5xpXvOqJm1f-2&8hKCiw_pVzWhL|hFSXyb8aG4LS?eOHRF>>Q9xKxMB}bHMcrHhT zMn%`8Dvbr#uc;?oH>I9%;hTDbkPBZ9YXHH(JF@%BEHf({j7L>iC)(;YJC!W@vHIagL560s3*e%nlmIL|f2p6nHuUcWcwCVcj)6f^XxBT0*24 zBNFQS&d#FTh$qWeqQ@PCKYx@c8@mV}*nwT6S<9n{Qm`TUFsjPL(Kx&X9tQG!?qr?xYK)jlDkC9?!WhadyyxZH`zrYpu ze#jA5Xr{}a~Lpz zXX!Q)($O~jQN7CFG5Ep|s?ZRKZMn#87i~D`Me%mHHWQ!qq^9X}Noe&WSW(=nL{ILW zWOXs|tQGtrEr5+jOLNDa&_QK6sxN~2%&<`8HlT)_gshhRbVRjvX(Wvg5H`@Io+hi< z!6m0mDKBVrvLwSgW6=F$fH^Kzh8+lX7L8wz{wXwN89Z9$4wk+dKT_f`ykZWw)J)i^ znCNI)vldMD!g?A|pwR%&d6>-r1V*$>>_e_dEUC!~ah1cjUt^%rVH!@}`46^=QCLdV zSBT8>`?t%Ln#Z0Ey7N&^oBG5+$Vde45j9ak=WF3CFT#ogHB`=7L#@j&ap|k9Ci^O8 z$NUA3I2hN-`O6OxDL?rmF%oym`De{W;wU7lyK(FAS^CkEsT5=w;`RLuc0*bD)=1wSl}=#wji6gUpEims~9 zhjZjmm7+y*grp)~MmqnUvEp!1@9(>p*uAzs-DI1?v>0@Q0w+EfQ8(7y*|YI9dlfvp zHU9Fw#G(F;dx@xDwzoJ?H26pDEhdUa|8D&FtM*2Izj4TK_KzDU4i%ODBjd#FHmC^= z(Q**13PB+JZ67%62)(RZ{bl3Di3xm=fxI>Z6TfjE@ivB(`iba+J{BD&MwB*SMH)M= zNWENAFYaqzE%t-^0Mi1A1oI-I1xbO4j!zd7 zZNf{+k_xK@bUEGs;s8;(*H}_Sqs|)os1nmUIjFFZR8nd|R2YhA$RAGHKI)^C0mjak0N{x~LYH`1TRt4eh@L|-iBzd zS0A&&QE#xJuV;V_)%dT2=s+M~Lpuxwfa)_)$Rr2~nFK+B68`g(L@l`3L&u1_{QZ6= zlF53>e;DEYMaPQuHO#=62s$t*xH0{EjuXTEvknmhLE?}qyW^fXW*2m59qb~S!!C&K zO{akA9$43?A%LNg^9qB!VH(sTeEF-1vlH3uLeAcSvy<6uxSvpZI1P8IU9wM6lo`gX zmGcf2MM8Wc5$Gg|Kqmo#pviPXpm~fyxL}(xXef$=*tkk598xKTMD6}zZK974wxN({ z6SM25w*`ydk95JSi998*#)4(!Y=$E!Eb`wtNR<0+hl|VlT@1}kp;m^+Voiw0T8v&d z_YEW&zO16$hM-)Kz3snsgcwn2W8HGFj0xKo{=*YRrHOJi{y~!hd@J`anIuvT0mkw7 zn`DAoM`4-3H;p(EVBFV}#6N&+(k{U2)%fdwA)X%1cqT=re1|!cyIiWVQeddb?4%vW z**aqbh;5jLJ!4?Qq5k_vLI~~QkDDxZ2T@!wS@aWQ{Xb8J1RC!bPQkkg{*mZRT>M86Qy8D*Hhw1wKdSZ#qHj&_|gS zh?ECsf}bDki9Vay>s9$+cyI5hw;gCw${j3*P z{^m4MApG-A7gc)`V#TQkAQlt49Bq>Oz^A;3JtYa3DhMHyASd8bgl?2c(h?QfkpPwa zPfi#8^!YKr&lw`MBR&rkv;qHcB{2yqA3-NlxxhycGmua6_L+ae8RGsT9;Co9@e!7| z|K6q0*qbgEN5$w9M)*phf9TC3?Z0&<09opn%*SOu{7n^{=^kB_^sQA;kG{TA{6v&q za*-J3KRI6vLb+#tC;Iy}XNkdLu|MW4F|ABCW=XYfm{yW1^Y1xJ{M9J4OZ^@G0E_RM zv&DJb#!~<3bHpHivG`BI@$Y^^)Mk5F>K}fddW&ki`D@M-C#Y&KpC5j)?**bkeBl4` z0`Ww4hWLU;c7{jzYZr(E6Z{d3GMY^x&8F6Omx`YUlfPtz80tU$J5h=`z4<##ZLwc| zq4*V2=U*r;PL|o~Y-h9m1Ao#jK*du3k&DC-f3_#C#B=FI;$l(e|M4O*P`+7{%^Y?~ zkm=AYimJ*Sqb*fN--e_W#=>on)AT#o!>#}LcU~eMO=Q;T3w%R)srU_=vYvu$`g*BI z_^p?U{Yy)4+e4#&HqC7P!>#F^;d8zEQt_k2SA6`6W*G0VUPt(MUM2?R)=LdXy$>%F zM`Nys{ywvG<-yXGVdZv~|NMGU?4Nw4sLsB9 z?#j&DPp=ABo4hFV!d)DG@y6oJi}U`1_R9Z|ZSNMwf+hYrOTsliULv*=U-<*B5zqG_ zuRq+$*7URmCo}fQ9egF*y2`I!Ds~dT^AB1o&J2NcTrO17I{_+({80=S7x?G@Q4M3i zj_7Xr*DVu21mIp;CZ?gWTYiuAnR%_aHkfsTzx#DzU={nP{z=UN$R5lAz{(Frz^T~3 z;D+q1ck@F~1h9(zUdzL8L!bo2LX85HMSkQan1&bl18x$}WNGBnFpcc$A91tTF|pKy zpLt)=nC}40_r`xhuyDnGqRc)x!4wPU3+13)u=4IrVt>6#Ek-|i_e1+`_6U0*-}$i6AM$T8A=E-e zJ#d{)Rng-0!(9ya2&NP&FYjPj7)eF^3Ac%TLbY}*%G7u)!wzqFNnEDZLV2x*X@yve zgI|W%;X?n`mqiP+`S)Ii+Vg~8{ff8&V9P;gprOK#-2>D(?Nu>~5c|~(Vpj(XU4_PH zz7|64*YhIw8)8n-SpT=M(s#W9?9SKmNPpv-VpOQ?I1IDxEpY}27u*%$NzHadIXyEmA~bz)Z(?dBLAafn^_VSb?qZ_kJG%`qJ!Csw%0Tfz>0G&%o-b#A2Wj zY)cf7o94Jr3;HWIKXKC7>aiw^s4cn%$N%tsQHHt4K7f=d^=Et_b|@(eVi=i`mHF3y zAg+pBMyy=wy1zXY`$NC|P~6a~Aq^Fb&t#O*6(W`C+G?zT^O0DQ1V`(IF%Lkb_!?h) zEcOQ_9Ctf>wsSrfL($V!AB#J9A_u;VjH`_QL()x*rRN~YK3dnXT08d~S zgDw;C5Bo$MBJsDz|K}%AEF1hUJ`tnpX-JMp_2PX?3{9(2W0S3;DZ9qsZ0NoH*`LBU zeb&nJKLsA3_T`_$_-V2u9EKl9EIx;$|{l3nPIC0;rDyOaFfOfmb59gy*w#i>L2oz zH~dl}P>7)SHod$JFcNWsK3O z(v?*a7> zTBxnZB7{7_74jm-4dQ+UT(C`Wu?uMH&yLMr1yT3l%7w7+MbV`Z;sA(?wG%1fZ%*p{ ze+EKFkh3Cm(xn4Jmys+?=qNy6x!`!E450&0$WS$>)9w=%za|L4rAA<=e&o+7(zh!H zj$~W?!oRUdPyGzx3Pdf;k_bYT9jd}35{{$Eppp7gJR|_k`1L}HSIJnym{o)6m#(7i z+EYc`-~Mxu^WR^B5qx4XmdN%mD%K|!B2J3VoJ6cg{f~=v=$n`92Y>d-C3=69UL=3+ zF41=!gb<|dK@=knt4@tT{0_(kMl?}6MIWJej?+vTzHRA4X_8qU`8WSyOHYmENg7N8 z_=8yI5fnc}8KHT1$F_gi(rYA!0eujqvc`{>>U)fxXqyyO43s_s-Yy^m2N}=?B>OnX z6@?>;f!ikd2zl5YBsV7ft4j68k+K%W#86C1hrki62RuO;NS8aybs2tC?iZKo!wNIO zKNtI>%XFuT*IZD9&pW}@$GmF|4!WpJuM~grAM366_g^g6t4Yuxw!0eN#_qHw)iIU9ZTJ($judDQ4{xwzl1z(m<`|4F77#Byqd{kgeCE#gfa7ezg^}$pVZsEygiu z@p`L){VbK|4@&9%>MCe=>qE_Xu*|uiqFK>}juRiZ7%79zH<%0l(J6gUseA>&NU!j7 zyZ`@)dlSGYi!*L~-~W*Xi!0+ zf^q}~t8yqJ^~P&KQ4v9Dz0l%OPrPr1dgJx~{hoQ>-AzC({rdktX|wM;?;Ov}JoC(R z&Mc3qBhl8UF-5J&(zrSW04(pUdV9NVkB$PzuFk4mS1>^Wg7<~-vNfGm2YYWP zHOoDpffk5J{H5TZ`` z)O%2<9iO_Xs%jX{%hTt#hAwbvsA*f9a!O+wY z46BC63pzgBmh@Chf_cY8Jl#`u=`FK};m6P;g3Zl8t>K1ZoFGj>N|1Bd0~|OqD~E9k z_JUsO6jq379*zpTSE`{9P^VN1k_aNaS*eB;Nv8x;uwuUfziks0s<}P1H*U~<)E?7Y z4aCp<-s&v;=svI;Z?`l0sKMT&$&G!~@4P%R-ic5GIB~}@y2K8x5^3Y)Ds=!FUEzN4 z*4FN>Qq56EVL#OaUuyc1t|o8jrwoPZoBFGh@RL74?T?>f1Jt?rd1wIW@D�Rtmk~ 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%0DJ7P>8@uORh~ad9;Fh$b*iG8W;mmo%?P8vD8) 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 { Vec2 computeWorldPosition(Bone bone) { final position = _bindings.spine_point_attachment_compute_world_position(_attachment, bone._bone); final result = Vec2(_bindings.spine_vector_get_x(position), _bindings.spine_vector_get_y(position)); - _allocator.free(position); return result; } @@ -2888,7 +2885,6 @@ class Skeleton { final nativeBounds = _bindings.spine_skeleton_get_bounds(_skeleton); final bounds = Bounds(_bindings.spine_bounds_get_x(nativeBounds), _bindings.spine_bounds_get_y(nativeBounds), _bindings.spine_bounds_get_width(nativeBounds), _bindings.spine_bounds_get_height(nativeBounds)); - _allocator.free(nativeBounds); return bounds; } diff --git a/spine-flutter/src/spine_flutter.cpp b/spine-flutter/src/spine_flutter.cpp index 5265f503f..2a3909440 100644 --- a/spine-flutter/src/spine_flutter.cpp +++ b/spine-flutter/src/spine_flutter.cpp @@ -1473,8 +1473,9 @@ spine_path_constraint spine_skeleton_find_path_constraint(spine_skeleton skeleto return (spine_path_constraint) _skeleton->findPathConstraint(constraintName); } +_spine_bounds tmp_bounds; spine_bounds spine_skeleton_get_bounds(spine_skeleton skeleton) { - _spine_bounds *bounds = (_spine_bounds *) malloc(sizeof(_spine_bounds)); + _spine_bounds *bounds = &tmp_bounds; if (skeleton == nullptr) return (spine_bounds) bounds; Skeleton *_skeleton = (Skeleton *) skeleton; Vector vertices; @@ -2128,8 +2129,9 @@ void spine_bone_set_to_setup_pose(spine_bone bone) { _bone->setToSetupPose(); } +_spine_vector tmp_vector; spine_vector spine_bone_world_to_local(spine_bone bone, float worldX, float worldY) { - _spine_vector *coords = SpineExtension::calloc<_spine_vector>(1, __FILE__, __LINE__); + _spine_vector *coords = &tmp_vector; if (bone == nullptr) return (spine_vector) coords; Bone *_bone = (Bone *) bone; _bone->worldToLocal(worldX, worldY, coords->x, coords->y); @@ -2137,7 +2139,7 @@ spine_vector spine_bone_world_to_local(spine_bone bone, float worldX, float worl } spine_vector spine_bone_local_to_world(spine_bone bone, float localX, float localY) { - _spine_vector *coords = SpineExtension::calloc<_spine_vector>(1, __FILE__, __LINE__); + _spine_vector *coords = &tmp_vector; if (bone == nullptr) return (spine_vector) coords; Bone *_bone = (Bone *) bone; _bone->localToWorld(localX, localY, coords->x, coords->y); @@ -2521,7 +2523,7 @@ void spine_attachment_dispose(spine_attachment attachment) { // PointAttachment spine_vector spine_point_attachment_compute_world_position(spine_point_attachment attachment, spine_bone bone) { - _spine_vector *result = SpineExtension::calloc<_spine_vector>(1, __FILE__, __LINE__); + _spine_vector *result = &tmp_vector; if (attachment == nullptr) return (spine_vector) result; PointAttachment *_attachment = (PointAttachment *) attachment; _attachment->computeWorldPosition(*(Bone *) bone, result->x, result->y); From 64853b73bcbce53a1851b2b3b05ba8f5ab37e6d8 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 22 Sep 2023 22:13:03 +0200 Subject: [PATCH 05/20] [Flutter] Release 4.1.7 --- spine-flutter/CHANGELOG.md | 3 +++ spine-flutter/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spine-flutter/CHANGELOG.md b/spine-flutter/CHANGELOG.md index 01855ad48..60d9ac18b 100644 --- a/spine-flutter/CHANGELOG.md +++ b/spine-flutter/CHANGELOG.md @@ -1,3 +1,6 @@ +# 4.1.7 +* Fix allocation patter for temporary structs on Windows, which resulted in a hard crash without a stack trace on the native side. + # 4.1.6 * Fixed bug in path handling on Windows. diff --git a/spine-flutter/pubspec.yaml b/spine-flutter/pubspec.yaml index 9fd54a24f..6b5d7b659 100644 --- a/spine-flutter/pubspec.yaml +++ b/spine-flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: spine_flutter description: The official Spine Flutter Runtime to load, display and interact with Spine animations. -version: 4.1.6 +version: 4.1.7 homepage: https://esotericsoftware.com repository: https://github.com/esotericsoftware/spine-runtimes issue_tracker: https://github.com/esotericsoftware/spine-runtimes/issues From c0032b4ffb5c0452af8444d14c20415f5867ed4e Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Mon, 2 Oct 2023 16:47:40 +0200 Subject: [PATCH 06/20] [unity] Added manual Update(float) method at SkeletonMecanim for updateTiming ManualUpdate. Closes #2389. --- .../Runtime/spine-unity/Components/SkeletonMecanim.cs | 7 +++++++ spine-unity/Assets/Spine/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs index 8e694feec..29959d445 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs @@ -109,6 +109,13 @@ namespace Spine.Unity { UpdateAnimation(); } + ///

Manual animation update. Required when updateTiming is set to ManualUpdate. + /// Ignored parameter. + public virtual void Update (float deltaTime) { + if (!valid) return; + UpdateAnimation(); + } + protected void UpdateAnimation () { wasUpdatedAfterInit = true; diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index ac358f72b..29bbb1af5 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.1.25", + "version": "4.1.26", "unity": "2018.3", "author": { "name": "Esoteric Software", From 209acdc892158d235f381ae8edd5a7cec3a55ed9 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 3 Oct 2023 17:17:37 +0200 Subject: [PATCH 07/20] [unity] Added `UpdateTiming` mode `In Late Update` for all animated skeleton components. --- CHANGELOG.md | 1 + .../Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs | 4 ++++ .../Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs | 3 +++ .../Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs | 2 ++ .../Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs | 3 ++- spine-unity/Assets/Spine/package.json | 2 +- 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a316f9333..8212b464d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ If you are using `SkeletonRenderSeparator` and need to enable and disable the `SkeletonRenderSeparator` component at runtime, you can increase the `RenderCombinedMesh` `Reference Renderers` array by one and assign the `SkeletonRenderer` itself at the last entry after the parts renderers. Disabled `MeshRenderer` components will be skipped when combining the final mesh, so the combined mesh is automatically filled from the desired active renderers. * Timeline extension package: Added static `EditorEvent` callback to allow editor scripts to react to animation events outside of play-mode. Register to the events via `Spine.Unity.Playables.SpineAnimationStateMixerBehaviour.EditorEvent += YourCallback;`. * URP Shaders: Added `Depth Write` property to shaders `Universal Render Pipeline/Spine/Skeleton` and `Universal Render Pipeline/Spine/Skeleton Lit`. Defaults to false to maintain existing behaviour. + * Added `Animation Update` mode (called `UpdateTiming` in code) `In Late Update` for `SkeletonAnimation`, `SkeletonMecanim` and `SkeletonGraphic`. This allows you to update the `SkeletonMecanim` skeleton in the same frame that the Mecanim Animator updated its state, which happens between `Update` and `LateUpdate`. * **Breaking changes** * Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs index 29c280661..087dabd76 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs @@ -273,8 +273,12 @@ namespace Spine.Unity { } public override void LateUpdate () { + if (updateTiming == UpdateTiming.InLateUpdate && valid) + Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime); + // instantiation can happen from Update() after this component, leading to a missing Update() call. if (!wasUpdatedAfterInit) Update(0); + base.LateUpdate(); } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index d8e0cf2dc..92871e4c3 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -415,6 +415,9 @@ namespace Spine.Unity { if (freeze) return; if (updateMode != UpdateMode.FullUpdate) return; + if (updateTiming == UpdateTiming.InLateUpdate) + Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime); + PrepareInstructionsAndRenderers(); SetVerticesDirty(); // triggers Rebuild and avoids potential double-update in a single frame diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs index 29959d445..41e72bc8b 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs @@ -167,6 +167,8 @@ namespace Spine.Unity { } public override void LateUpdate () { + if (updateTiming == UpdateTiming.InLateUpdate && valid) + UpdateAnimation(); // instantiation can happen from Update() after this component, leading to a missing Update() call. if (!wasUpdatedAfterInit) Update(); base.LateUpdate(); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs index 67ecf8393..06dea7f70 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs @@ -40,7 +40,8 @@ namespace Spine.Unity { public enum UpdateTiming { ManualUpdate = 0, InUpdate, - InFixedUpdate + InFixedUpdate, + InLateUpdate } public delegate void ISkeletonAnimationDelegate (ISkeletonAnimation animated); diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index 29bbb1af5..346c68baf 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.1.26", + "version": "4.1.27", "unity": "2018.3", "author": { "name": "Esoteric Software", From cc352cf026d754bf895b1b52abe061ff0db204b4 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 4 Oct 2023 19:37:58 +0200 Subject: [PATCH 08/20] [unity] Fixed exception at SkeletonMecanim with UpdateTiming InLateUpdate. Closes #2392. --- .../Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs | 2 +- spine-unity/Assets/Spine/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs index 41e72bc8b..7ad153045 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs @@ -167,7 +167,7 @@ namespace Spine.Unity { } public override void LateUpdate () { - if (updateTiming == UpdateTiming.InLateUpdate && valid) + if (updateTiming == UpdateTiming.InLateUpdate && valid && translator != null && translator.Animator != null) UpdateAnimation(); // instantiation can happen from Update() after this component, leading to a missing Update() call. if (!wasUpdatedAfterInit) Update(); diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index 346c68baf..039bc6651 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.1.27", + "version": "4.1.28", "unity": "2018.3", "author": { "name": "Esoteric Software", From b8eda5e27bb7f5e7af86788bb247dc4bf3512a4d Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 5 Oct 2023 11:52:47 +0200 Subject: [PATCH 09/20] [godot] Use GD.Print instead of Console.WriteLine. --- .../AnimationStateListener.cs | 10 +++++----- .../examples/03-mix-and-match/MixAndMatch.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spine-godot/example-v4-csharp/examples/02-animation-state-listeners/AnimationStateListener.cs b/spine-godot/example-v4-csharp/examples/02-animation-state-listeners/AnimationStateListener.cs index 6a01413e8..33c68f184 100644 --- a/spine-godot/example-v4-csharp/examples/02-animation-state-listeners/AnimationStateListener.cs +++ b/spine-godot/example-v4-csharp/examples/02-animation-state-listeners/AnimationStateListener.cs @@ -10,28 +10,28 @@ public partial class AnimationStateListener : Node2D spineboy.AnimationStarted += (sprite, animationState, trackEntry) => { var spineTrackEntry = trackEntry as SpineTrackEntry; - Console.WriteLine("Animation started: " + spineTrackEntry.GetAnimation().GetName()); + GD.Print("Animation started: " + spineTrackEntry.GetAnimation().GetName()); }; spineboy.AnimationInterrupted += (sprite, animationState, trackEntry) => { var spineTrackEntry = trackEntry as SpineTrackEntry; - Console.WriteLine("Animation interrupted: " + spineTrackEntry.GetAnimation().GetName()); + GD.Print("Animation interrupted: " + spineTrackEntry.GetAnimation().GetName()); }; spineboy.AnimationCompleted += (sprite, animationState, trackEntry) => { var spineTrackEntry = trackEntry as SpineTrackEntry; - Console.WriteLine("Animation completed: " + spineTrackEntry.GetAnimation().GetName()); + GD.Print("Animation completed: " + spineTrackEntry.GetAnimation().GetName()); }; spineboy.AnimationDisposed += (sprite, animationState, trackEntry) => { var spineTrackEntry = trackEntry as SpineTrackEntry; - Console.WriteLine("Animation disposed: " + spineTrackEntry.GetAnimation().GetName()); + GD.Print("Animation disposed: " + spineTrackEntry.GetAnimation().GetName()); }; spineboy.AnimationEvent += (sprite, animationState, trackEntry, eventObject) => { var spineTrackEntry = trackEntry as SpineTrackEntry; var spineEvent = eventObject as SpineEvent; - Console.WriteLine("Animation event: " + spineTrackEntry.GetAnimation().GetName() + ", " + spineEvent.GetData().GetEventName()); + GD.Print("Animation event: " + spineTrackEntry.GetAnimation().GetName() + ", " + spineEvent.GetData().GetEventName()); if (spineEvent.GetData().GetEventName() == "footstep") footStepAudio.Play(); }; diff --git a/spine-godot/example-v4-csharp/examples/03-mix-and-match/MixAndMatch.cs b/spine-godot/example-v4-csharp/examples/03-mix-and-match/MixAndMatch.cs index 6420080f4..aa17392b0 100644 --- a/spine-godot/example-v4-csharp/examples/03-mix-and-match/MixAndMatch.cs +++ b/spine-godot/example-v4-csharp/examples/03-mix-and-match/MixAndMatch.cs @@ -21,7 +21,7 @@ public partial class MixAndMatch : SpineSprite foreach (SpineSkinEntry entry in custom_skin.GetAttachments()) { - Console.WriteLine(entry.GetSlotIndex() + " " + entry.GetName()); + GD.Print(entry.GetSlotIndex() + " " + entry.GetName()); } GetAnimationState().SetAnimation("dance", true, 0); From f31f12d57e89310fd6405a209f1fb18ac6f0efc3 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 5 Oct 2023 11:58:31 +0200 Subject: [PATCH 10/20] [ue] Fixes #2368, use the InName provided to FactoryCreateFile for the asset object name. --- .../Private/SpineAtlasImportFactory.cpp | 13 ++++---- .../Private/SpineEditorPlugin.cpp | 30 +++++++++++++++++++ .../Private/SpineSkeletonImportFactory.cpp | 5 +--- .../SpinePlugin/Private/SpineAtlasAsset.cpp | 4 --- .../Public/SpineSkeletonDataAsset.h | 2 +- spine-ue4/SpineUE4.uproject | 2 +- 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp index df4c681bc..b9391e101 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp @@ -28,11 +28,9 @@ *****************************************************************************/ #include "SpineAtlasImportFactory.h" -#include "AssetRegistryModule.h" #include "AssetToolsModule.h" -#include "Developer/AssetTools/Public/IAssetTools.h" -#include "PackageTools.h" #include "SpineAtlasAsset.h" +#include "Editor.h" #define LOCTEXT_NAMESPACE "Spine" @@ -56,6 +54,9 @@ bool USpineAtlasAssetFactory::FactoryCanImport(const FString &Filename) { } UObject *USpineAtlasAssetFactory::FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) { + FString FileExtension = FPaths::GetExtension(Filename); + GEditor->GetEditorSubsystem()->BroadcastAssetPreImport(this, InClass, InParent, InName, *FileExtension); + FString rawString; if (!FFileHelper::LoadFileToString(rawString, *Filename)) { return nullptr; @@ -64,13 +65,12 @@ UObject *USpineAtlasAssetFactory::FactoryCreateFile(UClass *InClass, UObject *In FString currentSourcePath, filenameNoExtension, unusedExtension; const FString longPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName()); FPaths::Split(UFactory::GetCurrentFilename(), currentSourcePath, filenameNoExtension, unusedExtension); - FString name(InName.ToString()); - name.Append("-atlas"); - USpineAtlasAsset *asset = NewObject(InParent, InClass, FName(*name), Flags); + USpineAtlasAsset *asset = NewObject(InParent, InClass, InName, Flags); asset->SetRawData(rawString); asset->SetAtlasFileName(FName(*Filename)); LoadAtlas(asset, currentSourcePath, longPackagePath); + GEditor->GetEditorSubsystem()->BroadcastAssetPostImport(this, asset); return asset; } @@ -109,6 +109,7 @@ EReimportResult::Type USpineAtlasAssetFactory::Reimport(UObject *Obj) { else Obj->MarkPackageDirty(); + GEditor->GetEditorSubsystem()->BroadcastAssetReimport(asset); return EReimportResult::Succeeded; } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp index e556a854d..c95f82d59 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp @@ -28,16 +28,46 @@ *****************************************************************************/ #include "SpineEditorPlugin.h" +#include "AssetTypeActions_Base.h" +#include "SpineAtlasAsset.h" +#include "SpineSkeletonDataAsset.h" + +class FSpineAtlasAssetTypeActions : public FAssetTypeActions_Base +{ +public: + UClass* GetSupportedClass() const override { return USpineAtlasAsset::StaticClass(); }; + FText GetName() const override { return INVTEXT("Spine atlas asset"); }; + FColor GetTypeColor() const override { return FColor::Red; }; + uint32 GetCategories() override { return EAssetTypeCategories::Misc; }; +}; + +class FSpineSkeletonDataAssetTypeActions : public FAssetTypeActions_Base +{ +public: + UClass* GetSupportedClass() const override { return USpineSkeletonDataAsset::StaticClass(); }; + FText GetName() const override { return INVTEXT("Spine data asset"); }; + FColor GetTypeColor() const override { return FColor::Red; }; + uint32 GetCategories() override { return EAssetTypeCategories::Misc; }; +}; class FSpineEditorPlugin : public ISpineEditorPlugin { virtual void StartupModule() override; virtual void ShutdownModule() override; + TSharedPtr SpineAtlasAssetTypeActions; + TSharedPtr SpineSkeletonDataAssetTypeActions; }; IMPLEMENT_MODULE(FSpineEditorPlugin, SpineEditorPlugin) void FSpineEditorPlugin::StartupModule() { + SpineAtlasAssetTypeActions = MakeShared(); + FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(SpineAtlasAssetTypeActions.ToSharedRef()); + SpineSkeletonDataAssetTypeActions = MakeShared(); + FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(SpineSkeletonDataAssetTypeActions.ToSharedRef()); } void FSpineEditorPlugin::ShutdownModule() { + if (!FModuleManager::Get().IsModuleLoaded("AssetTools")) return; + FAssetToolsModule::GetModule().Get().UnregisterAssetTypeActions(SpineAtlasAssetTypeActions.ToSharedRef()); + FAssetToolsModule::GetModule().Get().UnregisterAssetTypeActions(SpineSkeletonDataAssetTypeActions.ToSharedRef()); } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp index 3603abfc1..f92d03f95 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp @@ -76,10 +76,7 @@ void LoadAtlas(const FString &Filename, const FString &TargetPath) { } UObject *USpineSkeletonAssetFactory::FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) { - FString name(InName.ToString()); - name.Append("-data"); - - USpineSkeletonDataAsset *asset = NewObject(InParent, InClass, FName(*name), Flags); + USpineSkeletonDataAsset *asset = NewObject(InParent, InClass, InName, Flags); TArray rawData; if (!FFileHelper::LoadFileToArray(rawData, *Filename, 0)) { return nullptr; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp index 9be02dfcc..bd7f63306 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp @@ -53,10 +53,6 @@ void USpineAtlasAsset::PostInitProperties() { } void USpineAtlasAsset::GetAssetRegistryTags(TArray &OutTags) const { - if (importData) { - OutTags.Add(FAssetRegistryTag(SourceFileTagName(), importData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden)); - } - Super::GetAssetRegistryTags(OutTags); } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h index 9abc7c5df..dd23d8c9e 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h @@ -111,7 +111,7 @@ public: protected: UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSettings) - class UAssetImportData *importData; + class UAssetImportData *importData = nullptr; virtual void PostInitProperties() override; virtual void GetAssetRegistryTags(TArray &OutTags) const override; diff --git a/spine-ue4/SpineUE4.uproject b/spine-ue4/SpineUE4.uproject index 40c4435b1..1a871bfec 100644 --- a/spine-ue4/SpineUE4.uproject +++ b/spine-ue4/SpineUE4.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.2", + "EngineAssociation": "5.3", "Category": "", "Description": "", "Modules": [ From b70aa0c9fb7160189d8ee5410f9f80ee5864e119 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 5 Oct 2023 15:46:58 +0200 Subject: [PATCH 11/20] [godot] Closes #2387, reimport of skeleton and atlas files is reflect by referencing objects. --- .../spine_godot/SpineAtlasResource.cpp | 23 +++++++++++++++++++ spine-godot/spine_godot/SpineAtlasResource.h | 2 ++ .../spine_godot/SpineSkeletonDataResource.cpp | 19 +++++++++++++++ .../spine_godot/SpineSkeletonFileResource.cpp | 13 +++++++++++ .../spine_godot/SpineSkeletonFileResource.h | 2 ++ 5 files changed, 59 insertions(+) diff --git a/spine-godot/spine_godot/SpineAtlasResource.cpp b/spine-godot/spine_godot/SpineAtlasResource.cpp index c56ee34e1..3dc42fd90 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.cpp +++ b/spine-godot/spine_godot/SpineAtlasResource.cpp @@ -122,6 +122,8 @@ void SpineAtlasResource::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_path"), "", "get_source_path"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures"), "", "get_textures"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "normal_maps"), "", "get_normal_maps"); + + ADD_SIGNAL(MethodInfo("skeleton_atlas_changed")); } SpineAtlasResource::SpineAtlasResource() : atlas(nullptr), texture_loader(nullptr), normal_map_prefix("n") { @@ -230,6 +232,27 @@ Error SpineAtlasResource::save_to_file(const String &path) { return OK; } +Error SpineAtlasResource::copy_from(const Ref &p_resource) { + auto error = Resource::copy_from(p_resource); + if (error != OK) return error; + + const Ref &spineAtlas = static_cast &>(p_resource); + this->clear(); + this->atlas = spineAtlas->atlas; + spineAtlas->atlas = nullptr; + this->texture_loader = spineAtlas->texture_loader; + spineAtlas->texture_loader = nullptr; + + this->source_path = spineAtlas->source_path; + this->atlas_data = spineAtlas->atlas_data; + this->normal_map_prefix = spineAtlas->normal_map_prefix; + this->textures = spineAtlas->textures; + this->normal_maps = spineAtlas->normal_maps; + emit_signal(SNAME("skeleton_file_changed")); + + return OK; +} + #if VERSION_MAJOR > 3 RES SpineAtlasResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) { #else diff --git a/spine-godot/spine_godot/SpineAtlasResource.h b/spine-godot/spine_godot/SpineAtlasResource.h index 6ae492990..be504ae2a 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.h +++ b/spine-godot/spine_godot/SpineAtlasResource.h @@ -69,6 +69,8 @@ public: Error save_to_file(const String &path);// .spatlas + virtual Error copy_from(const Ref &p_resource); + String get_source_path(); Array get_textures(); diff --git a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp index 36a0e636e..b8bfdb21e 100644 --- a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp @@ -115,6 +115,7 @@ void SpineSkeletonDataResource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_images_path"), &SpineSkeletonDataResource::get_images_path); ClassDB::bind_method(D_METHOD("get_audio_path"), &SpineSkeletonDataResource::get_audio_path); ClassDB::bind_method(D_METHOD("get_fps"), &SpineSkeletonDataResource::get_fps); + ClassDB::bind_method(D_METHOD("update_skeleton_data"), &SpineSkeletonDataResource::update_skeleton_data); ADD_SIGNAL(MethodInfo("skeleton_data_changed")); ADD_SIGNAL(MethodInfo("_internal_spine_objects_invalidated")); @@ -190,6 +191,15 @@ bool SpineSkeletonDataResource::is_skeleton_data_loaded() const { void SpineSkeletonDataResource::set_atlas_res(const Ref &atlas) { atlas_res = atlas; + if (atlas_res.is_valid()) { +#if VERSION_MAJOR > 3 + if (!atlas_res->is_connected(SNAME("skeleton_atlas_changed"), callable_mp(this, &SpineSkeletonDataResource::update_skeleton_data))) + atlas_res->connect(SNAME("skeleton_atlas_changed"), callable_mp(this, &SpineSkeletonDataResource::update_skeleton_data)); +#else + if (!atlas_res->is_connected(SNAME("skeleton_atlas_changed"), this, SNAME("update_skeleton_data"))) + atlas_res->connect(SNAME("skeleton_atlas_changed"), this, SNAME("update_skeleton_data")); +#endif + } update_skeleton_data(); } @@ -199,6 +209,15 @@ Ref SpineSkeletonDataResource::get_atlas_res() { void SpineSkeletonDataResource::set_skeleton_file_res(const Ref &skeleton_file) { skeleton_file_res = skeleton_file; + if (skeleton_file_res.is_valid()) { +#if VERSION_MAJOR > 3 + if (!skeleton_file_res->is_connected(SNAME("skeleton_file_changed"), callable_mp(this, &SpineSkeletonDataResource::update_skeleton_data))) + skeleton_file_res->connect(SNAME("skeleton_file_changed"), callable_mp(this, &SpineSkeletonDataResource::update_skeleton_data)); +#else + if (!skeleton_file_res->is_connected(SNAME("skeleton_file_changed"), this, SNAME("update_skeleton_data"))) + skeleton_file_res->connect(SNAME("skeleton_file_changed"), this, SNAME("update_skeleton_data")); +#endif + } update_skeleton_data(); } diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp index 53462c29e..0929ba5cf 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp @@ -28,6 +28,8 @@ *****************************************************************************/ #include "SpineSkeletonFileResource.h" +#include "core/error/error_list.h" +#include "core/error/error_macros.h" #if VERSION_MAJOR > 3 #include "core/io/file_access.h" #else @@ -85,6 +87,7 @@ static char *readString(BinaryInput *input) { } void SpineSkeletonFileResource::_bind_methods() { + ADD_SIGNAL(MethodInfo("skeleton_file_changed")); } static bool checkVersion(const char *version) { @@ -157,6 +160,16 @@ Error SpineSkeletonFileResource::save_to_file(const String &path) { return OK; } +Error SpineSkeletonFileResource::copy_from(const Ref &p_resource) { + auto error = Resource::copy_from(p_resource); + if (error != OK) return error; + const Ref &spineFile = static_cast &>(p_resource); + this->json = spineFile->json; + this->binary = spineFile->binary; + emit_signal(SNAME("skeleton_file_changed")); + return OK; +} + #if VERSION_MAJOR > 3 RES SpineSkeletonFileResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) { #else diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.h b/spine-godot/spine_godot/SpineSkeletonFileResource.h index 4fedd592e..c3b35569c 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.h +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.h @@ -52,6 +52,8 @@ public: Error load_from_file(const String &path); Error save_to_file(const String &path); + + virtual Error copy_from(const Ref &p_resource); }; class SpineSkeletonFileResourceFormatLoader : public ResourceFormatLoader { From b3e8821966d8250cc324ea7f11edf63131a27aaa Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 5 Oct 2023 20:51:50 +0200 Subject: [PATCH 12/20] [godot] Fix const assignments in SpineAtlasResource::copy_from() --- spine-godot/spine_godot/SpineAtlasResource.cpp | 3 +-- spine-godot/spine_godot/SpineAtlasResource.h | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spine-godot/spine_godot/SpineAtlasResource.cpp b/spine-godot/spine_godot/SpineAtlasResource.cpp index 3dc42fd90..e588ac7af 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.cpp +++ b/spine-godot/spine_godot/SpineAtlasResource.cpp @@ -239,9 +239,8 @@ Error SpineAtlasResource::copy_from(const Ref &p_resource) { const Ref &spineAtlas = static_cast &>(p_resource); this->clear(); this->atlas = spineAtlas->atlas; - spineAtlas->atlas = nullptr; this->texture_loader = spineAtlas->texture_loader; - spineAtlas->texture_loader = nullptr; + spineAtlas->clear_native_data(); this->source_path = spineAtlas->source_path; this->atlas_data = spineAtlas->atlas_data; diff --git a/spine-godot/spine_godot/SpineAtlasResource.h b/spine-godot/spine_godot/SpineAtlasResource.h index be504ae2a..5105d9606 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.h +++ b/spine-godot/spine_godot/SpineAtlasResource.h @@ -76,6 +76,11 @@ public: Array get_textures(); Array get_normal_maps(); + + void clear_native_data() { + this->atlas = nullptr; + this->texture_loader = nullptr; + } }; class SpineAtlasResourceFormatLoader : public ResourceFormatLoader { From 5f6b637d53db7ad4020fa2c79450bc2e0583f599 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 6 Oct 2023 10:50:15 +0200 Subject: [PATCH 13/20] [godot] Make SpineAtlasResource::clear_native_data const. --- spine-godot/spine_godot/SpineAtlasResource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-godot/spine_godot/SpineAtlasResource.h b/spine-godot/spine_godot/SpineAtlasResource.h index 5105d9606..af27d6cd9 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.h +++ b/spine-godot/spine_godot/SpineAtlasResource.h @@ -77,7 +77,7 @@ public: Array get_normal_maps(); - void clear_native_data() { + const void clear_native_data() { this->atlas = nullptr; this->texture_loader = nullptr; } From 66f2bfeb610791e5cfa6f9156fd3ed4d589fcac8 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 6 Oct 2023 10:53:09 +0200 Subject: [PATCH 14/20] [godot] Update 3.5 editor build to 3.5.3-stable --- .github/workflows/spine-godot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/spine-godot.yml b/.github/workflows/spine-godot.yml index fa4cab519..cd3d9af80 100644 --- a/.github/workflows/spine-godot.yml +++ b/.github/workflows/spine-godot.yml @@ -12,8 +12,8 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_EC2_METADATA_DISABLED: true EM_VERSION: 3.1.14 - GODOT_TAG: 3.5.2-stable - GODOT_VERSION: 3.5.2.stable + GODOT_TAG: 3.5.3-stable + GODOT_VERSION: 3.5.3.stable jobs: godot-editor-windows: From 97fb551ceee266c0fcf818b12b8949b9c390752d Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 6 Oct 2023 10:54:05 +0200 Subject: [PATCH 15/20] [godot] Update 4.1 editor build to 4.1.2-stable --- .github/workflows/spine-godot-v4-all.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/spine-godot-v4-all.yml b/.github/workflows/spine-godot-v4-all.yml index 7920d48b2..bb6a41236 100644 --- a/.github/workflows/spine-godot-v4-all.yml +++ b/.github/workflows/spine-godot-v4-all.yml @@ -15,8 +15,8 @@ jobs: version: [ {"tag": "4.0.4-stable", "version": "4.0.4.stable", "mono": false}, - {"tag": "4.1.1-stable", "version": "4.1.1.stable", "mono": false}, - {"tag": "4.1.1-stable", "version": "4.1.1.stable", "mono": true}, + {"tag": "4.1.2-stable", "version": "4.1.2.stable", "mono": false}, + {"tag": "4.1.2-stable", "version": "4.1.2.stable", "mono": true}, ] uses: ./.github/workflows/spine-godot-v4.yml with: From f5caeacf0ce5ffc86a89d115a312a2185cfefd40 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 6 Oct 2023 11:15:21 +0200 Subject: [PATCH 16/20] [godot] Fix compilation errors with 3.5.3, fix const correctness. --- spine-godot/spine_godot/SpineAtlasResource.cpp | 2 ++ spine-godot/spine_godot/SpineAtlasResource.h | 8 +++++--- spine-godot/spine_godot/SpineSkeletonFileResource.cpp | 6 +++++- spine-godot/spine_godot/SpineSkeletonFileResource.h | 2 ++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/spine-godot/spine_godot/SpineAtlasResource.cpp b/spine-godot/spine_godot/SpineAtlasResource.cpp index e588ac7af..dbe3b747a 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.cpp +++ b/spine-godot/spine_godot/SpineAtlasResource.cpp @@ -232,6 +232,7 @@ Error SpineAtlasResource::save_to_file(const String &path) { return OK; } +#if VERSION_MAJOR > 3 Error SpineAtlasResource::copy_from(const Ref &p_resource) { auto error = Resource::copy_from(p_resource); if (error != OK) return error; @@ -251,6 +252,7 @@ Error SpineAtlasResource::copy_from(const Ref &p_resource) { return OK; } +#endif #if VERSION_MAJOR > 3 RES SpineAtlasResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) { diff --git a/spine-godot/spine_godot/SpineAtlasResource.h b/spine-godot/spine_godot/SpineAtlasResource.h index af27d6cd9..55807a58e 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.h +++ b/spine-godot/spine_godot/SpineAtlasResource.h @@ -45,8 +45,8 @@ class SpineAtlasResource : public Resource { protected: static void _bind_methods(); - spine::Atlas *atlas; - GodotSpineTextureLoader *texture_loader; + mutable spine::Atlas *atlas; + mutable GodotSpineTextureLoader *texture_loader; String source_path; String atlas_data; @@ -69,7 +69,9 @@ public: Error save_to_file(const String &path);// .spatlas +#if VERSION_MAJOR > 3 virtual Error copy_from(const Ref &p_resource); +#endif String get_source_path(); @@ -77,7 +79,7 @@ public: Array get_normal_maps(); - const void clear_native_data() { + void clear_native_data() const { this->atlas = nullptr; this->texture_loader = nullptr; } diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp index 0929ba5cf..7880d2a25 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp @@ -28,11 +28,13 @@ *****************************************************************************/ #include "SpineSkeletonFileResource.h" +#if VERSION_MAJOR > 3 #include "core/error/error_list.h" #include "core/error/error_macros.h" -#if VERSION_MAJOR > 3 #include "core/io/file_access.h" #else +#include "core/error_list.h" +#include "core/error_macros.h" #include "core/os/file_access.h" #endif #include @@ -160,6 +162,7 @@ Error SpineSkeletonFileResource::save_to_file(const String &path) { return OK; } +#if VERSION_MAJOR > 3 Error SpineSkeletonFileResource::copy_from(const Ref &p_resource) { auto error = Resource::copy_from(p_resource); if (error != OK) return error; @@ -169,6 +172,7 @@ Error SpineSkeletonFileResource::copy_from(const Ref &p_resource) { emit_signal(SNAME("skeleton_file_changed")); return OK; } +#endif #if VERSION_MAJOR > 3 RES SpineSkeletonFileResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) { diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.h b/spine-godot/spine_godot/SpineSkeletonFileResource.h index c3b35569c..94924d3f5 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.h +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.h @@ -53,7 +53,9 @@ public: Error save_to_file(const String &path); +#if VERSION_MAJOR > 3 virtual Error copy_from(const Ref &p_resource); +#endif }; class SpineSkeletonFileResourceFormatLoader : public ResourceFormatLoader { From 8bc1201b8306356b49808bd319a91cbbe44525e5 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 6 Oct 2023 18:32:09 +0200 Subject: [PATCH 17/20] [unity] On-demand loading of Atlas Textures now supports SkeletonGraphic. Added method `RequestLoadTexture` and TextureRequested delegate to OnDemandTextureLoader. Fixed missing loader asset name suffix. See #1890. --- .../spine-unity/Asset Types/AtlasAssetBase.cs | 5 ++ .../Asset Types/OnDemandTextureLoader.cs | 32 +++++++++++ .../spine-unity/Components/SkeletonGraphic.cs | 55 ++++++++++++++++-- spine-unity/Assets/Spine/package.json | 2 +- .../AddressablesTextureLoaderInspector.cs | 3 +- .../Runtime/AddressablesTextureLoader.cs | 8 ++- .../package.json | 2 +- .../GenericOnDemandTextureLoaderInspector.cs | 22 ++++++-- .../Runtime/GenericOnDemandTextureLoader.cs | 56 +++++++++++++++---- .../package.json | 2 +- 10 files changed, 161 insertions(+), 26 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/AtlasAssetBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/AtlasAssetBase.cs index 59b5bf5b6..4190ef2b1 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/AtlasAssetBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/AtlasAssetBase.cs @@ -71,6 +71,11 @@ namespace Spine.Unity { onDemandTextureLoader.RequestLoadMaterialTextures(material, ref overrideMaterial); } + public virtual void RequireTextureLoaded (Texture placeholderTexture, ref Texture replacementTexture, System.Action onTextureLoaded) { + if (onDemandTextureLoader) + onDemandTextureLoader.RequestLoadTexture(placeholderTexture, ref replacementTexture, onTextureLoaded); + } + [SerializeField] protected LoadingMode textureLoadingMode = LoadingMode.Normal; [SerializeField] protected OnDemandTextureLoader onDemandTextureLoader = null; #endif diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs index 16de1b725..a7b5c4613 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs @@ -51,6 +51,27 @@ namespace Spine.Unity { /// A newly created list of materials which has a placeholder texture assigned. /// True, if any placeholder texture is assigned at a Material of the associated AtlasAssetBase. public abstract bool HasPlaceholderTexturesAssigned (out List placeholderMaterials); + + /// + /// Returns whether any main texture is null at a Material of the associated AtlasAssetBase. + /// + /// A newly created list of materials which has a null main texture assigned. + /// True, if any null main texture is assigned at a Material of the associated AtlasAssetBase. + public virtual bool HasNullMainTexturesAssigned (out List nullTextureMaterials) { + nullTextureMaterials = null; + if (!atlasAsset) return false; + + bool anyNullTexture = false; + foreach (Material material in atlasAsset.Materials) { + if (material.mainTexture == null) { + anyNullTexture = true; + if (nullTextureMaterials == null) nullTextureMaterials = new List(); + nullTextureMaterials.Add(material); + } + } + return anyNullTexture; + } + /// /// Assigns previously setup target textures at each Material where placeholder textures are setup. /// True on success, false if the target texture could not be assigned at any of the @@ -60,13 +81,20 @@ namespace Spine.Unity { public abstract void EndCustomTextureLoading (); public abstract bool HasPlaceholderAssigned (Material material); public abstract void RequestLoadMaterialTextures (Material material, ref Material overrideMaterial); + public abstract void RequestLoadTexture (Texture placeholderTexture, ref Texture replacementTexture, + System.Action onTextureLoaded = null); public abstract void Clear (bool clearAtlasAsset = false); #region Event delegates public delegate void TextureLoadDelegate (OnDemandTextureLoader loader, Material material, int textureIndex); + protected event TextureLoadDelegate onTextureRequested; protected event TextureLoadDelegate onTextureLoaded; protected event TextureLoadDelegate onTextureUnloaded; + public event TextureLoadDelegate TextureRequested { + add { onTextureRequested += value; } + remove { onTextureRequested -= value; } + } public event TextureLoadDelegate TextureLoaded { add { onTextureLoaded += value; } remove { onTextureLoaded -= value; } @@ -76,6 +104,10 @@ namespace Spine.Unity { remove { onTextureUnloaded -= value; } } + protected void OnTextureRequested (Material material, int textureIndex) { + if (onTextureRequested != null) + onTextureRequested(this, material, textureIndex); + } protected void OnTextureLoaded (Material material, int textureIndex) { if (onTextureLoaded != null) onTextureLoaded(this, material, textureIndex); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index 92871e4c3..ff963b8c3 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -35,6 +35,8 @@ #define HAS_CULL_TRANSPARENT_MESH #endif +#define SPINE_OPTIONAL_ON_DEMAND_LOADING + using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; @@ -821,14 +823,22 @@ namespace Spine.Unity { else canvasRenderer.SetMesh(null); + bool assignTexture = false; if (currentInstructions.submeshInstructions.Count > 0) { Material material = currentInstructions.submeshInstructions.Items[0].material; if (material != null && baseTexture != material.mainTexture) { baseTexture = material.mainTexture; if (overrideTexture == null && assignAtCanvasRenderer) - canvasRenderer.SetTexture(this.mainTexture); + assignTexture = true; } } + +#if SPINE_OPTIONAL_ON_DEMAND_LOADING + if (Application.isPlaying) + HandleOnDemandLoading(); +#endif + if (assignTexture) + canvasRenderer.SetTexture(this.mainTexture); } protected void UpdateMaterialsMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) { @@ -899,7 +909,6 @@ namespace Spine.Unity { bool pmaVertexColors = meshGenerator.settings.pmaVertexColors; Material[] usedMaterialItems = usedMaterials.Items; Texture[] usedTextureItems = usedTextures.Items; - bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride); for (int i = 0; i < submeshCount; i++) { SubmeshInstruction submeshInstructionItem = currentInstructions.submeshInstructions.Items[i]; meshGenerator.Begin(); @@ -932,13 +941,51 @@ namespace Spine.Unity { #endif } canvasRenderer.materialCount = 1; - if (assignAtCanvasRenderer) - canvasRenderer.SetMaterial(usedMaterialItems[i], usedTextureItems[i]); } + +#if SPINE_OPTIONAL_ON_DEMAND_LOADING + if (Application.isPlaying) + HandleOnDemandLoading(); +#endif + bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride); + if (assignAtCanvasRenderer) { + for (int i = 0; i < submeshCount; i++) { + CanvasRenderer canvasRenderer = canvasRenderers[i]; + canvasRenderer.SetMaterial(usedMaterialItems[i], usedTextureItems[i]); + } + } + if (assignMeshOverrideMultiple != null) assignMeshOverrideMultiple(submeshCount, meshesItems, usedMaterialItems, usedTextureItems); } +#if SPINE_OPTIONAL_ON_DEMAND_LOADING + void HandleOnDemandLoading () { + foreach (AtlasAssetBase atlasAsset in skeletonDataAsset.atlasAssets) { + if (atlasAsset.TextureLoadingMode != AtlasAssetBase.LoadingMode.Normal) { + atlasAsset.BeginCustomTextureLoading(); + + if (!this.allowMultipleCanvasRenderers) { + Texture loadedTexture = null; + atlasAsset.RequireTextureLoaded(this.mainTexture, ref loadedTexture, null); + if (loadedTexture) + this.baseTexture = loadedTexture; + } + else { + Texture[] textureItems = usedTextures.Items; + for (int i = 0, count = usedTextures.Count; i < count; ++i) { + Texture loadedTexture = null; + atlasAsset.RequireTextureLoaded(textureItems[i], ref loadedTexture, null); + if (loadedTexture) + usedTextures.Items[i] = loadedTexture; + } + } + atlasAsset.EndCustomTextureLoading(); + } + } + } +#endif + protected void EnsureCanvasRendererCount (int targetCount) { #if UNITY_EDITOR RemoveNullCanvasRenderers(); diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index 039bc6651..a21deed83 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.1.28", + "version": "4.1.29", "unity": "2018.3", "author": { "name": "Esoteric Software", diff --git a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Editor/AddressablesTextureLoaderInspector.cs b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Editor/AddressablesTextureLoaderInspector.cs index dcb3bf2a4..f0af3df67 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Editor/AddressablesTextureLoaderInspector.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Editor/AddressablesTextureLoaderInspector.cs @@ -47,9 +47,10 @@ namespace Spine.Unity.Editor { [CustomEditor(typeof(AddressablesTextureLoader)), CanEditMultipleObjects] public class AddressablesTextureLoaderInspector : GenericTextureLoaderInspector { - public string LoaderSuffix { get { return "_Addressable"; } } public class AddressablesMethodImplementations : StaticMethodImplementations { + public override string LoaderSuffix { get { return "_Addressable"; } } + public override GenericTextureLoader GetOrCreateLoader (string loaderPath) { AddressablesTextureLoader loader = AssetDatabase.LoadAssetAtPath(loaderPath); if (loader == null) { diff --git a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs index be137bb4b..3411087f2 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs @@ -76,13 +76,17 @@ namespace Spine.Unity { [System.Serializable] public class AddressablesTextureLoader : GenericOnDemandTextureLoader { public override void CreateTextureRequest (AddressableTextureReference targetReference, - MaterialOnDemandData materialData, int textureIndex, Material materialToUpdate) { + MaterialOnDemandData materialData, int textureIndex, Material materialToUpdate, + System.Action onTextureLoaded) { + OnTextureRequested(materialToUpdate, textureIndex); materialData.textureRequests[textureIndex].handle = targetReference.assetReference.LoadAssetAsync(); materialData.textureRequests[textureIndex].handle.Completed += (obj) => { if (obj.Status == AsyncOperationStatus.Succeeded) { - materialToUpdate.mainTexture = (Texture)targetReference.assetReference.Asset; + Texture loadedTexture = (Texture)targetReference.assetReference.Asset; + materialToUpdate.mainTexture = loadedTexture; OnTextureLoaded(materialToUpdate, textureIndex); + if (onTextureLoaded != null) onTextureLoaded(loadedTexture); } }; } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json b/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json index 56662e150..f2ae6d3e0 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.addressables", "displayName": "Spine Addressables Extensions [Experimental]", "description": "This experimental plugin provides integration of Addressables on-demand texture loading for the spine-unity runtime.\nPlease be sure to test this package first and create backups of your project before using.\n\nUsage: First declare your target Material textures as addressable. Then select the SpineAtlasAsset, right-click the SpineAtlasAsset Inspector heading and select 'Add Addressables Loader'. This generates an 'AddressableTextureLoader' asset providing configuration parameters and sets up low-resolution placeholder textures which are automatically assigned in a pre-build step when building your game executable.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.1.0-preview.1", + "version": "4.1.0-preview.2", "unity": "2018.3", "author": { "name": "Esoteric Software", diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs index 9744617fc..786e1b8b7 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs @@ -37,6 +37,7 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEditor; using UnityEngine; @@ -92,7 +93,7 @@ namespace Spine.Unity.Editor { /// When set to e.g. "_Addressable", the loader asset created for /// the "Skeleton_Atlas" asset is named "Skeleton_Addressable". /// - public string LoaderSuffix { get; } + public virtual string LoaderSuffix { get { return "_Loader"; } } public abstract bool SetupOnDemandLoadingReference ( ref TargetReference targetTextureReference, Texture targetTexture); @@ -237,7 +238,7 @@ namespace Spine.Unity.Editor { #if NEWPLAYMODECALLBACKS static void OnPlaymodeChanged (PlayModeStateChange mode) { - bool assignTargetTextures = mode == PlayModeStateChange.ExitingPlayMode; + bool assignTargetTextures = mode == PlayModeStateChange.EnteredEditMode; #else static void OnPlaymodeChanged () { bool assignTargetTextures = !Application.isPlaying; @@ -259,14 +260,23 @@ namespace Spine.Unity.Editor { public static void AssignTargetTexturesAtLoader (OnDemandTextureLoader loader) { List placeholderMaterials; + List nullTextureMaterials; bool anyPlaceholdersAssigned = loader.HasPlaceholderTexturesAssigned(out placeholderMaterials); - if (anyPlaceholdersAssigned) { - Debug.Log("OnDemandTextureLoader detected placeholders assigned at one or more materials. Resetting to target textures.", loader); + bool anyMaterialNull = loader.HasNullMainTexturesAssigned(out nullTextureMaterials); + if (anyPlaceholdersAssigned || anyMaterialNull) { + Debug.Log("OnDemandTextureLoader detected placeholders assigned or null main textures at one or more materials. Resetting to target textures.", loader); AssetDatabase.StartAssetEditing(); IEnumerable modifiedMaterials; loader.AssignTargetTextures(out modifiedMaterials); - foreach (Material placeholderMaterial in placeholderMaterials) { - EditorUtility.SetDirty(placeholderMaterial); + if (placeholderMaterials != null) { + foreach (Material placeholderMaterial in placeholderMaterials) { + EditorUtility.SetDirty(placeholderMaterial); + } + } + if (nullTextureMaterials != null) { + foreach (Material nullTextureMaterial in nullTextureMaterials) { + EditorUtility.SetDirty(nullTextureMaterial); + } } AssetDatabase.StopAssetEditing(); AssetDatabase.SaveAssets(); diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs index 47a02f031..5025701b5 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs @@ -164,6 +164,7 @@ namespace Spine.Unity { if (placeholderMaterials == null) placeholderMaterials = new List(); placeholderMaterials.Add(material); } + materialIndex++; } return anyPlaceholderAssigned; } @@ -180,8 +181,7 @@ namespace Spine.Unity { atlasAsset, i + 1, targetMaterial), this); return false; } - Material ignoredArgument = null; - RequestLoadMaterialTextures(targetMaterial, ref ignoredArgument); + AssignTargetTextures(targetMaterial, i); ++i; } modifiedMaterials = atlasAsset.Materials; @@ -223,7 +223,7 @@ namespace Spine.Unity { int foundMaterialIndex = Array.FindIndex(placeholderMap, entry => entry.textures[textureIndex].placeholderTexture == currentTexture); if (foundMaterialIndex >= 0) - RequestLoadTexture(material, foundMaterialIndex, textureIndex); + RequestLoadTexture(material, foundMaterialIndex, textureIndex, null); int loadedMaterialIndex = Array.FindIndex(loadedDataAtMaterial, entry => entry.textureRequests[textureIndex].WasRequested && @@ -232,33 +232,69 @@ namespace Spine.Unity { loadedDataAtMaterial[loadedMaterialIndex].lastFrameRequested = Time.frameCount; } - protected virtual void RequestLoadTexture (Material material, int materialIndex, int textureIndex) { + public override void RequestLoadTexture (Texture placeholderTexture, ref Texture replacementTexture, + System.Action onTextureLoaded = null) { + + if (placeholderTexture == null) return; + + Texture currentTexture = placeholderTexture; + int textureIndex = 0; // Todo: currently only main texture is supported. + + int foundMaterialIndex = Array.FindIndex(placeholderMap, entry => entry.textures[textureIndex].placeholderTexture == currentTexture); + if (foundMaterialIndex >= 0) { + Material material = atlasAsset.Materials.ElementAt(foundMaterialIndex); + Texture loadedTexture = RequestLoadTexture(material, foundMaterialIndex, textureIndex, onTextureLoaded); + if (loadedTexture != null) + replacementTexture = loadedTexture; + } + + int loadedMaterialIndex = Array.FindIndex(loadedDataAtMaterial, entry => + entry.textureRequests[textureIndex].WasRequested && + entry.textureRequests[textureIndex].IsTarget(placeholderTexture)); + if (loadedMaterialIndex >= 0) + loadedDataAtMaterial[loadedMaterialIndex].lastFrameRequested = Time.frameCount; + } + + protected void AssignTargetTextures (Material material, int materialIndex) { + int textureIndex = 0; // Todo: currently only main texture is supported. + RequestLoadTexture(material, materialIndex, textureIndex, null); + } + + protected virtual Texture RequestLoadTexture (Material material, int materialIndex, int textureIndex, + System.Action onTextureLoaded) { + PlaceholderTextureMapping[] placeholderTextures = placeholderMap[materialIndex].textures; TargetReference targetReference = placeholderTextures[textureIndex].targetTextureReference; loadedDataAtMaterial[materialIndex].lastFrameRequested = Time.frameCount; #if UNITY_EDITOR if (!Application.isPlaying) { - if (targetReference.EditorTexture != null) + if (targetReference.EditorTexture != null) { material.mainTexture = targetReference.EditorTexture; - return; + if (onTextureLoaded != null) onTextureLoaded(targetReference.EditorTexture); + } + return targetReference.EditorTexture; } #endif MaterialOnDemandData materialData = loadedDataAtMaterial[materialIndex]; if (materialData.textureRequests[textureIndex].WasRequested) { Texture loadedTexture = GetAlreadyLoadedTexture(materialIndex, textureIndex); - if (loadedTexture != null) + if (loadedTexture != null) { material.mainTexture = loadedTexture; - return; + if (onTextureLoaded != null) onTextureLoaded(loadedTexture); + } + return loadedTexture; } - CreateTextureRequest(targetReference, materialData, textureIndex, material); + CreateTextureRequest(targetReference, materialData, textureIndex, material, onTextureLoaded); + return null; } public abstract Texture GetAlreadyLoadedTexture (int materialIndex, int textureIndex); public abstract void CreateTextureRequest (TargetReference targetReference, - MaterialOnDemandData materialData, int textureIndex, Material materialToUpdate); + MaterialOnDemandData materialData, int textureIndex, Material materialToUpdate, + System.Action onTextureLoaded); public virtual void UnloadUnusedTextures () { int currentFrameCount = Time.frameCount; diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json index 5682f3443..88ff77e98 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.on-demand-loading", "displayName": "Spine On-Demand Loading Extensions [Experimental]", "description": "This experimental plugin provides a generic basic implementation of on-demand texture loading for the spine-unity runtime. You might want to use the available com.esotericsoftware.spine.addressables package which depends on this package.\nPlease be sure to test this package first and create backups of your project before using.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.1.0", + "version": "4.1.0-preview.2", "unity": "2018.3", "author": { "name": "Esoteric Software", From ca06a5a7f6f04fddae91ac7959b2df54d6621a84 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 6 Oct 2023 19:18:45 +0200 Subject: [PATCH 18/20] [unity] Minor: formatting fixes. --- .../Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs | 3 +-- .../Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs | 2 +- .../Runtime/GenericOnDemandTextureLoader.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index ff963b8c3..fac6c783a 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -970,8 +970,7 @@ namespace Spine.Unity { atlasAsset.RequireTextureLoaded(this.mainTexture, ref loadedTexture, null); if (loadedTexture) this.baseTexture = loadedTexture; - } - else { + } else { Texture[] textureItems = usedTextures.Items; for (int i = 0, count = usedTextures.Count; i < count; ++i) { Texture loadedTexture = null; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs index 7ad153045..fa17e9d98 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs @@ -167,7 +167,7 @@ namespace Spine.Unity { } public override void LateUpdate () { - if (updateTiming == UpdateTiming.InLateUpdate && valid && translator != null && translator.Animator != null) + if (updateTiming == UpdateTiming.InLateUpdate && valid && translator != null && translator.Animator != null) UpdateAnimation(); // instantiation can happen from Update() after this component, leading to a missing Update() call. if (!wasUpdatedAfterInit) Update(); diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs index 5025701b5..95e33ce19 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs @@ -261,7 +261,7 @@ namespace Spine.Unity { } protected virtual Texture RequestLoadTexture (Material material, int materialIndex, int textureIndex, - System.Action onTextureLoaded) { + System.Action onTextureLoaded) { PlaceholderTextureMapping[] placeholderTextures = placeholderMap[materialIndex].textures; TargetReference targetReference = placeholderTextures[textureIndex].targetTextureReference; From f854579239fdc85c79350839cf78d8eebf98a042 Mon Sep 17 00:00:00 2001 From: Misaki Eymard <85478846+misaki-eymard@users.noreply.github.com> Date: Wed, 11 Oct 2023 18:33:02 +0900 Subject: [PATCH 19/20] Update to clarify the supported versions of spine-ue4 with Unreal Engine. (#2396) --- spine-ue4/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spine-ue4/README.md b/spine-ue4/README.md index 387442d7d..e4f765428 100644 --- a/spine-ue4/README.md +++ b/spine-ue4/README.md @@ -1,5 +1,5 @@ # spine-ue4 -The spine-ue4 runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unreal Engine 4.21+](https://www.unrealengine.com/). spine-ue4 is based on [spine-cpp](../spine-cpp). +The spine-ue4 runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unreal Engine 4.27-5.2](https://www.unrealengine.com/). spine-ue4 is based on [spine-cpp](../spine-cpp). ## Licensing @@ -34,7 +34,7 @@ See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documen ## Example ### [Please see the spine-ue4 guide for full documentation](http://esotericsoftware.com/spine-ue4) -The Spine UE4 example works on all platforms supported by Unreal Engine. The samples require Unreal Engine 4.25+. +The Spine UE4 example works on all platforms supported by Unreal Engine. The samples require Unreal Engine 4.27-5.2. 1. Copy the `spine-cpp` folder from this repositories root directory to your `Plugins/SpinePlugin/Sources/SpinePlugin/Public/` directory. You can run the `setup.bat` or `setup.sh` scripts to accomplish this. 2. Open the SpineUE4.uproject file with Unreal Editor From 087b2688db22a1f5b03c3982dfa77ef50d5a2824 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 11 Oct 2023 11:44:36 +0200 Subject: [PATCH 20/20] [c] Closes #2394, double free of sequences in mesh attachments. --- spine-c/spine-c/src/spine/MeshAttachment.c | 1 - spine-ts/spine-webgl/src/SpineCanvas.ts | 4 ++-- .../SpineEditorPlugin/Private/SpineEditorPlugin.cpp | 12 +++++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/spine-c/spine-c/src/spine/MeshAttachment.c b/spine-c/spine-c/src/spine/MeshAttachment.c index 3657900ad..e62c074c2 100644 --- a/spine-c/spine-c/src/spine/MeshAttachment.c +++ b/spine-c/spine-c/src/spine/MeshAttachment.c @@ -43,7 +43,6 @@ void _spMeshAttachment_dispose(spAttachment *attachment) { FREE(self->edges); } else _spAttachment_deinit(attachment); - if (self->sequence) FREE(self->sequence); FREE(self); } diff --git a/spine-ts/spine-webgl/src/SpineCanvas.ts b/spine-ts/spine-webgl/src/SpineCanvas.ts index ec8dc6cfa..b200ef893 100644 --- a/spine-ts/spine-webgl/src/SpineCanvas.ts +++ b/spine-ts/spine-webgl/src/SpineCanvas.ts @@ -87,7 +87,7 @@ export class SpineCanvas { update: () => { }, render: () => { }, error: () => { }, - dispose: () => { }, + dispose: () => { }, } if (config.webglConfig) config.webglConfig = { alpha: true }; @@ -131,7 +131,7 @@ export class SpineCanvas { } /** Disposes the app, so the update() and render() functions are no longer called. Calls the dispose() callback.*/ - dispose() { + dispose () { if (this.config.app.dispose) this.config.app.dispose(this); this.disposed = true; } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp index c95f82d59..3945076c6 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp @@ -31,20 +31,18 @@ #include "AssetTypeActions_Base.h" #include "SpineAtlasAsset.h" #include "SpineSkeletonDataAsset.h" - -class FSpineAtlasAssetTypeActions : public FAssetTypeActions_Base -{ + +class FSpineAtlasAssetTypeActions : public FAssetTypeActions_Base { public: - UClass* GetSupportedClass() const override { return USpineAtlasAsset::StaticClass(); }; + UClass *GetSupportedClass() const override { return USpineAtlasAsset::StaticClass(); }; FText GetName() const override { return INVTEXT("Spine atlas asset"); }; FColor GetTypeColor() const override { return FColor::Red; }; uint32 GetCategories() override { return EAssetTypeCategories::Misc; }; }; -class FSpineSkeletonDataAssetTypeActions : public FAssetTypeActions_Base -{ +class FSpineSkeletonDataAssetTypeActions : public FAssetTypeActions_Base { public: - UClass* GetSupportedClass() const override { return USpineSkeletonDataAsset::StaticClass(); }; + UClass *GetSupportedClass() const override { return USpineSkeletonDataAsset::StaticClass(); }; FText GetName() const override { return INVTEXT("Spine data asset"); }; FColor GetTypeColor() const override { return FColor::Red; }; uint32 GetCategories() override { return EAssetTypeCategories::Misc; };