mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
669 lines
20 KiB
Haxe
669 lines
20 KiB
Haxe
/******************************************************************************
|
|
* Spine Runtimes License Agreement
|
|
* Last updated July 28, 2023. Replaces all prior versions.
|
|
*
|
|
* Copyright (c) 2013-2023, 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 lime.math.Rectangle;
|
|
import haxe.ds.StringMap;
|
|
import spine.attachments.Attachment;
|
|
import spine.attachments.MeshAttachment;
|
|
import spine.attachments.PathAttachment;
|
|
import spine.attachments.RegionAttachment;
|
|
|
|
class Skeleton {
|
|
private var _data:SkeletonData;
|
|
|
|
public var bones:Array<Bone>;
|
|
public var slots:Array<Slot>; // Setup pose draw order.
|
|
public var drawOrder:Array<Slot>;
|
|
public var ikConstraints:Array<IkConstraint>;
|
|
public var transformConstraints:Array<TransformConstraint>;
|
|
public var pathConstraints:Array<PathConstraint>;
|
|
public var physicsConstraints:Array<PhysicsConstraint>;
|
|
|
|
private var _updateCache:Array<Updatable> = new Array<Updatable>();
|
|
private var _skin:Skin;
|
|
|
|
public var color:Color = new Color(1, 1, 1, 1);
|
|
public var scaleX:Float = 1;
|
|
public var scaleY:Float = 1;
|
|
public var x:Float = 0;
|
|
public var y:Float = 0;
|
|
public var time:Float = 0;
|
|
|
|
public function new(data:SkeletonData) {
|
|
if (data == null) {
|
|
throw new SpineException("data cannot be null.");
|
|
}
|
|
_data = data;
|
|
|
|
bones = new Array<Bone>();
|
|
for (boneData in data.bones) {
|
|
var bone:Bone;
|
|
if (boneData.parent == null) {
|
|
bone = new Bone(boneData, this, null);
|
|
} else {
|
|
var parent:Bone = bones[boneData.parent.index];
|
|
bone = new Bone(boneData, this, parent);
|
|
parent.children.push(bone);
|
|
}
|
|
bones.push(bone);
|
|
}
|
|
|
|
slots = new Array<Slot>();
|
|
drawOrder = new Array<Slot>();
|
|
for (slotData in data.slots) {
|
|
var bone = bones[slotData.boneData.index];
|
|
var slot:Slot = new Slot(slotData, bone);
|
|
slots.push(slot);
|
|
drawOrder.push(slot);
|
|
}
|
|
|
|
ikConstraints = new Array<IkConstraint>();
|
|
for (ikConstraintData in data.ikConstraints) {
|
|
ikConstraints.push(new IkConstraint(ikConstraintData, this));
|
|
}
|
|
|
|
transformConstraints = new Array<TransformConstraint>();
|
|
for (transformConstraintData in data.transformConstraints) {
|
|
transformConstraints.push(new TransformConstraint(transformConstraintData, this));
|
|
}
|
|
|
|
pathConstraints = new Array<PathConstraint>();
|
|
for (pathConstraintData in data.pathConstraints) {
|
|
pathConstraints.push(new PathConstraint(pathConstraintData, this));
|
|
}
|
|
|
|
physicsConstraints = new Array<PhysicsConstraint>();
|
|
for (physicConstraintData in data.physicsConstraints) {
|
|
physicsConstraints.push(new PhysicsConstraint(physicConstraintData, this));
|
|
}
|
|
|
|
updateCache();
|
|
}
|
|
|
|
/** Caches information about bones and constraints. Must be called if bones, constraints, or weighted path attachments are
|
|
* added or removed. */
|
|
public function updateCache():Void {
|
|
_updateCache.resize(0);
|
|
|
|
for (bone in bones) {
|
|
bone.sorted = bone.data.skinRequired;
|
|
bone.active = !bone.sorted;
|
|
}
|
|
|
|
if (skin != null) {
|
|
var skinBones:Array<BoneData> = skin.bones;
|
|
for (i in 0...skin.bones.length) {
|
|
var bone:Bone = bones[skinBones[i].index];
|
|
do {
|
|
bone.sorted = false;
|
|
bone.active = true;
|
|
bone = bone.parent;
|
|
} while (bone != null);
|
|
}
|
|
}
|
|
|
|
// IK first, lowest hierarchy depth first.
|
|
var ikCount:Int = ikConstraints.length;
|
|
var transformCount:Int = transformConstraints.length;
|
|
var pathCount:Int = pathConstraints.length;
|
|
var physicCount:Int = physicsConstraints.length;
|
|
var constraintCount:Int = ikCount + transformCount + pathCount + physicCount;
|
|
|
|
var continueOuter:Bool;
|
|
for (i in 0...constraintCount) {
|
|
continueOuter = false;
|
|
for (ikConstraint in ikConstraints) {
|
|
if (ikConstraint.data.order == i) {
|
|
sortIkConstraint(ikConstraint);
|
|
continueOuter = true;
|
|
break;
|
|
}
|
|
}
|
|
if (continueOuter)
|
|
continue;
|
|
for (transformConstraint in transformConstraints) {
|
|
if (transformConstraint.data.order == i) {
|
|
sortTransformConstraint(transformConstraint);
|
|
continueOuter = true;
|
|
break;
|
|
}
|
|
}
|
|
if (continueOuter)
|
|
continue;
|
|
for (pathConstraint in pathConstraints) {
|
|
if (pathConstraint.data.order == i) {
|
|
sortPathConstraint(pathConstraint);
|
|
break;
|
|
}
|
|
}
|
|
if (continueOuter)
|
|
continue;
|
|
for (physicConstraint in physicsConstraints) {
|
|
if (physicConstraint.data.order == i) {
|
|
sortPhysicsConstraint(physicConstraint);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (bone in bones) {
|
|
sortBone(bone);
|
|
}
|
|
}
|
|
|
|
private static function contains(list:Array<ConstraintData>, element:ConstraintData):Bool {
|
|
return list.indexOf(element) != -1;
|
|
}
|
|
|
|
private function sortIkConstraint(constraint:IkConstraint):Void {
|
|
constraint.active = constraint.target.isActive()
|
|
&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
|
|
if (!constraint.active)
|
|
return;
|
|
|
|
var target:Bone = constraint.target;
|
|
sortBone(target);
|
|
|
|
var constrained:Array<Bone> = constraint.bones;
|
|
var parent:Bone = constrained[0];
|
|
sortBone(parent);
|
|
|
|
if (constrained.length == 1) {
|
|
_updateCache.push(constraint);
|
|
sortReset(parent.children);
|
|
} else {
|
|
var child:Bone = constrained[constrained.length - 1];
|
|
sortBone(child);
|
|
|
|
_updateCache.push(constraint);
|
|
|
|
sortReset(parent.children);
|
|
child.sorted = true;
|
|
}
|
|
|
|
_updateCache.push(constraint);
|
|
|
|
sortReset(parent.children);
|
|
constrained[constrained.length - 1].sorted = true;
|
|
}
|
|
|
|
private function sortPathConstraint(constraint:PathConstraint):Void {
|
|
constraint.active = constraint.target.bone.isActive()
|
|
&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
|
|
if (!constraint.active)
|
|
return;
|
|
|
|
var slot:Slot = constraint.target;
|
|
var slotIndex:Int = slot.data.index;
|
|
var slotBone:Bone = slot.bone;
|
|
if (skin != null)
|
|
sortPathConstraintAttachment(skin, slotIndex, slotBone);
|
|
if (data.defaultSkin != null && data.defaultSkin != skin) {
|
|
sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
|
|
}
|
|
for (i in 0...data.skins.length) {
|
|
sortPathConstraintAttachment(data.skins[i], slotIndex, slotBone);
|
|
}
|
|
|
|
var attachment:Attachment = slot.attachment;
|
|
if (Std.isOfType(attachment, PathAttachment))
|
|
sortPathConstraintAttachment2(attachment, slotBone);
|
|
|
|
var constrainedBones:Array<Bone> = constraint.bones;
|
|
for (bone in constrainedBones) {
|
|
sortBone(bone);
|
|
}
|
|
|
|
_updateCache.push(constraint);
|
|
|
|
for (bone in constrainedBones) {
|
|
sortReset(bone.children);
|
|
}
|
|
for (bone in constrainedBones) {
|
|
bone.sorted = true;
|
|
}
|
|
}
|
|
|
|
private function sortTransformConstraint(constraint:TransformConstraint):Void {
|
|
constraint.active = constraint.target.isActive()
|
|
&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
|
|
if (!constraint.active)
|
|
return;
|
|
|
|
sortBone(constraint.target);
|
|
|
|
var constrainedBones:Array<Bone> = constraint.bones;
|
|
if (constraint.data.local) {
|
|
for (bone in constrainedBones) {
|
|
sortBone(bone.parent);
|
|
sortBone(bone);
|
|
}
|
|
} else {
|
|
for (bone in constrainedBones) {
|
|
sortBone(bone);
|
|
}
|
|
}
|
|
|
|
_updateCache.push(constraint);
|
|
for (bone in constrainedBones) {
|
|
sortReset(bone.children);
|
|
}
|
|
for (bone in constrainedBones) {
|
|
bone.sorted = true;
|
|
}
|
|
}
|
|
|
|
private function sortPathConstraintAttachment(skin:Skin, slotIndex:Int, slotBone:Bone):Void {
|
|
var dict:StringMap<Attachment> = skin.attachments[slotIndex];
|
|
if (dict != null) {
|
|
for (attachment in dict.keyValueIterator()) {
|
|
sortPathConstraintAttachment2(attachment.value, slotBone);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function sortPathConstraintAttachment2(attachment:Attachment, slotBone:Bone):Void {
|
|
var pathAttachment:PathAttachment = cast(attachment, PathAttachment);
|
|
if (pathAttachment == null)
|
|
return;
|
|
var pathBones:Array<Int> = pathAttachment.bones;
|
|
if (pathBones == null) {
|
|
sortBone(slotBone);
|
|
} else {
|
|
var i:Int = 0;
|
|
var n:Int = pathBones.length;
|
|
while (i < n) {
|
|
var nn:Int = pathBones[i++];
|
|
nn += i;
|
|
while (i < nn) {
|
|
sortBone(bones[pathBones[i++]]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function sortPhysicsConstraint (constraint: PhysicsConstraint) {
|
|
var bone:Bone = constraint.bone;
|
|
constraint.active = bone.active && (!constraint.data.skinRequired || (skin != null && contains(skin.constraints, constraint.data)));
|
|
if (!constraint.active) return;
|
|
|
|
sortBone(bone);
|
|
|
|
_updateCache.push(constraint);
|
|
|
|
sortReset(bone.children);
|
|
bone.sorted = true;
|
|
}
|
|
|
|
private function sortBone(bone:Bone):Void {
|
|
if (bone.sorted)
|
|
return;
|
|
var parent:Bone = bone.parent;
|
|
if (parent != null)
|
|
sortBone(parent);
|
|
bone.sorted = true;
|
|
_updateCache.push(bone);
|
|
}
|
|
|
|
private function sortReset(bones:Array<Bone>):Void {
|
|
for (bone in bones) {
|
|
if (!bone.active)
|
|
continue;
|
|
if (bone.sorted)
|
|
sortReset(bone.children);
|
|
bone.sorted = false;
|
|
}
|
|
}
|
|
|
|
/** Updates the world transform for each bone and applies constraints. */
|
|
public function updateWorldTransform(physics:Physics):Void {
|
|
if (physics == null) throw new SpineException("physics is undefined");
|
|
for (bone in bones) {
|
|
bone.ax = bone.x;
|
|
bone.ay = bone.y;
|
|
bone.arotation = bone.rotation;
|
|
bone.ascaleX = bone.scaleX;
|
|
bone.ascaleY = bone.scaleY;
|
|
bone.ashearX = bone.shearX;
|
|
bone.ashearY = bone.shearY;
|
|
}
|
|
|
|
for (updatable in _updateCache) {
|
|
updatable.update(physics);
|
|
}
|
|
}
|
|
|
|
public function updateWorldTransformWith(physics:Physics, parent:Bone):Void {
|
|
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
|
var rootBone:Bone = rootBone;
|
|
var pa:Float = parent.a,
|
|
pb:Float = parent.b,
|
|
pc:Float = parent.c,
|
|
pd:Float = parent.d;
|
|
rootBone.worldX = pa * x + pb * y + parent.worldX;
|
|
rootBone.worldY = pc * x + pd * y + parent.worldY;
|
|
|
|
var rx:Float = (rootBone.rotation + rootBone.shearX) * MathUtils.degRad;
|
|
var ry:Float = (rootBone.rotation + 90 + rootBone.shearY) * MathUtils.degRad;
|
|
var la:Float = Math.cos(rx) * rootBone.scaleX;
|
|
var lb:Float = Math.cos(ry) * rootBone.scaleY;
|
|
var lc:Float = Math.sin(rx) * rootBone.scaleX;
|
|
var ld:Float = Math.sin(ry) * rootBone.scaleY;
|
|
rootBone.a = (pa * la + pb * lc) * scaleX;
|
|
rootBone.b = (pa * lb + pb * ld) * scaleX;
|
|
rootBone.c = (pc * la + pd * lc) * scaleY;
|
|
rootBone.d = (pc * lb + pd * ld) * scaleY;
|
|
|
|
// Update everything except root bone.
|
|
for (updatable in _updateCache) {
|
|
if (updatable != rootBone)
|
|
updatable.update(physics);
|
|
}
|
|
}
|
|
|
|
/** Sets the bones, constraints, and slots to their setup pose values. */
|
|
public function setToSetupPose():Void {
|
|
setBonesToSetupPose();
|
|
setSlotsToSetupPose();
|
|
}
|
|
|
|
/** Sets the bones and constraints to their setup pose values. */
|
|
public function setBonesToSetupPose():Void {
|
|
for (bone in this.bones) bone.setToSetupPose();
|
|
for (constraint in this.ikConstraints) constraint.setToSetupPose();
|
|
for (constraint in this.transformConstraints) constraint.setToSetupPose();
|
|
for (constraint in this.pathConstraints) constraint.setToSetupPose();
|
|
for (constraint in this.physicsConstraints) constraint.setToSetupPose();
|
|
}
|
|
|
|
public function setSlotsToSetupPose():Void {
|
|
var i:Int = 0;
|
|
for (slot in slots) {
|
|
drawOrder[i++] = slot;
|
|
slot.setToSetupPose();
|
|
}
|
|
}
|
|
|
|
public var data(get, never):SkeletonData;
|
|
|
|
private function get_data():SkeletonData {
|
|
return _data;
|
|
}
|
|
|
|
public var getUpdateCache(get, never):Array<Updatable>;
|
|
|
|
private function get_getUpdateCache():Array<Updatable> {
|
|
return _updateCache;
|
|
}
|
|
|
|
public var rootBone(get, never):Bone;
|
|
|
|
private function get_rootBone():Bone {
|
|
if (bones.length == 0)
|
|
return null;
|
|
return bones[0];
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findBone(boneName:String):Bone {
|
|
if (boneName == null) {
|
|
throw new SpineException("boneName cannot be null.");
|
|
}
|
|
for (bone in bones) {
|
|
if (bone.data.name == boneName)
|
|
return bone;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** @return -1 if the bone was not found. */
|
|
public function findBoneIndex(boneName:String):Int {
|
|
if (boneName == null) {
|
|
throw new SpineException("boneName cannot be null.");
|
|
}
|
|
var i:Int = 0;
|
|
for (bone in bones) {
|
|
if (bone.data.name == boneName)
|
|
return i;
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findSlot(slotName:String):Slot {
|
|
if (slotName == null) {
|
|
throw new SpineException("slotName cannot be null.");
|
|
}
|
|
for (slot in slots) {
|
|
if (slot.data.name == slotName)
|
|
return slot;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public var skinName(get, set):String;
|
|
|
|
private function set_skinName(skinName:String):String {
|
|
var skin:Skin = data.findSkin(skinName);
|
|
if (skin == null)
|
|
throw new SpineException("Skin not found: " + skinName);
|
|
this.skin = skin;
|
|
return skinName;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
private function get_skinName():String {
|
|
return _skin == null ? null : _skin.name;
|
|
}
|
|
|
|
public var skin(get, set):Skin;
|
|
|
|
private function get_skin():Skin {
|
|
return _skin;
|
|
}
|
|
|
|
/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
|
|
* Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was
|
|
* no old skin, each slot's setup mode attachment is attached from the new skin.
|
|
* @param newSkin May be null. */
|
|
private function set_skin(newSkin:Skin):Skin {
|
|
if (newSkin == _skin)
|
|
return null;
|
|
if (newSkin != null) {
|
|
if (skin != null) {
|
|
newSkin.attachAll(this, skin);
|
|
} else {
|
|
var i:Int = 0;
|
|
for (slot in slots) {
|
|
var name:String = slot.data.attachmentName;
|
|
if (name != null) {
|
|
var attachment:Attachment = newSkin.getAttachment(i, name);
|
|
if (attachment != null)
|
|
slot.attachment = attachment;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
_skin = newSkin;
|
|
updateCache();
|
|
return _skin;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function getAttachmentForSlotName(slotName:String, attachmentName:String):Attachment {
|
|
return getAttachmentForSlotIndex(data.findSlot(slotName).index, attachmentName);
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function getAttachmentForSlotIndex(slotIndex:Int, attachmentName:String):Attachment {
|
|
if (attachmentName == null)
|
|
throw new SpineException("attachmentName cannot be null.");
|
|
if (skin != null) {
|
|
var attachment:Attachment = skin.getAttachment(slotIndex, attachmentName);
|
|
if (attachment != null)
|
|
return attachment;
|
|
}
|
|
if (data.defaultSkin != null)
|
|
return data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
|
return null;
|
|
}
|
|
|
|
/** @param attachmentName May be null. */
|
|
public function setAttachment(slotName:String, attachmentName:String):Void {
|
|
if (slotName == null)
|
|
throw new SpineException("slotName cannot be null.");
|
|
var i:Int = 0;
|
|
for (slot in slots) {
|
|
if (slot.data.name == slotName) {
|
|
var attachment:Attachment = null;
|
|
if (attachmentName != null) {
|
|
attachment = getAttachmentForSlotIndex(i, attachmentName);
|
|
if (attachment == null) {
|
|
throw new SpineException("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
|
}
|
|
}
|
|
slot.attachment = attachment;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
throw new SpineException("Slot not found: " + slotName);
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findIkConstraint(constraintName:String):IkConstraint {
|
|
if (constraintName == null)
|
|
throw new SpineException("constraintName cannot be null.");
|
|
for (ikConstraint in ikConstraints) {
|
|
if (ikConstraint.data.name == constraintName)
|
|
return ikConstraint;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findTransformConstraint(constraintName:String):TransformConstraint {
|
|
if (constraintName == null)
|
|
throw new SpineException("constraintName cannot be null.");
|
|
for (transformConstraint in transformConstraints) {
|
|
if (transformConstraint.data.name == constraintName)
|
|
return transformConstraint;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findPathConstraint(constraintName:String):PathConstraint {
|
|
if (constraintName == null)
|
|
throw new SpineException("constraintName cannot be null.");
|
|
for (pathConstraint in pathConstraints) {
|
|
if (pathConstraint.data.name == constraintName)
|
|
return pathConstraint;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** @return May be null. */
|
|
public function findPhysicsConstraint(constraintName:String):PhysicsConstraint {
|
|
if (constraintName == null)
|
|
throw new SpineException("constraintName cannot be null.");
|
|
for (physicsConstraint in physicsConstraints) {
|
|
if (physicsConstraint.data.name == constraintName)
|
|
return physicsConstraint;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function toString():String {
|
|
return _data.name != null ? _data.name : "Skeleton?";
|
|
}
|
|
|
|
private var _tempVertices = new Array<Float>();
|
|
private var _bounds = new Rectangle();
|
|
|
|
public function getBounds():Rectangle {
|
|
var minX:Float = Math.POSITIVE_INFINITY;
|
|
var minY:Float = Math.POSITIVE_INFINITY;
|
|
var maxX:Float = Math.NEGATIVE_INFINITY;
|
|
var maxY:Float = Math.NEGATIVE_INFINITY;
|
|
for (slot in drawOrder) {
|
|
var verticesLength:Int = 0;
|
|
var vertices:Array<Float> = null;
|
|
var attachment:Attachment = slot.attachment;
|
|
if (Std.isOfType(attachment, RegionAttachment)) {
|
|
verticesLength = 8;
|
|
_tempVertices.resize(verticesLength);
|
|
vertices = _tempVertices;
|
|
cast(attachment, RegionAttachment).computeWorldVertices(slot, vertices, 0, 2);
|
|
} else if (Std.isOfType(attachment, MeshAttachment)) {
|
|
var mesh:MeshAttachment = cast(attachment, MeshAttachment);
|
|
verticesLength = mesh.worldVerticesLength;
|
|
_tempVertices.resize(verticesLength);
|
|
vertices = _tempVertices;
|
|
mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
|
|
}
|
|
if (vertices != null) {
|
|
var ii:Int = 0;
|
|
var nn:Int = vertices.length;
|
|
while (ii < nn) {
|
|
var x:Float = vertices[ii], y:Float = vertices[ii + 1];
|
|
minX = Math.min(minX, x);
|
|
minY = Math.min(minY, y);
|
|
maxX = Math.max(maxX, x);
|
|
maxY = Math.max(maxY, y);
|
|
ii += 2;
|
|
}
|
|
}
|
|
}
|
|
_bounds.x = minX;
|
|
_bounds.y = minY;
|
|
_bounds.width = maxX - minX;
|
|
_bounds.height = maxY - minY;
|
|
return _bounds;
|
|
}
|
|
|
|
public function update (delta:Float):Void {
|
|
time += delta;
|
|
}
|
|
|
|
public function physicsTranslate (x:Float, y:Float):Void {
|
|
for (physicsConstraint in physicsConstraints)
|
|
physicsConstraint.translate(x, y);
|
|
}
|
|
|
|
public function physicsRotate (x:Float, y:Float, degrees:Float):Void {
|
|
for (physicsConstraint in physicsConstraints)
|
|
physicsConstraint.rotate(x, y, degrees);
|
|
}
|
|
}
|