2025-10-30 09:49:40 +01:00

226 lines
8.1 KiB
Haxe

/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine;
import haxe.io.Bytes;
import spine.animation.Animation;
import spine.atlas.TextureAtlas;
import spine.attachments.AtlasAttachmentLoader;
/** Stores the setup pose and all of the stateless data for a skeleton.
*
*
* @see https://esotericsoftware.com/spine-runtime-architecture#Data-objects Data objects in the Spine Runtimes
* Guide. */
class SkeletonData {
/** The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been
* set. */
public var name:String = null;
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
public final bones = new Array<BoneData>(); // Ordered parents first.
/** The skeleton's slots in the setup pose draw order. */
public final slots = new Array<SlotData>(); // Setup pose draw order.
/** All skins, including the default skin. */
public final skins = new Array<Skin>();
/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine.
*
* See Skeleton#getAttachment(int, String). */
public var defaultSkin:Skin = null;
/** The skeleton's events. */
public var events = new Array<EventData>();
/** The skeleton's animations. */
public var animations = new Array<Animation>();
/** The skeleton's constraints. */
public var constraints = new Array<ConstraintData<Dynamic, Dynamic>>();
/** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */
public var x:Float = 0;
/** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */
public var y:Float = 0;
/** The width of the skeleton's axis aligned bounding box in the setup pose. */
public var width:Float = 0;
/** The height of the skeleton's axis aligned bounding box in the setup pose. */
public var height:Float = 0;
/** Baseline scale factor for applying physics and other effects based on distance to non-scalable properties, such as angle or
* scale. Default is 100. */
public var referenceScale:Float = 100;
/** The Spine version used to export the skeleton data, or null. */
public var version:String;
/** The skeleton data hash. This value will change if any of the skeleton data has changed. */
public var hash:String;
// Nonessential.
/** The dopesheet FPS in Spine, or zero if nonessential data was not exported. */
public var fps:Float = 30;
/** The path to the images directory as defined in Spine, or null if nonessential data was not exported. */
public var imagesPath:String;
/** The path to the audio directory as defined in Spine, or null if nonessential data was not exported. */
public var audioPath:String;
public static function from(data:Dynamic, atlas:TextureAtlas, scale:Float = 1.0):SkeletonData {
if (Std.isOfType(data, Bytes)) {
var loader = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
loader.scale = scale;
return loader.readSkeletonData(cast(data, Bytes));
} else if (Std.isOfType(data, String)) {
var loader = new SkeletonJson(new AtlasAttachmentLoader(atlas));
loader.scale = scale;
return loader.readSkeletonData(cast(data, String));
} else {
throw new SpineException("Data must either be a String (.json) or Bytes (.skel) instance.");
}
}
public function new() {}
// --- Bones.
/** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
* multiple times.
* @param boneName The name of the bone to find.
* @return May be null. */
public function findBone(boneName:String):BoneData {
if (boneName == null)
throw new SpineException("boneName cannot be null.");
for (bone in bones)
if (bone.name == boneName)
return bone;
return null;
}
/** Finds the index of a bone by comparing each bone's name.
* @param boneName The name of the bone to find.
* @return -1 if the bone was not found. */
public function findBoneIndex(boneName:String):Int {
if (boneName == null)
throw new SpineException("boneName cannot be null.");
for (i in 0...bones.length) {
if (bones[i].name == boneName)
return i;
}
return -1;
}
// --- Slots.
/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
* multiple times.
* @param slotName The name of the slot to find.
* @return May be null. */
public function findSlot(slotName:String):SlotData {
if (slotName == null)
throw new SpineException("slotName cannot be null.");
for (slot in slots)
if (slot.name == slotName)
return slot;
return null;
}
// --- Skins.
/** Finds a skin by comparing each skin's name. It is more efficient to cache the results of this method than to call it
* multiple times.
* @param skinName The name of the skin to find.
* @return May be null. */
public function findSkin(skinName:String):Skin {
if (skinName == null)
throw new SpineException("skinName cannot be null.");
for (skin in skins)
if (skin.name == skinName)
return skin;
return null;
}
// --- Events.
/** Finds an event by comparing each events's name. It is more efficient to cache the results of this method than to call it
* multiple times.
* @param eventName The name of the event to find.
* @return May be null. */
public function findEvent(eventName:String):EventData {
if (eventName == null)
throw new SpineException("eventName cannot be null.");
for (eventData in events)
if (eventData.name == eventName)
return eventData;
return null;
}
// --- Animations.
/** Finds an animation by comparing each animation's name. It is more efficient to cache the results of this method than to
* call it multiple times.
* @param animationName The name of the animation to find.
* @return May be null. */
public function findAnimation(animationName:String):Animation {
if (animationName == null)
throw new SpineException("animationName cannot be null.");
for (animation in animations)
if (animation.name == animationName)
return animation;
return null;
}
// --- Constraints.
public function findConstraint<T:ConstraintData<Dynamic, Dynamic>>(constraintName:String, type:Class<T>):T {
if (constraintName == null)
throw new SpineException("constraintName cannot be null.");
if (type == null)
throw new SpineException("type cannot be null.");
for (constraint in constraints) {
if (Std.isOfType(constraint, type) && constraint.name == constraintName)
return Std.downcast(constraint, type);
}
return null;
}
public function toString():String {
return name;
}
}