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) and REGION_SEQUENCE(2).
 52  * @constant
 53  * @type {{REGION: number, BOUNDING_BOX: number, REGION_SEQUENCE: number}}
 54  */
 55 sp.ATTACHMENT_TYPE = {
 56     REGION: 0,
 57     BOUNDING_BOX: 1,
 58     REGION_SEQUENCE: 2
 59 };
 60 
 61 /**
 62  * <p>
 63  *     The skeleton of Spine.                                                                          <br/>
 64  *     Skeleton has a reference to a SkeletonData and stores the state for skeleton instance,
 65  *     which consists of the current pose's bone SRT, slot colors, and which slot attachments are visible.           <br/>
 66  *     Multiple skeletons can use the same SkeletonData (which includes all animations, skins, and attachments).     <br/>
 67  * </p>
 68  * @class
 69  * @extends cc.Node
 70  */
 71 sp.Skeleton = cc.Node.extend(/** @lends sp.Skeleton# */{
 72     _skeleton: null,
 73     _rootBone: null,
 74     _timeScale: 1,
 75     _debugSlots: false,
 76     _debugBones: false,
 77     _premultipliedAlpha: false,
 78     _ownsSkeletonData: null,
 79     _atlas: null,
 80     _blendFunc: null,
 81 
 82     /**
 83      * The constructor of sp.Skeleton. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
 84      */
 85     ctor:function(skeletonDataFile, atlasFile, scale){
 86         cc.Node.prototype.ctor.call(this);
 87         this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
 88 
 89         if(arguments.length === 0)
 90             this.init();
 91         else
 92             this.initWithArgs(skeletonDataFile, atlasFile, scale);
 93     },
 94 
 95     _createRenderCmd:function () {
 96         if(cc._renderType === cc._RENDER_TYPE_CANVAS)
 97             return new sp.Skeleton.CanvasRenderCmd(this);
 98         else
 99             return new sp.Skeleton.WebGLRenderCmd(this);
100     },
101 
102     /**
103      * Initializes a sp.Skeleton. please do not call this function by yourself, you should pass the parameters to constructor to initialize it.
104      */
105     init: function () {
106         cc.Node.prototype.init.call(this);
107         this.setOpacityModifyRGB(true);
108         this._blendFunc.src = cc.ONE;
109         this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
110         this.scheduleUpdate();
111     },
112 
113     /**
114      * Sets whether open debug solots.
115      * @param {boolean} enable true to open, false to close.
116      */
117     setDebugSolots:function(enable){
118         this._debugSlots = enable;
119     },
120 
121     /**
122      * Sets whether open debug bones.
123      * @param {boolean} enable
124      */
125     setDebugBones:function(enable){
126         this._debugBones = enable;
127     },
128 
129     /**
130      * Sets the time scale of sp.Skeleton.
131      * @param {Number} v
132      */
133     setTimeScale:function(v){
134         this._timeScale = v;
135     },
136 
137     /**
138      * Initializes sp.Skeleton with Data.
139      * @param {spine.SkeletonData|String} skeletonDataFile
140      * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
141      * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
142      */
143     initWithArgs: function (skeletonDataFile, atlasFile, scale) {
144         var argSkeletonFile = skeletonDataFile, argAtlasFile = atlasFile,
145             skeletonData, atlas, ownsSkeletonData;
146 
147         if (cc.isString(argSkeletonFile)) {
148             if (cc.isString(argAtlasFile)) {
149                 var data = cc.loader.getRes(argAtlasFile);
150                 sp._atlasLoader.setAtlasFile(argAtlasFile);
151                 atlas = new spine.Atlas(data, sp._atlasLoader);
152             } else {
153                 atlas = atlasFile;
154             }
155             scale = scale || 1 / cc.director.getContentScaleFactor();
156 
157             var attachmentLoader = new spine.AtlasAttachmentLoader(atlas);
158             var skeletonJsonReader = new spine.SkeletonJson(attachmentLoader);
159             skeletonJsonReader.scale = scale;
160 
161             var skeletonJson = cc.loader.getRes(argSkeletonFile);
162             skeletonData = skeletonJsonReader.readSkeletonData(skeletonJson);
163             atlas.dispose(skeletonJsonReader);
164             ownsSkeletonData = true;
165         } else {
166             skeletonData = skeletonDataFile;
167             ownsSkeletonData = atlasFile;
168         }
169         this.setSkeletonData(skeletonData, ownsSkeletonData);
170         this.init();
171     },
172 
173     /**
174      * Returns the bounding box of sp.Skeleton.
175      * @returns {cc.Rect}
176      */
177     getBoundingBox: function () {
178         var minX = cc.FLT_MAX, minY = cc.FLT_MAX, maxX = cc.FLT_MIN, maxY = cc.FLT_MIN;
179         var scaleX = this.getScaleX(), scaleY = this.getScaleY(), vertices = [],
180             slots = this._skeleton.slots, VERTEX = sp.VERTEX_INDEX;
181 
182         for (var i = 0, slotCount = slots.length; i < slotCount; ++i) {
183             var slot = slots[i];
184             if (!slot.attachment || slot.attachment.type != sp.ATTACHMENT_TYPE.REGION)
185                 continue;
186             var attachment = slot.attachment;
187             sp._regionAttachment_computeWorldVertices(attachment, slot.skeleton.x, slot.skeleton.y, slot.bone, vertices);
188             minX = Math.min(minX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
189             minY = Math.min(minY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
190             maxX = Math.max(maxX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
191             maxY = Math.max(maxY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
192         }
193         var position = this.getPosition();
194         return cc.rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY);
195     },
196 
197     /**
198      * Computes the world SRT from the local SRT for each bone.
199      */
200     updateWorldTransform: function () {
201         this._skeleton.updateWorldTransform();
202     },
203 
204     /**
205      * Sets the bones and slots to the setup pose.
206      */
207     setToSetupPose: function () {
208         this._skeleton.setToSetupPose();
209     },
210 
211     /**
212      * Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`.
213      */
214     setBonesToSetupPose: function () {
215         this._skeleton.setBonesToSetupPose();
216     },
217 
218     /**
219      * Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`.
220      */
221     setSlotsToSetupPose: function () {
222         this._skeleton.setSlotsToSetupPose();
223     },
224 
225     /**
226      * Finds a bone by name. This does a string comparison for every bone.
227      * @param {String} boneName
228      * @returns {spine.Bone}
229      */
230     findBone: function (boneName) {
231         return this._skeleton.findBone(boneName);
232     },
233 
234     /**
235      * Finds a slot by name. This does a string comparison for every slot.
236      * @param {String} slotName
237      * @returns {spine.Slot}
238      */
239     findSlot: function (slotName) {
240         return this._skeleton.findSlot(slotName);
241     },
242 
243     /**
244      * 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.
245      * @param {string} skinName
246      * @returns {spine.Skin}
247      */
248     setSkin: function (skinName) {
249         return this._skeleton.setSkinByName(skinName);
250     },
251 
252     /**
253      * Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
254      * @param {String} slotName
255      * @param {String} attachmentName
256      * @returns {spine.RegionAttachment|spine.BoundingBoxAttachment}
257      */
258     getAttachment: function (slotName, attachmentName) {
259         return this._skeleton.getAttachmentBySlotName(slotName, attachmentName);
260     },
261 
262     /**
263      * Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
264      * @param {String} slotName
265      * @param {String} attachmentName
266      */
267     setAttachment: function (slotName, attachmentName) {
268         this._skeleton.setAttachment(slotName, attachmentName);
269     },
270 
271     /**
272      * Sets the premultiplied alpha value to sp.Skeleton.
273      * @param {Number} alpha
274      */
275     setOpacityModifyRGB: function (alpha) {
276         this._premultipliedAlpha = alpha;
277     },
278 
279     /**
280      * Returns whether to enable premultiplied alpha.
281      * @returns {boolean}
282      */
283     isOpacityModifyRGB: function () {
284         return this._premultipliedAlpha;
285     },
286 
287     /**
288      * Sets skeleton data to sp.Skeleton.
289      * @param {spine.SkeletonData} skeletonData
290      * @param {spine.SkeletonData} ownsSkeletonData
291      */
292     setSkeletonData: function (skeletonData, ownsSkeletonData) {
293         this._skeleton = new spine.Skeleton(skeletonData);
294         this._rootBone = this._skeleton.getRootBone();
295         this._ownsSkeletonData = ownsSkeletonData;
296 
297         this._renderCmd._createChildFormSkeletonData();
298     },
299 
300     /**
301      * Return the renderer of attachment.
302      * @param {spine.RegionAttachment|spine.BoundingBoxAttachment} regionAttachment
303      * @returns {cc.Node}
304      */
305     getTextureAtlas: function (regionAttachment) {
306         return regionAttachment.rendererObject.page.rendererObject;
307     },
308 
309     /**
310      * Returns the blendFunc of sp.Skeleton.
311      * @returns {cc.BlendFunc}
312      */
313     getBlendFunc: function () {
314         return this._blendFunc;
315     },
316 
317     /**
318      * Sets the blendFunc of sp.Skeleton.
319      * @param {cc.BlendFunc|Number} src
320      * @param {Number} [dst]
321      */
322     setBlendFunc: function (src, dst) {
323         var locBlendFunc = this._blendFunc;
324         if (dst === undefined) {
325             locBlendFunc.src = src.src;
326             locBlendFunc.dst = src.dst;
327         } else {
328             locBlendFunc.src = src;
329             locBlendFunc.dst = dst;
330         }
331     },
332 
333     /**
334      * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live".
335      * @param {Number} dt Delta time since last update
336      */
337     update: function (dt) {
338         this._skeleton.update(dt);
339         this._renderCmd._updateChild();
340     }
341 });
342 
343 /**
344  * Creates a skeleton object.
345  * @deprecated since v3.0, please use new sp.Skeleton(skeletonDataFile, atlasFile, scale) instead.
346  * @param {spine.SkeletonData|String} skeletonDataFile
347  * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
348  * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
349  * @returns {sp.Skeleton}
350  */
351 sp.Skeleton.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) {
352     return new sp.Skeleton(skeletonDataFile, atlasFile, scale);
353 };