[haxe] minimal scene framework, fixes for Haxe C++, clean-up of API

This commit is contained in:
Mario Zechner 2023-09-13 15:35:53 +02:00
parent 19d627592d
commit fdfc4642ad
11 changed files with 182 additions and 192 deletions

View File

@ -0,0 +1,29 @@
import openfl.utils.Assets;
import spine.SkeletonBinary;
import spine.SkeletonData;
import spine.SkeletonJson;
import spine.animation.AnimationStateData;
import spine.atlas.TextureAtlas;
import spine.attachments.AtlasAttachmentLoader;
import spine.starling.SkeletonSprite;
import starling.core.Starling;
class BasicExample extends Scene {
var loadBinary = true;
public function load():Void {
var atlas = TextureAtlas.fromAssets("assets/raptor.atlas");
var skeletondata = SkeletonData.fromAssets("assets/raptor-pro" + (loadBinary ? ".skel" : ".json"), atlas);
var animationStateData = new AnimationStateData(skeletondata);
animationStateData.defaultMix = 0.25;
var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData);
skeletonSprite.x = Starling.current.stage.stageWidth / 2;
skeletonSprite.y = Starling.current.stage.stageHeight * 0.5;
skeletonSprite.state.setAnimationByName(0, "walk", true);
addChild(skeletonSprite);
juggler.add(skeletonSprite);
}
}

View File

@ -1,29 +1,12 @@
package;
import starling.display.Image;
import haxe.io.Bytes;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import Scene.SceneManager;
import openfl.display.Sprite;
import openfl.Assets;
import openfl.geom.Rectangle;
import openfl.utils.ByteArray;
import openfl.utils.Endian;
import spine.animation.AnimationStateData;
import spine.atlas.TextureAtlas;
import spine.attachments.AtlasAttachmentLoader;
import spine.SkeletonBinary;
import spine.SkeletonData;
import spine.SkeletonJson;
import spine.starling.SkeletonAnimation;
import spine.starling.StarlingTextureLoader;
import starling.core.Starling;
import starling.events.Event;
import starling.textures.Texture;
class Main extends Sprite {
private static inline var loadBinary:Bool = false;
private var starlingSingleton:Starling;
public function new() {
@ -39,39 +22,6 @@ class Main extends Sprite {
starlingSingleton.start();
Starling.current.stage.color = 0x000000;
loadSpineAnimation();
}
private function loadSpineAnimation():Void {
var textureAtlasBitmapData:BitmapData = Assets.getBitmapData("assets/raptor.png");
var stAtlas = Assets.getText("assets/raptor.atlas");
var binaryData = Assets.getBytes("assets/raptor-pro.skel");
var jsonData = Assets.getText("assets/raptor-pro.json");
var textureAtlas = Texture.fromBitmapData(textureAtlasBitmapData);
var textureloader = new StarlingTextureLoader(textureAtlas);
var atlas = new TextureAtlas(stAtlas, textureloader);
var skeletondata:SkeletonData;
if (loadBinary) {
var skeletonBinary:SkeletonBinary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
var bytearray:ByteArray = ByteArray.fromBytes(binaryData);
bytearray.endian = Endian.BIG_ENDIAN;
skeletondata = skeletonBinary.readSkeletonData(bytearray);
} else {
var skeletonJson:SkeletonJson = new SkeletonJson(new AtlasAttachmentLoader(atlas));
skeletondata = skeletonJson.readSkeletonData(jsonData);
}
var stateData:AnimationStateData = new AnimationStateData(skeletondata);
stateData.defaultMix = 0.25;
var skeletonanimation:SkeletonAnimation = new SkeletonAnimation(skeletondata, stateData);
skeletonanimation.x = Starling.current.stage.stageWidth / 2;
skeletonanimation.y = Starling.current.stage.stageHeight * 0.5;
Starling.current.stage.addChild(skeletonanimation);
Starling.current.juggler.add(skeletonanimation);
skeletonanimation.state.setAnimationByName(0, "walk", true);
SceneManager.getInstance().switchScene(new BasicExample());
}
}

View File

@ -0,0 +1,46 @@
import starling.core.Starling;
import starling.display.Sprite;
class SceneManager {
private static var instance:SceneManager;
private var currentScene:Sprite;
private function new() {
// Singleton pattern to ensure only one instance of SceneManager
}
public static function getInstance():SceneManager {
if (instance == null) {
instance = new SceneManager();
}
return instance;
}
public function switchScene(newScene:Scene):Void {
if (currentScene != null) {
currentScene.dispose();
currentScene.removeFromParent(true);
}
currentScene = newScene;
starling.core.Starling.current.stage.addChild(currentScene);
newScene.load();
}
}
abstract class Scene extends Sprite {
var juggler = new starling.animation.Juggler();
public function new() {
super();
Starling.current.juggler.add(juggler);
}
abstract public function load():Void;
public override function dispose():Void {
juggler.purge();
Starling.current.juggler.remove(juggler);
super.dispose();
}
}

View File

@ -1,5 +1,7 @@
package spine;
import spine.attachments.AtlasAttachmentLoader;
import openfl.utils.Endian;
import spine.animation.SequenceTimeline;
import openfl.errors.ArgumentError;
import openfl.errors.Error;
@ -81,20 +83,17 @@ class SkeletonBinary {
private static inline var CURVE_STEPPED:Int = 1;
private static inline var CURVE_BEZIER:Int = 2;
public function new(attachmentLoader:AttachmentLoader = null) {
public function new(attachmentLoader:AttachmentLoader) {
this.attachmentLoader = attachmentLoader;
}
public function readSkeletonData(object:ByteArray):SkeletonData {
if (object == null)
throw new ArgumentError("Object cannot be null");
if (!Std.isOfType(object, ByteArrayData))
throw new ArgumentError("Object must be ByteArrayData");
public function readSkeletonData(bytes:ByteArray):SkeletonData {
bytes.endian = Endian.BIG_ENDIAN;
var skeletonData:SkeletonData = new SkeletonData();
skeletonData.name = null;
var input:BinaryInput = new BinaryInput(object);
var input:BinaryInput = new BinaryInput(bytes);
var lowHash:Int = input.readInt32();
var highHash:Int = input.readInt32();

View File

@ -1,5 +1,8 @@
package spine;
import spine.attachments.AtlasAttachmentLoader;
import openfl.utils.Assets;
import spine.atlas.TextureAtlas;
import openfl.errors.ArgumentError;
import openfl.Vector;
import spine.animation.Animation;
@ -27,6 +30,22 @@ class SkeletonData {
public var imagesPath:String;
public var audioPath:String;
public static function fromAssets(path:String, atlas:TextureAtlas, scale:Float = 1.0):SkeletonData {
if (StringTools.endsWith(path, ".skel")) {
var byteData = Assets.getBytes(path);
var loader = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
loader.scale = scale;
return loader.readSkeletonData(byteData);
} else if (StringTools.endsWith(path, ".json")) {
var jsonData = Assets.getText(path);
var loader = new SkeletonJson(new AtlasAttachmentLoader(atlas));
loader.scale = scale;
return loader.readSkeletonData(jsonData);
} else {
throw new SpineException("Path of skeleton data file must end with .json or .skel");
}
}
public function new() {}
// --- Bones.

View File

@ -54,28 +54,17 @@ class SkeletonJson {
private var linkedMeshes:Vector<LinkedMesh> = new Vector<LinkedMesh>();
public function new(attachmentLoader:AttachmentLoader = null) {
public function new(attachmentLoader:AttachmentLoader) {
this.attachmentLoader = attachmentLoader;
}
/** @param object A String or ByteArray. */
public function readSkeletonData(object:Object, name:String = null):SkeletonData {
if (object == null)
public function readSkeletonData(json:String):SkeletonData {
if (json == null)
throw new ArgumentError("object cannot be null.");
var root:Object;
if (Std.isOfType(object, String)) {
root = Json.parse(cast(object, String));
} else if (Std.isOfType(object, ByteArrayData)) {
root = Json.parse(cast(object, ByteArray).readUTFBytes(cast(object, ByteArray).length));
} else if (Std.isOfType(object, Dynamic)) {
root = object;
} else {
throw new ArgumentError("object must be a String, ByteArray or Object.");
}
var root = Json.parse(json);
var skeletonData:SkeletonData = new SkeletonData();
skeletonData.name = name;
// Skeleton.
var skeletonMap:Object = getString(root, "skeleton", "");
@ -1172,10 +1161,10 @@ class SkeletonJson {
}
var i:Int = value << 2;
var cx1:Float = curve[Std.string(i)];
var cy1:Float = curve[Std.string(i + 1)] * scale;
var cx2:Float = curve[Std.string(i + 2)];
var cy2:Float = curve[Std.string(i + 3)] * scale;
var cx1:Float = curve[i];
var cy1:Float = curve[i + 1] * scale;
var cx2:Float = curve[i + 2];
var cy2:Float = curve[i + 3] * scale;
timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
return bezier + 1;
}

View File

@ -0,0 +1,30 @@
package spine.atlas;
import starling.textures.Texture;
import spine.atlas.TextureAtlasRegion;
import spine.atlas.TextureAtlasPage;
import spine.atlas.TextureLoader;
class AssetsTextureLoader implements TextureLoader {
private var basePath:String;
public function new(basePath:String) {
this.basePath = basePath;
}
public function loadPage(page:TextureAtlasPage, path:String) {
var bitmapData = openfl.utils.Assets.getBitmapData(basePath + "/" + path);
if (bitmapData == null) {
throw new SpineException("Could not load atlas page texture " + basePath + "/" + path);
}
page.texture = Texture.fromBitmapData(bitmapData);
}
public function loadRegion(region:TextureAtlasRegion):Void {
region.texture = region.page.texture;
}
public function unloadPage(page:TextureAtlasPage):Void {
cast(page.texture, Texture).dispose();
}
}

View File

@ -1,5 +1,6 @@
package spine.atlas;
import openfl.utils.Assets;
import openfl.errors.ArgumentError;
import openfl.utils.ByteArray;
import openfl.utils.Dictionary;
@ -10,6 +11,17 @@ class TextureAtlas {
private var regions = new Vector<TextureAtlasRegion>();
private var textureLoader:TextureLoader;
public static function fromAssets(path:String) {
var basePath = "";
var slashIndex = path.lastIndexOf("/");
if (slashIndex != -1) {
basePath = path.substring(0, slashIndex);
}
var textureLoader = new AssetsTextureLoader(basePath);
return new TextureAtlas(Assets.getText("assets/raptor.atlas"), textureLoader);
}
/** @param object A String or ByteArray. */
public function new(object:Dynamic, textureLoader:TextureLoader) {
if (object == null) {
@ -132,7 +144,7 @@ class TextureAtlas {
field();
}
}
textureLoader.loadPage(page, line);
textureLoader.loadPage(page, page.name);
pages.push(page);
} else {
region = new TextureAtlasRegion(page, line);

View File

@ -1,32 +0,0 @@
package spine.starling;
import starling.core.Starling;
import spine.animation.AnimationState;
import spine.animation.AnimationStateData;
import spine.SkeletonData;
import starling.animation.IAnimatable;
class SkeletonAnimation extends SkeletonSprite implements IAnimatable {
public var state:AnimationState;
private var functionUpdate:Void->Void;
public function new(skeletonData:SkeletonData, stateData:AnimationStateData = null) {
super(skeletonData);
state = new AnimationState(stateData != null ? stateData : new AnimationStateData(skeletonData));
}
public function advanceTime(time:Float):Void {
var stage = Starling.current.stage;
state.update(time);
state.apply(skeleton);
skeleton.updateWorldTransform();
this.setRequiresRedraw();
if (this.functionUpdate != null)
this.functionUpdate();
}
public function setFunctionAnimationUpdate(functionUpdate:Void->Void):Void {
this.functionUpdate = functionUpdate;
}
}

View File

@ -1,37 +1,41 @@
package spine.starling;
import starling.textures.Texture;
import starling.utils.Max;
import starling.animation.IAnimatable;
import openfl.Vector;
import openfl.geom.Matrix;
import openfl.geom.Point;
import openfl.geom.Rectangle;
import openfl.Vector;
import spine.atlas.TextureAtlasRegion;
import spine.attachments.Attachment;
import spine.attachments.ClippingAttachment;
import spine.attachments.MeshAttachment;
import spine.attachments.RegionAttachment;
import spine.Bone;
import spine.Skeleton;
import spine.SkeletonClipping;
import spine.SkeletonData;
import spine.Slot;
import spine.animation.AnimationState;
import spine.animation.AnimationStateData;
import spine.attachments.Attachment;
import spine.attachments.ClippingAttachment;
import spine.attachments.MeshAttachment;
import spine.attachments.RegionAttachment;
import starling.display.BlendMode;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.rendering.IndexData;
import starling.rendering.Painter;
import starling.rendering.VertexData;
import starling.textures.Texture;
import starling.utils.Color;
import starling.utils.MatrixUtil;
import starling.utils.Max;
class SkeletonSprite extends DisplayObject {
class SkeletonSprite extends DisplayObject implements IAnimatable {
static private var _tempPoint:Point = new Point();
static private var _tempMatrix:Matrix = new Matrix();
static private var _tempVertices:Vector<Float> = new Vector<Float>();
static private var blendModes:Vector<String> = Vector.ofArray([BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN]);
private var _skeleton:Skeleton;
public var _state:AnimationState;
private var _smoothing:String = "bilinear";
private static var clipper:SkeletonClipping = new SkeletonClipping();
@ -40,11 +44,12 @@ class SkeletonSprite extends DisplayObject {
private var tempLight:spine.Color = new spine.Color(0, 0, 0);
private var tempDark:spine.Color = new spine.Color(0, 0, 0);
public function new(skeletonData:SkeletonData) {
public function new(skeletonData:SkeletonData, animationStateData:AnimationStateData = null) {
super();
Bone.yDown = true;
_skeleton = new Skeleton(skeletonData);
_skeleton.updateWorldTransform();
_state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData));
}
override public function render(painter:Painter):Void {
@ -285,6 +290,12 @@ class SkeletonSprite extends DisplayObject {
return _skeleton;
}
public var state(get, never):AnimationState;
private function get_state():AnimationState {
return _state;
}
public var smoothing(get, set):String;
private function get_smoothing():String {
@ -295,4 +306,11 @@ class SkeletonSprite extends DisplayObject {
_smoothing = smoothing;
return _smoothing;
}
public function advanceTime(time:Float):Void {
_state.update(time);
_state.apply(skeleton);
skeleton.updateWorldTransform();
this.setRequiresRedraw();
}
}

View File

@ -1,70 +0,0 @@
package spine.starling;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.errors.ArgumentError;
import openfl.utils.Object;
import spine.atlas.TextureAtlasPage;
import spine.atlas.TextureAtlasRegion;
import spine.atlas.TextureLoader;
import starling.display.Image;
import starling.textures.Texture;
class StarlingTextureLoader implements TextureLoader {
public var bitmapDatasOrTextures:Object = {};
public var singleBitmapDataOrTexture:Dynamic;
/** @param bitmaps A Bitmap or BitmapData or Texture for an atlas that has only one page, or for a multi page atlas an object where the
* key is the image path and the value is the Bitmap or BitmapData or Texture. */
public function new(bitmapsOrTextures:Dynamic) {
if (Std.isOfType(bitmapsOrTextures, BitmapData)) {
singleBitmapDataOrTexture = cast(bitmapsOrTextures, BitmapData);
return;
}
if (Std.isOfType(bitmapsOrTextures, Bitmap)) {
singleBitmapDataOrTexture = cast(bitmapsOrTextures, Bitmap).bitmapData;
return;
}
if (Std.isOfType(bitmapsOrTextures, Texture)) {
singleBitmapDataOrTexture = cast(bitmapsOrTextures, Texture);
return;
}
for (path in Reflect.fields(bitmapsOrTextures)) {
var object:Dynamic = Reflect.getProperty(bitmapsOrTextures, path);
var bitmapDataOrTexture:Dynamic;
if (Std.isOfType(object, BitmapData)) {
bitmapDataOrTexture = cast(object, BitmapData);
} else if (Std.isOfType(object, Bitmap)) {
bitmapDataOrTexture = cast(object, Bitmap).bitmapData;
} else if (Std.isOfType(object, Texture)) {
bitmapDataOrTexture = cast(object, Texture);
} else {
throw new ArgumentError("Object for path \"" + path + "\" must be a Bitmap, BitmapData or Texture: " + object);
}
bitmapDatasOrTextures[path] = bitmapDataOrTexture;
}
}
public function loadPage(page:TextureAtlasPage, path:String):Void {
var bitmapDataOrTexture:Dynamic = singleBitmapDataOrTexture != null ? singleBitmapDataOrTexture : bitmapDatasOrTextures[path];
if (bitmapDataOrTexture == null) {
throw new ArgumentError("BitmapData/Texture not found with name: " + path);
}
if (Std.isOfType(bitmapDataOrTexture, BitmapData)) {
var bitmapData:BitmapData = cast(bitmapDataOrTexture, BitmapData);
page.texture = Texture.fromBitmapData(bitmapData);
} else {
var texture:Texture = cast(bitmapDataOrTexture, Texture);
page.texture = texture;
}
}
public function loadRegion(region:TextureAtlasRegion):Void {
region.texture = region.page.texture;
}
public function unloadPage(page:TextureAtlasPage):Void {
cast(page.texture, Texture).dispose();
}
}