mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
[flutter] Fix batching, invoke bounds provider once before controller initalization, finish SkinsAndAnimationBounds
This commit is contained in:
parent
b65fef5b7a
commit
0b84fdbb32
@ -95,10 +95,16 @@ cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.png "$ROOT/spine-flutter/example/assets/"
|
||||
|
||||
cp -f ../mix-and-match/export/mix-and-match-pro.skel "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../mix-and-match/export/mix-and-match.atlas "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../mix-and-match/export/mix-and-match.png "$ROOT/spine-flutter/example/assets/"
|
||||
|
||||
cp -f ../dragon/export/dragon-ess.skel "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../dragon/export/dragon.atlas "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../dragon/export/dragon.png "$ROOT/spine-flutter/example/assets/"
|
||||
cp -f ../dragon/export/dragon_*.png "$ROOT/spine-flutter/example/assets/"
|
||||
|
||||
echo "spine-godot"
|
||||
rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.atlas
|
||||
rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.png
|
||||
|
||||
BIN
spine-flutter/example/assets/dragon-ess.skel
Normal file
BIN
spine-flutter/example/assets/dragon-ess.skel
Normal file
Binary file not shown.
123
spine-flutter/example/assets/dragon.atlas
Normal file
123
spine-flutter/example/assets/dragon.atlas
Normal file
@ -0,0 +1,123 @@
|
||||
dragon.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
back
|
||||
bounds: 564, 534, 190, 185
|
||||
chest
|
||||
bounds: 2, 645, 136, 122
|
||||
chin
|
||||
bounds: 140, 619, 214, 146
|
||||
front-toe-a
|
||||
bounds: 2, 862, 29, 50
|
||||
rotate: 90
|
||||
front-toe-b
|
||||
bounds: 467, 835, 56, 57
|
||||
rotate: 90
|
||||
head
|
||||
bounds: 756, 398, 296, 260
|
||||
rotate: 90
|
||||
left-front-leg
|
||||
bounds: 599, 834, 84, 57
|
||||
left-front-thigh
|
||||
bounds: 782, 819, 84, 72
|
||||
left-rear-leg
|
||||
bounds: 356, 558, 206, 177
|
||||
left-rear-thigh
|
||||
bounds: 216, 767, 91, 149
|
||||
rotate: 90
|
||||
left-wing01
|
||||
bounds: 2, 268, 264, 589
|
||||
rotate: 90
|
||||
left-wing02
|
||||
bounds: 2, 2, 264, 589
|
||||
rotate: 90
|
||||
right-front-leg
|
||||
bounds: 113, 769, 101, 89
|
||||
right-front-thigh
|
||||
bounds: 758, 709, 108, 108
|
||||
right-rear-leg
|
||||
bounds: 640, 721, 116, 100
|
||||
right-rear-thigh
|
||||
bounds: 367, 742, 91, 149
|
||||
rotate: 90
|
||||
right-rear-toe
|
||||
bounds: 2, 781, 109, 77
|
||||
tail01
|
||||
bounds: 868, 696, 120, 153
|
||||
rotate: 90
|
||||
tail02
|
||||
bounds: 518, 737, 95, 120
|
||||
rotate: 90
|
||||
tail03
|
||||
bounds: 868, 818, 73, 92
|
||||
rotate: 90
|
||||
tail04
|
||||
bounds: 526, 835, 56, 71
|
||||
rotate: 90
|
||||
tail05
|
||||
bounds: 406, 839, 52, 59
|
||||
rotate: 90
|
||||
tail06
|
||||
bounds: 685, 823, 95, 68
|
||||
thiagobrayner
|
||||
bounds: 54, 860, 350, 31
|
||||
|
||||
dragon_2.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
left-wing03
|
||||
bounds: 2, 534, 264, 589
|
||||
rotate: 90
|
||||
left-wing04
|
||||
bounds: 2, 268, 264, 589
|
||||
rotate: 90
|
||||
left-wing05
|
||||
bounds: 593, 209, 264, 589
|
||||
left-wing06
|
||||
bounds: 2, 2, 264, 589
|
||||
rotate: 90
|
||||
|
||||
dragon_3.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
left-wing07
|
||||
bounds: 2, 694, 264, 589
|
||||
rotate: 90
|
||||
left-wing08
|
||||
bounds: 2, 428, 264, 589
|
||||
rotate: 90
|
||||
left-wing09
|
||||
bounds: 593, 369, 264, 589
|
||||
right-wing01
|
||||
bounds: 2, 2, 365, 643
|
||||
rotate: 90
|
||||
|
||||
dragon_4.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
right-wing02
|
||||
bounds: 2, 369, 365, 643
|
||||
right-wing03
|
||||
bounds: 369, 369, 365, 643
|
||||
right-wing04
|
||||
bounds: 2, 2, 365, 643
|
||||
rotate: 90
|
||||
|
||||
dragon_5.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
right-wing05
|
||||
bounds: 2, 369, 365, 643
|
||||
right-wing06
|
||||
bounds: 369, 369, 365, 643
|
||||
right-wing07
|
||||
bounds: 2, 2, 365, 643
|
||||
rotate: 90
|
||||
|
||||
dragon_6.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
right-wing08
|
||||
bounds: 2, 2, 365, 643
|
||||
right-wing09
|
||||
bounds: 369, 2, 365, 643
|
||||
BIN
spine-flutter/example/assets/dragon.png
Normal file
BIN
spine-flutter/example/assets/dragon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 325 KiB |
BIN
spine-flutter/example/assets/dragon_2.png
Normal file
BIN
spine-flutter/example/assets/dragon_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
spine-flutter/example/assets/dragon_3.png
Normal file
BIN
spine-flutter/example/assets/dragon_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
spine-flutter/example/assets/dragon_4.png
Normal file
BIN
spine-flutter/example/assets/dragon_4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
BIN
spine-flutter/example/assets/dragon_5.png
Normal file
BIN
spine-flutter/example/assets/dragon_5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
spine-flutter/example/assets/dragon_6.png
Normal file
BIN
spine-flutter/example/assets/dragon_6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@ -107,7 +107,7 @@ class DressUpState extends State<DressUp> {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]),)
|
||||
child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@ -16,7 +16,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = SpineWidgetController(onInitialized: (controller) {
|
||||
controller.animationState.setAnimationByName(0, "walk", true);
|
||||
controller.animationState.setAnimationByName(0, "flying", true);
|
||||
});
|
||||
isPlaying = true;
|
||||
}
|
||||
@ -33,7 +33,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Play/Pause')),
|
||||
body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
|
||||
body: SpineWidget.asset("assets/dragon.atlas", "assets/dragon-ess.skel", controller, boundsProvider: SkinAndAnimationBounds(animation: "flying"),),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _togglePlay,
|
||||
child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
|
||||
|
||||
@ -70,7 +70,7 @@ class SkinsState extends State<Skins> {
|
||||
)
|
||||
),
|
||||
Expanded(
|
||||
child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]))
|
||||
child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]))
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@ -97,33 +97,54 @@ class RawBounds extends BoundsProvider {
|
||||
}
|
||||
|
||||
class SkinAndAnimationBounds extends BoundsProvider {
|
||||
final List<String> _skins;
|
||||
final String? _animation;
|
||||
final List<String> skins;
|
||||
final String? animation;
|
||||
final double stepTime;
|
||||
|
||||
SkinAndAnimationBounds(this._skins, [this._animation]);
|
||||
SkinAndAnimationBounds({List<String>? skins, this.animation, this.stepTime = 0.1}) :
|
||||
skins = skins == null || skins.isEmpty? ["default"] : skins;
|
||||
|
||||
@override
|
||||
Bounds computeBounds(SkeletonDrawable drawable) {
|
||||
var data = drawable.skeletonData;
|
||||
var oldSkin = drawable.skeleton.getSkin();
|
||||
var customSkin = Skin("custom-skin");
|
||||
for (var skinName in _skins) {
|
||||
for (var skinName in skins) {
|
||||
var skin = data.findSkin(skinName);
|
||||
if (skin == null) continue;
|
||||
customSkin.addSkin(skin);
|
||||
}
|
||||
drawable.skeleton.setSkin(customSkin);
|
||||
drawable.skeleton.setToSetupPose();
|
||||
var bounds = drawable.skeleton.getBounds();
|
||||
customSkin.dispose();
|
||||
|
||||
if (oldSkin == null) {
|
||||
drawable.skeleton.setSkinByName("");
|
||||
final animation = data.findAnimation(this.animation!);
|
||||
double minX = double.infinity;
|
||||
double minY = double.infinity;
|
||||
double maxX = double.negativeInfinity;
|
||||
double maxY = double.negativeInfinity;
|
||||
if (animation == null) {
|
||||
final bounds = drawable.skeleton.getBounds();
|
||||
minX = bounds.x;
|
||||
minY = bounds.y;
|
||||
maxX = minX + bounds.width;
|
||||
maxY = minY + bounds.height;
|
||||
} else {
|
||||
drawable.skeleton.setSkin(oldSkin);
|
||||
drawable.animationState.setAnimation(0, animation, false);
|
||||
final steps = max(animation.getDuration() / stepTime, 1.0).toInt();
|
||||
for (int i = 0; i < steps; i++) {
|
||||
drawable.update(i > 0 ? stepTime : 0);
|
||||
final bounds = drawable.skeleton.getBounds();
|
||||
minX = min(minX, bounds.x);
|
||||
minY = min(minY, bounds.y);
|
||||
maxX = max(maxX, minX + bounds.width);
|
||||
maxY = max(maxY, minY + bounds.height);
|
||||
}
|
||||
}
|
||||
customSkin.dispose();
|
||||
drawable.skeleton.setSkinByName("default");
|
||||
drawable.animationState.clearTracks();
|
||||
drawable.skeleton.setToSetupPose();
|
||||
return bounds;
|
||||
drawable.update(0);
|
||||
return Bounds(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +210,7 @@ class SpineWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SpineWidgetState extends State<SpineWidget> {
|
||||
late Bounds _computedBounds;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -201,6 +223,7 @@ class _SpineWidgetState extends State<SpineWidget> {
|
||||
}
|
||||
|
||||
void loadDrawable(SkeletonDrawable drawable) {
|
||||
_computedBounds = widget._boundsProvider.computeBounds(drawable);
|
||||
widget._controller._initialize(drawable);
|
||||
setState(() {});
|
||||
}
|
||||
@ -224,7 +247,7 @@ class _SpineWidgetState extends State<SpineWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget._controller._drawable != null) {
|
||||
return _SpineRenderObjectWidget(widget._controller._drawable!, widget._controller, widget._fit, widget._alignment, widget._boundsProvider, widget._sizedByBounds);
|
||||
return _SpineRenderObjectWidget(widget._controller._drawable!, widget._controller, widget._fit, widget._alignment, _computedBounds, widget._sizedByBounds);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
@ -242,14 +265,14 @@ class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
final SpineWidgetController _controller;
|
||||
final BoxFit _fit;
|
||||
final Alignment _alignment;
|
||||
final BoundsProvider _boundsProvider;
|
||||
final Bounds _bounds;
|
||||
final bool _sizedByBounds;
|
||||
|
||||
const _SpineRenderObjectWidget(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._boundsProvider, this._sizedByBounds);
|
||||
const _SpineRenderObjectWidget(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _SpineRenderObject(_skeletonDrawable, _controller, _fit, _alignment, _boundsProvider, _sizedByBounds);
|
||||
return _SpineRenderObject(_skeletonDrawable, _controller, _fit, _alignment, _bounds, _sizedByBounds);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -257,7 +280,7 @@ class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
renderObject.skeletonDrawable = _skeletonDrawable;
|
||||
renderObject.fit = _fit;
|
||||
renderObject.alignment = _alignment;
|
||||
renderObject.boundsProvider = _boundsProvider;
|
||||
renderObject.bounds = _bounds;
|
||||
renderObject.sizedByBounds = _sizedByBounds;
|
||||
}
|
||||
}
|
||||
@ -269,16 +292,14 @@ class _SpineRenderObject extends RenderBox {
|
||||
final Stopwatch _stopwatch = Stopwatch();
|
||||
BoxFit _fit;
|
||||
Alignment _alignment;
|
||||
BoundsProvider _boundsProvider;
|
||||
bool _sizedByBounds;
|
||||
Bounds _bounds;
|
||||
_SpineRenderObject(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._boundsProvider, this._sizedByBounds): _bounds = _boundsProvider.computeBounds(_skeletonDrawable);
|
||||
bool _sizedByBounds;
|
||||
_SpineRenderObject(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
|
||||
|
||||
set skeletonDrawable(SkeletonDrawable skeletonDrawable) {
|
||||
if (_skeletonDrawable == skeletonDrawable) return;
|
||||
|
||||
_skeletonDrawable = skeletonDrawable;
|
||||
_bounds = _boundsProvider.computeBounds(_skeletonDrawable);
|
||||
markNeedsLayout();
|
||||
markNeedsPaint();
|
||||
}
|
||||
@ -303,12 +324,11 @@ class _SpineRenderObject extends RenderBox {
|
||||
}
|
||||
}
|
||||
|
||||
BoundsProvider get boundsProvider => _boundsProvider;
|
||||
Bounds get bounds => _bounds;
|
||||
|
||||
set boundsProvider(BoundsProvider boundsProvider) {
|
||||
if (boundsProvider != _boundsProvider) {
|
||||
_boundsProvider = boundsProvider;
|
||||
_bounds = boundsProvider.computeBounds(_skeletonDrawable);
|
||||
set bounds(Bounds bounds) {
|
||||
if (bounds != _bounds) {
|
||||
_bounds = bounds;
|
||||
markNeedsLayout();
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
@ -3,20 +3,41 @@
|
||||
|
||||
using namespace spine;
|
||||
|
||||
String blendMode(spine_blend_mode mode) {
|
||||
switch(mode) {
|
||||
case SPINE_BLEND_MODE_NORMAL:
|
||||
return "normal";
|
||||
case SPINE_BLEND_MODE_ADDITIVE:
|
||||
return "additiev";
|
||||
case SPINE_BLEND_MODE_MULTIPLY:
|
||||
return "multiply";
|
||||
case SPINE_BLEND_MODE_SCREEN:
|
||||
return "screen";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int atlasLength = 0;
|
||||
void* atlasData = SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/spineboy.atlas", &atlasLength);
|
||||
void* atlasData = SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/dragon.atlas", &atlasLength);
|
||||
uint8_t* cstringAtlas = SpineExtension::calloc<uint8_t>(atlasLength + 1, __FILE__, __LINE__);
|
||||
memcpy(cstringAtlas, atlasData, atlasLength);
|
||||
int dataLength = 0;
|
||||
uint8_t* data = (uint8_t*)SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/spineboy-pro.skel", &dataLength);
|
||||
uint8_t* data = (uint8_t*)SpineExtension::getInstance()->_readFile("/Users/badlogic/workspaces/spine-runtimes/spine-flutter/example/assets/dragon-ess.skel", &dataLength);
|
||||
|
||||
spine_atlas atlas = spine_atlas_load((const utf8*)cstringAtlas);
|
||||
Vector<String> atlasPages;
|
||||
for (int i = 0, n = spine_atlas_get_num_image_paths(atlas); i < n; i++) {
|
||||
atlasPages.add(spine_atlas_get_image_path(atlas, i));
|
||||
}
|
||||
|
||||
spine_skeleton_data_result result = spine_skeleton_data_load_binary(atlas, data, dataLength);
|
||||
spine_skeleton_drawable drawable = spine_skeleton_drawable_create(spine_skeleton_data_result_get_data(result));
|
||||
spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
|
||||
spine_skeleton_update_world_transform(skeleton);
|
||||
spine_render_command cmd = spine_skeleton_drawable_render(drawable);
|
||||
|
||||
int batchId = 0;
|
||||
while(cmd) {
|
||||
int numVertices = spine_render_command_get_num_vertices(cmd);
|
||||
int numIndices = spine_render_command_get_num_indices(cmd);
|
||||
float *positions = spine_render_command_get_positions(cmd);
|
||||
@ -24,6 +45,11 @@ int main(int argc, char** argv) {
|
||||
int32_t *colors = spine_render_command_get_colors(cmd);
|
||||
uint16_t *indices = spine_render_command_get_indices(cmd);
|
||||
String str;
|
||||
int atlasPage = spine_render_command_get_atlas_page(cmd);
|
||||
str.append(atlasPages[atlasPage]);
|
||||
str.append("\n");
|
||||
str.append(blendMode(spine_render_command_get_blend_mode(cmd)));
|
||||
str.append("\n");
|
||||
str.append(numVertices);
|
||||
str.append("\n");
|
||||
str.append(numIndices);
|
||||
@ -44,7 +70,14 @@ int main(int argc, char** argv) {
|
||||
str.append(indices[i]);
|
||||
str.append("\n");
|
||||
}
|
||||
FILE *file = fopen("/Users/badlogic/Desktop/spineboy.mesh", "w");
|
||||
String outputFile = "";
|
||||
outputFile.append("/Users/badlogic/Desktop/dragon-");
|
||||
outputFile.append(batchId);
|
||||
outputFile.append(".mesh");
|
||||
FILE *file = fopen(outputFile.buffer(), "w");
|
||||
fwrite(str.buffer(), str.length(), 1, file);
|
||||
fclose(file);
|
||||
batchId++;
|
||||
cmd = spine_render_command_get_next(cmd);
|
||||
}
|
||||
}
|
||||
@ -657,7 +657,7 @@ void spine_skeleton_drawable_dispose(spine_skeleton_drawable drawable) {
|
||||
}
|
||||
|
||||
static _spine_render_command *batch_sub_commands(BlockAllocator &allocator, Vector<_spine_render_command*> &commands, int first, int last, int numVertices, int numIndices) {
|
||||
_spine_render_command *batched = spine_render_command_create(allocator, numVertices, numIndices, commands[0]->blendMode, commands[0]->atlasPage);
|
||||
_spine_render_command *batched = spine_render_command_create(allocator, numVertices, numIndices, commands[first]->blendMode, commands[first]->atlasPage);
|
||||
float *positions = batched->positions;
|
||||
float *uvs = batched->uvs;
|
||||
int32_t *colors = batched->colors;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user