1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4  Copyright (c) 2014 Shengxiang Chen (Nero Chan)
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * The main namespace of Spine, all classes, functions, properties and constants of Spine are defined in this namespace
 29  * @namespace
 30  * @name sp
 31  */
 32 var sp = sp || {};
 33 
 34 /**
 35  * The vertex index of spine.
 36  * @constant
 37  * @type {{X1: number, Y1: number, X2: number, Y2: number, X3: number, Y3: number, X4: number, Y4: number}}
 38  */
 39 sp.VERTEX_INDEX = {
 40     X1: 0,
 41     Y1: 1,
 42     X2: 2,
 43     Y2: 3,
 44     X3: 4,
 45     Y3: 5,
 46     X4: 6,
 47     Y4: 7
 48 };
 49 
 50 /**
 51  * The attachment type of spine.  It contains three type: REGION(0), BOUNDING_BOX(1), REGION_SEQUENCE(2) and MESH(2).
 52  * @constant
 53  * @type {{REGION: number, BOUNDING_BOX: number, REGION_SEQUENCE: number, MESH: number}}
 54  */
 55 sp.ATTACHMENT_TYPE = {
 56     REGION: 0,
 57     BOUNDING_BOX: 1,
 58     REGION_SEQUENCE: 2,
 59     MESH: 2
 60 };
 61 
 62 /**
 63  * <p>
 64  *     The skeleton of Spine.                                                                          <br/>
 65  *     Skeleton has a reference to a SkeletonData and stores the state for skeleton instance,
 66  *     which consists of the current pose's bone SRT, slot colors, and which slot attachments are visible.           <br/>
 67  *     Multiple skeletons can use the same SkeletonData (which includes all animations, skins, and attachments).     <br/>
 68  * </p>
 69  * @class
 70  * @extends cc.Node
 71  */
 72 sp.Skeleton = cc.Node.extend(/** @lends sp.Skeleton# */{
 73     _skeleton: null,
 74     _rootBone: null,
 75     _timeScale: 1,
 76     _debugSlots: false,
 77     _debugBones: false,
 78     _premultipliedAlpha: false,
 79     _ownsSkeletonData: null,
 80     _atlas: null,
 81     _blendFunc: null,
 82 
 83     /**
 84      * The constructor of sp.Skeleton. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
 85      */
 86     ctor:function(skeletonDataFile, atlasFile, scale){
 87         cc.Node.prototype.ctor.call(this);
 88         this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
 89 
 90         if(arguments.length === 0)
 91             this.init();
 92         else
 93             this.initWithArgs(skeletonDataFile, atlasFile, scale);
 94     },
 95 
 96     _createRenderCmd:function () {
 97         if(cc._renderType === cc._RENDER_TYPE_CANVAS)
 98             return new sp.Skeleton.CanvasRenderCmd(this);
 99         else
100             return new sp.Skeleton.WebGLRenderCmd(this);
101     },
102 
103     /**
104      * Initializes a sp.Skeleton. please do not call this function by yourself, you should pass the parameters to constructor to initialize it.
105      */
106     init: function () {
107         cc.Node.prototype.init.call(this);
108         this.setOpacityModifyRGB(true);
109         this._blendFunc.src = cc.ONE;
110         this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
111         this.scheduleUpdate();
112     },
113 
114     /**
115      * Sets whether open debug solots.
116      * @param {boolean} enable true to open, false to close.
117      */
118     setDebugSolots:function(enable){
119         this._debugSlots = enable;
120     },
121 
122     /**
123      * Sets whether open debug bones.
124      * @param {boolean} enable
125      */
126     setDebugBones:function(enable){
127         this._debugBones = enable;
128     },
129 
130     /**
131      * Sets the time scale of sp.Skeleton.
132      * @param {Number} v
133      */
134     setTimeScale:function(v){
135         this._timeScale = v;
136     },
137 
138     /**
139      * Initializes sp.Skeleton with Data.
140      * @param {spine.SkeletonData|String} skeletonDataFile
141      * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
142      * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
143      */
144     initWithArgs: function (skeletonDataFile, atlasFile, scale) {
145         var argSkeletonFile = skeletonDataFile, argAtlasFile = atlasFile,
146             skeletonData, atlas, ownsSkeletonData;
147 
148         if (cc.isString(argSkeletonFile)) {
149             if (cc.isString(argAtlasFile)) {
150                 var data = cc.loader.getRes(argAtlasFile);
151                 sp._atlasLoader.setAtlasFile(argAtlasFile);
152                 atlas = new spine.Atlas(data, sp._atlasLoader);
153             } else {
154                 atlas = atlasFile;
155             }
156             scale = scale || 1 / cc.director.getContentScaleFactor();
157 
158             var attachmentLoader = new spine.AtlasAttachmentLoader(atlas);
159             var skeletonJsonReader = new spine.SkeletonJson(attachmentLoader);
160             skeletonJsonReader.scale = scale;
161 
162             var skeletonJson = cc.loader.getRes(argSkeletonFile);
163             skeletonData = skeletonJsonReader.readSkeletonData(skeletonJson);
164             atlas.dispose(skeletonJsonReader);
165             ownsSkeletonData = true;
166         } else {
167             skeletonData = skeletonDataFile;
168             ownsSkeletonData = atlasFile;
169         }
170         this.setSkeletonData(skeletonData, ownsSkeletonData);
171         this.init();
172     },
173 
174     /**
175      * Returns the bounding box of sp.Skeleton.
176      * @returns {cc.Rect}
177      */
178     getBoundingBox: function () {
179         var minX = cc.FLT_MAX, minY = cc.FLT_MAX, maxX = cc.FLT_MIN, maxY = cc.FLT_MIN;
180         var scaleX = this.getScaleX(), scaleY = this.getScaleY(), vertices = [],
181             slots = this._skeleton.slots, VERTEX = sp.VERTEX_INDEX;
182 
183         for (var i = 0, slotCount = slots.length; i < slotCount; ++i) {
184             var slot = slots[i];
185             if (!slot.attachment || slot.attachment.type != sp.ATTACHMENT_TYPE.REGION)
186                 continue;
187             var attachment = slot.attachment;
188             sp._regionAttachment_computeWorldVertices(attachment, slot.skeleton.x, slot.skeleton.y, slot.bone, vertices);
189             minX = Math.min(minX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
190             minY = Math.min(minY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
191             maxX = Math.max(maxX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
192             maxY = Math.max(maxY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
193         }
194         var position = this.getPosition();
195         return cc.rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY);
196     },
197 
198     /**
199      * Computes the world SRT from the local SRT for each bone.
200      */
201     updateWorldTransform: function () {
202         this._skeleton.updateWorldTransform();
203     },
204 
205     /**
206      * Sets the bones and slots to the setup pose.
207      */
208     setToSetupPose: function () {
209         this._skeleton.setToSetupPose();
210     },
211 
212     /**
213      * Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`.
214      */
215     setBonesToSetupPose: function () {
216         this._skeleton.setBonesToSetupPose();
217     },
218 
219     /**
220      * Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`.
221      */
222     setSlotsToSetupPose: function () {
223         this._skeleton.setSlotsToSetupPose();
224     },
225 
226     /**
227      * Finds a bone by name. This does a string comparison for every bone.
228      * @param {String} boneName
229      * @returns {spine.Bone}
230      */
231     findBone: function (boneName) {
232         return this._skeleton.findBone(boneName);
233     },
234 
235     /**
236      * Finds a slot by name. This does a string comparison for every slot.
237      * @param {String} slotName
238      * @returns {spine.Slot}
239      */
240     findSlot: function (slotName) {
241         return this._skeleton.findSlot(slotName);
242     },
243 
244     /**
245      * Finds a skin by name and makes it the active skin. This does a string comparison for every skin. Note that setting the skin does not change which attachments are visible.
246      * @param {string} skinName
247      * @returns {spine.Skin}
248      */
249     setSkin: function (skinName) {
250         return this._skeleton.setSkinByName(skinName);
251     },
252 
253     /**
254      * Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
255      * @param {String} slotName
256      * @param {String} attachmentName
257      * @returns {spine.RegionAttachment|spine.BoundingBoxAttachment}
258      */
259     getAttachment: function (slotName, attachmentName) {
260         return this._skeleton.getAttachmentBySlotName(slotName, attachmentName);
261     },
262 
263     /**
264      * Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
265      * @param {String} slotName
266      * @param {String} attachmentName
267      */
268     setAttachment: function (slotName, attachmentName) {
269         this._skeleton.setAttachment(slotName, attachmentName);
270     },
271 
272     /**
273      * Sets the premultiplied alpha value to sp.Skeleton.
274      * @param {Number} alpha
275      */
276     setOpacityModifyRGB: function (alpha) {
277         this._premultipliedAlpha = alpha;
278     },
279 
280     /**
281      * Returns whether to enable premultiplied alpha.
282      * @returns {boolean}
283      */
284     isOpacityModifyRGB: function () {
285         return this._premultipliedAlpha;
286     },
287 
288     /**
289      * Sets skeleton data to sp.Skeleton.
290      * @param {spine.SkeletonData} skeletonData
291      * @param {spine.SkeletonData} ownsSkeletonData
292      */
293     setSkeletonData: function (skeletonData, ownsSkeletonData) {
294         if(skeletonData.width != null && skeletonData.height != null)
295             this.setContentSize(skeletonData.width / cc.director.getContentScaleFactor(), skeletonData.height / cc.director.getContentScaleFactor());
296 
297         this._skeleton = new spine.Skeleton(skeletonData);
298         this._rootBone = this._skeleton.getRootBone();
299         this._ownsSkeletonData = ownsSkeletonData;
300 
301         this._renderCmd._createChildFormSkeletonData();
302     },
303 
304     /**
305      * Return the renderer of attachment.
306      * @param {spine.RegionAttachment|spine.BoundingBoxAttachment} regionAttachment
307      * @returns {cc.Node}
308      */
309     getTextureAtlas: function (regionAttachment) {
310         return regionAttachment.rendererObject.page.rendererObject;
311     },
312 
313     /**
314      * Returns the blendFunc of sp.Skeleton.
315      * @returns {cc.BlendFunc}
316      */
317     getBlendFunc: function () {
318         return this._blendFunc;
319     },
320 
321     /**
322      * Sets the blendFunc of sp.Skeleton.
323      * @param {cc.BlendFunc|Number} src
324      * @param {Number} [dst]
325      */
326     setBlendFunc: function (src, dst) {
327         var locBlendFunc = this._blendFunc;
328         if (dst === undefined) {
329             locBlendFunc.src = src.src;
330             locBlendFunc.dst = src.dst;
331         } else {
332             locBlendFunc.src = src;
333             locBlendFunc.dst = dst;
334         }
335     },
336 
337     /**
338      * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live".
339      * @param {Number} dt Delta time since last update
340      */
341     update: function (dt) {
342         this._skeleton.update(dt);
343         this._renderCmd._updateChild();
344     }
345 });
346 
347 /**
348  * Creates a skeleton object.
349  * @deprecated since v3.0, please use new sp.Skeleton(skeletonDataFile, atlasFile, scale) instead.
350  * @param {spine.SkeletonData|String} skeletonDataFile
351  * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
352  * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
353  * @returns {sp.Skeleton}
354  */
355 sp.Skeleton.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) {
356     return new sp.Skeleton(skeletonDataFile, atlasFile, scale);
357 };