From 311a71901eb2f2290cfd7dfbe670c83725860851 Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Wed, 17 Jul 2024 18:43:32 +0200 Subject: [PATCH] [haxe][flixel] WIP fix alpha and color tinting not working on meshes. --- .../example/assets/export/skeleton.atlas | 6 ++ .../example/assets/export/skeleton.json | 1 + spine-haxe/example/assets/export/skeleton.png | Bin 0 -> 2254 bytes .../src/flixelExamples/BasicExample.hx | 35 ++++--- .../example/src/flixelExamples/FlixelState.hx | 15 ++- .../src/flixelExamples/MixAndMatchExample.hx | 90 ++++++++++++++++++ .../src/flixelExamples/SequenceExample.hx | 39 ++++++++ .../spine/flixel/FlixelTextureLoader.hx | 7 +- .../spine-haxe/spine/flixel/SkeletonMesh.hx | 7 +- .../spine-haxe/spine/flixel/SkeletonSprite.hx | 52 ++++++++-- 10 files changed, 215 insertions(+), 37 deletions(-) create mode 100644 spine-haxe/example/assets/export/skeleton.atlas create mode 100644 spine-haxe/example/assets/export/skeleton.json create mode 100644 spine-haxe/example/assets/export/skeleton.png create mode 100644 spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx create mode 100644 spine-haxe/example/src/flixelExamples/SequenceExample.hx diff --git a/spine-haxe/example/assets/export/skeleton.atlas b/spine-haxe/example/assets/export/skeleton.atlas new file mode 100644 index 000000000..a21ccd6e0 --- /dev/null +++ b/spine-haxe/example/assets/export/skeleton.atlas @@ -0,0 +1,6 @@ +skeleton.png +size:404,404 +filter:Linear,Linear +pma:true +square +bounds:2,2,400,400 diff --git a/spine-haxe/example/assets/export/skeleton.json b/spine-haxe/example/assets/export/skeleton.json new file mode 100644 index 000000000..2bcdde15f --- /dev/null +++ b/spine-haxe/example/assets/export/skeleton.json @@ -0,0 +1 @@ +{"skeleton":{"hash":"gAzm+5Vz+z4","spine":"4.2.33","x":-320.5,"y":-381.5,"width":1036,"height":1194,"images":"./images","audio":"./audio"},"bones":[{"name":"root"}],"slots":[{"name":"square","bone":"root","attachment":"square"},{"name":"square2","bone":"root","attachment":"square"},{"name":"square3","bone":"root","attachment":"square"},{"name":"square4","bone":"root","color":"ff7676ff","attachment":"square"},{"name":"square5","bone":"root","attachment":"square"},{"name":"square6","bone":"root","attachment":"square"}],"skins":[{"name":"default","attachments":{"square":{"square":{"x":-2.5,"y":-5.5,"width":400,"height":400}},"square2":{"square":{"color":"ff0000ff","x":227.5,"y":160.5,"width":400,"height":400}},"square3":{"square":{"x":397.5,"y":306.5,"width":400,"height":400}},"square4":{"square":{"x":199.5,"y":612.5,"width":400,"height":400}},"square5":{"square":{"x":-120.5,"y":442.5,"width":400,"height":400}},"square6":{"square":{"color":"f700c0ff","x":515.5,"y":-181.5,"width":400,"height":400}}}}],"animations":{"animation":{}}} \ No newline at end of file diff --git a/spine-haxe/example/assets/export/skeleton.png b/spine-haxe/example/assets/export/skeleton.png new file mode 100644 index 0000000000000000000000000000000000000000..295a6b55bc3f1d9b32f9e63deb5923979ffe66ef GIT binary patch literal 2254 zcmeAS@N?(olHy`uVBq!ia0y~yV4MQN9Be=lp`%w8FfedT^>lFzsfc@f)ljg7f#HaQ z%A>cjiCyn67xIeCo_Te`;|VIBla`z>joUBHFz=N=4{L*GeUb))&o5zT#)M1rCAb(S z|2q=EFyo7Q8`FU$_J$%1YJWQyG8nuZRXQ31qaiRF0;3@?8UmvsFd70xh5)d&p|bbB a{DDcnw=X61YzDSd7(8A5T-G@yGywp9ctO$t literal 0 HcmV?d00001 diff --git a/spine-haxe/example/src/flixelExamples/BasicExample.hx b/spine-haxe/example/src/flixelExamples/BasicExample.hx index 9e3a74461..b3d2e1fa4 100644 --- a/spine-haxe/example/src/flixelExamples/BasicExample.hx +++ b/spine-haxe/example/src/flixelExamples/BasicExample.hx @@ -29,6 +29,7 @@ package flixelExamples; +import flixel.ui.FlxButton; import flixel.FlxG; import spine.flixel.SkeletonSprite; import spine.flixel.FlixelTextureLoader; @@ -43,15 +44,20 @@ class BasicExample extends FlxState { var skeletonSprite:SkeletonSprite; override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new SequenceExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + var atlas = new TextureAtlas(Assets.getText("assets/raptor.atlas"), new FlixelTextureLoader("assets/raptor-pro.atlas")); - var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/raptor-pro.skel") : Assets.getText("assets/raptor-pro.json"), atlas); + var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/raptor-pro.skel") : Assets.getText("assets/raptor-pro.json"), atlas, .25); var animationStateData = new AnimationStateData(skeletondata); animationStateData.defaultMix = 0.25; - skeletonSprite = new SkeletonSprite(skeletondata, animationStateData, .25); - // var bounds = skeletonSprite.skeleton.getBounds(); - // skeletonSprite.scale = Starling.current.stage.stageWidth / bounds.width * 0.5; - skeletonSprite.setPosition(.5 * FlxG.width, .5 * FlxG.height); + skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); + skeletonSprite.setPosition( + .5 * FlxG.width - skeletonSprite.width / 2, + .5 * FlxG.height - skeletonSprite.height / 2 + ); skeletonSprite.state.setAnimationByName(0, "walk", true); @@ -66,31 +72,22 @@ class BasicExample extends FlxState { trace("loaded"); } - // public function onTouch(e:TouchEvent) { - // var touch = e.getTouch(this); - // if (touch != null && touch.phase == TouchPhase.ENDED) { - // SceneManager.getInstance().switchScene(new SequenceExample()); - // } - // } - override public function update(elapsed:Float):Void { if (FlxG.keys.anyPressed([RIGHT])) { - skeletonSprite.x += 250 * elapsed; + skeletonSprite.x += 15; } - if (FlxG.keys.anyPressed([LEFT])) { - skeletonSprite.x -= 250 * elapsed; + skeletonSprite.x -= 15; } - if (FlxG.keys.anyPressed([UP])) { - skeletonSprite.y += 250 * elapsed; + skeletonSprite.y += 15; } - if (FlxG.keys.anyPressed([DOWN])) { - skeletonSprite.y -= 250 * elapsed; + skeletonSprite.y -= 15; } super.update(elapsed); } + } diff --git a/spine-haxe/example/src/flixelExamples/FlixelState.hx b/spine-haxe/example/src/flixelExamples/FlixelState.hx index b47453a90..cf72dad43 100644 --- a/spine-haxe/example/src/flixelExamples/FlixelState.hx +++ b/spine-haxe/example/src/flixelExamples/FlixelState.hx @@ -30,6 +30,10 @@ class FlixelState extends FlxState override public function create():Void { + FlxG.switchState(new MixAndMatchExample()); + // FlxG.switchState(new SequenceExample()); + // FlxG.switchState(new BasicExample()); + FlxG.cameras.bgColor = 0xffa1b2b0; // setting speed of spineboy (450 is the speed to not let him slide) @@ -50,10 +54,8 @@ class FlixelState extends FlxState myText.alignment = CENTER; group.add(myText); - var button = new FlxButton(0, 0, "Click me", () -> { - FlxG.switchState(new BasicExample()); - }); - button.screenCenter(); + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new BasicExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); add(button); // creating a sprite for the floor @@ -61,9 +63,6 @@ class FlixelState extends FlxState floor.loadGraphic(FlxGraphic.fromRectangle(FlxG.width, FlxG.height - 100, 0xff822f02)); floor.y = FlxG.height - 100; add(floor); - var button = new FlxButton("Click me", () -> { - trace("clicked"); - }); // loading spineboy var atlas = new TextureAtlas(Assets.getText("assets/spineboy.atlas"), new FlixelTextureLoader("assets/spineboy.atlas")); @@ -119,7 +118,7 @@ class FlixelState extends FlxState // adding spineboy to the stage add(spineSprite); - FlxG.debugger.visible = !FlxG.debugger.visible; + // FlxG.debugger.visible = !FlxG.debugger.visible; // debug ui // FlxG.debugger.visible = true; // FlxG.debugger.drawDebug = true; diff --git a/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx b/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx new file mode 100644 index 000000000..bd28a32d6 --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/MixAndMatchExample.hx @@ -0,0 +1,90 @@ +package flixelExamples; + + +import spine.Skin; +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class MixAndMatchExample extends FlxState { + var loadBinary = false; + // var loadBinary = true; + + var skeletonSprite:SkeletonSprite; + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new BasicExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + // var atlas = new TextureAtlas(Assets.getText("assets/export/skeleton.atlas"), new FlixelTextureLoader("assets/export/skeleton.atlas")); + // var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/export/skeleton.skel") : Assets.getText("assets/export/skeleton.json"), atlas, .5); + + var atlas = new TextureAtlas(Assets.getText("assets/mix-and-match.atlas"), new FlixelTextureLoader("assets/mix-and-match.atlas")); + var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/mix-and-match-pro.skel") : Assets.getText("assets/mix-and-match-pro.json"), atlas, .5); + var animationStateData = new AnimationStateData(data); + animationStateData.defaultMix = 0.25; + + skeletonSprite = new SkeletonSprite(data, animationStateData); + // var customSkin = new Skin("custom"); + // var skinBase = data.findSkin("skin-base"); + // customSkin.addSkin(skinBase); + // customSkin.addSkin(data.findSkin("nose/short")); + // customSkin.addSkin(data.findSkin("eyelids/girly")); + // customSkin.addSkin(data.findSkin("eyes/violet")); + // customSkin.addSkin(data.findSkin("hair/brown")); + // customSkin.addSkin(data.findSkin("clothes/hoodie-orange")); + // customSkin.addSkin(data.findSkin("legs/pants-jeans")); + // customSkin.addSkin(data.findSkin("accessories/bag")); + // customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")); + // skeletonSprite.skeleton.skin = customSkin; + + camera.zoom = .5; + skeletonSprite.skeleton.skinName = "full-skins/girl"; + skeletonSprite.setBoundingBox(); + + skeletonSprite.afterUpdateWorldTransforms = s -> { + for(slot in s.skeleton.slots) { + if (slot.data.name != "hair-patch") { + // slot.attachment = null; + } + } + } + + // skeletonSprite.y -=300; + + skeletonSprite.screenCenter(); + // skeletonSprite.state.setAnimationByName(0, "dance", true); + add(skeletonSprite); + + // FlxG.debugger.visible = !FlxG.debugger.visible; + // FlxG.debugger.track(skeletonSprite); + // FlxG.debugger.track(camera); + FlxG.debugger.drawDebug = true; + super.create(); + } + + override public function update(elapsed:Float):Void + { + if (FlxG.keys.anyPressed([RIGHT])) { + skeletonSprite.x += 15; + } + if (FlxG.keys.anyPressed([LEFT])) { + skeletonSprite.x -= 15; + } + if (FlxG.keys.anyPressed([UP])) { + skeletonSprite.y -= 15; + } + if (FlxG.keys.anyPressed([DOWN])) { + skeletonSprite.y += 15; + } + + super.update(elapsed); + } + +} diff --git a/spine-haxe/example/src/flixelExamples/SequenceExample.hx b/spine-haxe/example/src/flixelExamples/SequenceExample.hx new file mode 100644 index 000000000..066d673fc --- /dev/null +++ b/spine-haxe/example/src/flixelExamples/SequenceExample.hx @@ -0,0 +1,39 @@ +package flixelExamples; + + +import flixel.ui.FlxButton; +import flixel.FlxG; +import spine.flixel.SkeletonSprite; +import spine.flixel.FlixelTextureLoader; +import flixel.FlxState; +import openfl.utils.Assets; +import spine.SkeletonData; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; + +class SequenceExample extends FlxState { + var loadBinary = true; + + var skeletonSprite:SkeletonSprite; + override public function create():Void { + var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(new MixAndMatchExample())); + button.setPosition(FlxG.width * .75, FlxG.height / 10); + add(button); + + var atlas = new TextureAtlas(Assets.getText("assets/dragon.atlas"), new FlixelTextureLoader("assets/dragon.atlas")); + var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/dragon-ess.skel") : Assets.getText("assets/dragon-.json"), atlas, .5); + var animationStateData = new AnimationStateData(skeletondata); + animationStateData.defaultMix = 0.25; + + skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); + skeletonSprite.screenCenter(); + skeletonSprite.y -= 100; + + skeletonSprite.state.setAnimationByName(0, "flying", true); + FlxG.debugger.visible = !FlxG.debugger.visible; + FlxG.debugger.track(skeletonSprite); + add(skeletonSprite); + super.create(); + } + +} diff --git a/spine-haxe/spine-haxe/spine/flixel/FlixelTextureLoader.hx b/spine-haxe/spine-haxe/spine/flixel/FlixelTextureLoader.hx index b16fdf35b..b672b1b14 100644 --- a/spine-haxe/spine-haxe/spine/flixel/FlixelTextureLoader.hx +++ b/spine-haxe/spine-haxe/spine/flixel/FlixelTextureLoader.hx @@ -29,6 +29,7 @@ package spine.flixel; +import flixel.graphics.FlxGraphic; import flixel.FlxG; import spine.atlas.TextureAtlasPage; import spine.atlas.TextureAtlasRegion; @@ -53,7 +54,11 @@ class FlixelTextureLoader implements TextureLoader if (bitmapData == null) { throw new SpineException("Could not load atlas page texture " + basePath + "/" + path); } - page.texture = SpineTexture.from(bitmapData); + var texture:FlxGraphic = SpineTexture.from(bitmapData); + // TODO: reset this value to true when destroy skeleton + // this is needed for sequence, otherwise the previous texture would be detroyed + texture.destroyOnNoUse = false; + page.texture = texture; } public function loadRegion(region:TextureAtlasRegion):Void { diff --git a/spine-haxe/spine-haxe/spine/flixel/SkeletonMesh.hx b/spine-haxe/spine-haxe/spine/flixel/SkeletonMesh.hx index ad93a55ad..a6a6c62ee 100644 --- a/spine-haxe/spine-haxe/spine/flixel/SkeletonMesh.hx +++ b/spine-haxe/spine-haxe/spine/flixel/SkeletonMesh.hx @@ -33,7 +33,10 @@ import flixel.system.FlxAssets.FlxGraphicAsset; import flixel.FlxStrip; class SkeletonMesh extends FlxStrip { - public function new(texture:FlxGraphicAsset) { - super(0, 0, texture); + public function new(/*texture:FlxGraphicAsset*/) { + super(); + // graphic = texture; } + + } diff --git a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx index c4b0b6274..6f6f0d893 100644 --- a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx @@ -56,11 +56,17 @@ class SkeletonSprite extends FlxObject skeleton.updateWorldTransform(Physics.update); state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData)); + setBoundingBox(); + } + + public function setBoundingBox() { var bounds = skeleton.getBounds(); - width = bounds.width; - height = bounds.height; - offsetX = bounds.width / 2; - offsetY = bounds.height; + if (bounds.width > 0 && bounds.height > 0) { + width = bounds.width; + height = bounds.height; + offsetX = bounds.width / 2; + offsetY = bounds.height; + } } override public function destroy():Void @@ -131,6 +137,7 @@ class SkeletonSprite extends FlxObject region.computeWorldVertices(slot, worldVertices, 0, clippedVertexSize); mesh = getFlixelMeshFromRendererAttachment(region); + mesh.graphic = region.region.texture; triangles = QUAD_INDICES; uvs = region.uvs; attachmentColor = region.color; @@ -144,6 +151,7 @@ class SkeletonSprite extends FlxObject meshAttachment.computeWorldVertices(slot, 0, meshAttachment.worldVerticesLength, worldVertices, 0, clippedVertexSize); mesh = getFlixelMeshFromRendererAttachment(meshAttachment); + mesh.graphic = meshAttachment.region.texture; triangles = meshAttachment.triangles; uvs = meshAttachment.uvs; attachmentColor = meshAttachment.color; @@ -158,11 +166,41 @@ class SkeletonSprite extends FlxObject if (mesh != null) { - mesh.color.setRGBFloat( + // cannot use directly mesh.color.setRGBFloat otherwise the setter won't be called and transfor color not set + // trace('${slot.data.name}'); + // trace(skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat); + // trace(skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat); + // trace(skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat); + // trace('${mesh.color}\n'); + var _tmpColor:Int; + // _tmpColor = FlxColor.fromRGBFloat(1,1,1,1); + + + _tmpColor = FlxColor.fromRGBFloat( skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat, skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat, - skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat + skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat, + 1 ); + + + // // if (slot.data.name == "hair-patch") { + // if (slot.data.name == "square2") { + // _tmpColor = FlxColor.fromRGBFloat( + // skeleton.color.r * slot.color.r * attachmentColor.r * color.redFloat, + // skeleton.color.g * slot.color.g * attachmentColor.g * color.greenFloat, + // skeleton.color.b * slot.color.b * attachmentColor.b * color.blueFloat, + // 1 + // ); + // // continue; + // // trace('${mesh.color.red} | ${mesh.color.green} | ${mesh.color.blue} | ${mesh.color.alpha}'); + // } else { + // // trace(slot.data.name); + // _tmpColor = FlxColor.fromRGBFloat(1,1,1,1); + // } + // trace('${slot.data.name}\t${mesh.color}'); + + mesh.color = _tmpColor; mesh.alpha = skeleton.color.a * slot.color.a * attachmentColor.a * alpha; if (clipper.isClipping()) { @@ -200,7 +238,7 @@ class SkeletonSprite extends FlxObject private function getFlixelMeshFromRendererAttachment(region: RenderedAttachment) { if (region.rendererObject == null) { - var skeletonMesh = new SkeletonMesh(region.region.texture); + var skeletonMesh = new SkeletonMesh(); region.rendererObject = skeletonMesh; skeletonMesh.exists = false; _meshes.push(skeletonMesh);