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), MESH(2) and SKINNED_MESH.
 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     MESH: 2,
 59     SKINNED_MESH:3
 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 
 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.game.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._premultipliedAlpha = (cc._renderType === cc.game.RENDER_TYPE_WEBGL && cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA);
108         this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
109         this.scheduleUpdate();
110     },
111 
112     /**
113      * Sets whether open debug slots.
114      * @param {boolean} enable true to open, false to close.
115      */
116     setDebugSolots:function(enable){
117         this._debugSlots = enable;
118     },
119 
120     /**
121      * Sets whether open debug bones.
122      * @param {boolean} enable
123      */
124     setDebugBones:function(enable){
125         this._debugBones = enable;
126     },
127 
128     /**
129      * Sets whether open debug slots.
130      * @param {boolean} enabled true to open, false to close.
131      */
132     setDebugSlotsEnabled: function(enabled) {
133         this._debugSlots = enabled;
134     },
135 
136     /**
137      * Gets whether open debug slots.
138      * @returns {boolean} true to open, false to close.
139      */
140     getDebugSlotsEnabled: function() {
141         return this._debugSlots;
142     },
143 
144     /**
145      * Sets whether open debug bones.
146      * @param {boolean} enabled
147      */
148     setDebugBonesEnabled: function(enabled) {
149         this._debugBones = enabled;
150     },
151 
152     /**
153      * Gets whether open debug bones.
154      * @returns {boolean} true to open, false to close.
155      */
156     getDebugBonesEnabled: function() {
157         return this._debugBones;
158     },
159 
160     /**
161      * Sets the time scale of sp.Skeleton.
162      * @param {Number} scale
163      */
164     setTimeScale:function(scale){
165         this._timeScale = scale;
166     },
167 
168     getTimeScale: function(){
169         return this._timeScale;
170     },
171 
172     /**
173      * Initializes sp.Skeleton with Data.
174      * @param {spine.SkeletonData|String} skeletonDataFile
175      * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
176      * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
177      */
178     initWithArgs: function (skeletonDataFile, atlasFile, scale) {
179         var argSkeletonFile = skeletonDataFile, argAtlasFile = atlasFile,
180             skeletonData, atlas, ownsSkeletonData;
181 
182         if (cc.isString(argSkeletonFile)) {
183             if (cc.isString(argAtlasFile)) {
184                 var data = cc.loader.getRes(argAtlasFile);
185                 sp._atlasLoader.setAtlasFile(argAtlasFile);
186                 atlas = new spine.Atlas(data, sp._atlasLoader);
187             } else {
188                 atlas = atlasFile;
189             }
190             scale = scale || 1 / cc.director.getContentScaleFactor();
191 
192             var attachmentLoader = new spine.AtlasAttachmentLoader(atlas);
193             var skeletonJsonReader = new spine.SkeletonJson(attachmentLoader);
194             skeletonJsonReader.scale = scale;
195 
196             var skeletonJson = cc.loader.getRes(argSkeletonFile);
197             skeletonData = skeletonJsonReader.readSkeletonData(skeletonJson);
198             atlas.dispose(skeletonJsonReader);
199             ownsSkeletonData = true;
200         } else {
201             skeletonData = skeletonDataFile;
202             ownsSkeletonData = atlasFile;
203         }
204         this.setSkeletonData(skeletonData, ownsSkeletonData);
205         this.init();
206     },
207 
208     /**
209      * Returns the bounding box of sp.Skeleton.
210      * @returns {cc.Rect}
211      */
212     getBoundingBox: function () {
213         var minX = cc.FLT_MAX, minY = cc.FLT_MAX, maxX = cc.FLT_MIN, maxY = cc.FLT_MIN;
214         var scaleX = this.getScaleX(), scaleY = this.getScaleY(), vertices = [],
215             slots = this._skeleton.slots, VERTEX = sp.VERTEX_INDEX;
216 
217         for (var i = 0, slotCount = slots.length; i < slotCount; ++i) {
218             var slot = slots[i];
219             if (!slot.attachment || slot.attachment.type != sp.ATTACHMENT_TYPE.REGION)
220                 continue;
221             var attachment = slot.attachment;
222             this._computeRegionAttachmentWorldVertices(attachment, slot.bone.skeleton.x, slot.bone.skeleton.y, slot.bone, vertices);
223             minX = Math.min(minX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
224             minY = Math.min(minY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
225             maxX = Math.max(maxX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX);
226             maxY = Math.max(maxY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY);
227         }
228         var position = this.getPosition();
229         return cc.rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY);
230     },
231 
232     _computeRegionAttachmentWorldVertices : function(self, x, y, bone, vertices){
233         var offset = self.offset, vertexIndex = sp.VERTEX_INDEX;
234         x += bone.worldX;
235         y += bone.worldY;
236         vertices[vertexIndex.X1] = offset[vertexIndex.X1] * bone.m00 + offset[vertexIndex.Y1] * bone.m01 + x;
237         vertices[vertexIndex.Y1] = offset[vertexIndex.X1] * bone.m10 + offset[vertexIndex.Y1] * bone.m11 + y;
238         vertices[vertexIndex.X2] = offset[vertexIndex.X2] * bone.m00 + offset[vertexIndex.Y2] * bone.m01 + x;
239         vertices[vertexIndex.Y2] = offset[vertexIndex.X2] * bone.m10 + offset[vertexIndex.Y2] * bone.m11 + y;
240         vertices[vertexIndex.X3] = offset[vertexIndex.X3] * bone.m00 + offset[vertexIndex.Y3] * bone.m01 + x;
241         vertices[vertexIndex.Y3] = offset[vertexIndex.X3] * bone.m10 + offset[vertexIndex.Y3] * bone.m11 + y;
242         vertices[vertexIndex.X4] = offset[vertexIndex.X4] * bone.m00 + offset[vertexIndex.Y4] * bone.m01 + x;
243         vertices[vertexIndex.Y4] = offset[vertexIndex.X4] * bone.m10 + offset[vertexIndex.Y4] * bone.m11 + y;
244     },
245 
246     /**
247      * Computes the world SRT from the local SRT for each bone.
248      */
249     updateWorldTransform: function () {
250         this._skeleton.updateWorldTransform();
251     },
252 
253     /**
254      * Sets the bones and slots to the setup pose.
255      */
256     setToSetupPose: function () {
257         this._skeleton.setToSetupPose();
258     },
259 
260     /**
261      * Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`.
262      */
263     setBonesToSetupPose: function () {
264         this._skeleton.setBonesToSetupPose();
265     },
266 
267     /**
268      * Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`.
269      */
270     setSlotsToSetupPose: function () {
271         this._skeleton.setSlotsToSetupPose();
272     },
273 
274     /**
275      * Finds a bone by name. This does a string comparison for every bone.
276      * @param {String} boneName
277      * @returns {spine.Bone}
278      */
279     findBone: function (boneName) {
280         return this._skeleton.findBone(boneName);
281     },
282 
283     /**
284      * Finds a slot by name. This does a string comparison for every slot.
285      * @param {String} slotName
286      * @returns {spine.Slot}
287      */
288     findSlot: function (slotName) {
289         return this._skeleton.findSlot(slotName);
290     },
291 
292     /**
293      * 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.
294      * @param {string} skinName
295      * @returns {spine.Skin}
296      */
297     setSkin: function (skinName) {
298         return this._skeleton.setSkinByName(skinName);
299     },
300 
301     /**
302      * Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
303      * @param {String} slotName
304      * @param {String} attachmentName
305      * @returns {spine.RegionAttachment|spine.BoundingBoxAttachment}
306      */
307     getAttachment: function (slotName, attachmentName) {
308         return this._skeleton.getAttachmentBySlotName(slotName, attachmentName);
309     },
310 
311     /**
312      * Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin.
313      * @param {String} slotName
314      * @param {String} attachmentName
315      */
316     setAttachment: function (slotName, attachmentName) {
317         this._skeleton.setAttachment(slotName, attachmentName);
318     },
319 
320     /**
321      * Sets the premultiplied alpha value to sp.Skeleton.
322      * @param {Number} alpha
323      */
324     setPremultipliedAlpha: function (premultiplied) {
325         this._premultipliedAlpha = premultiplied;
326     },
327 
328     /**
329      * Returns whether to enable premultiplied alpha.
330      * @returns {boolean}
331      */
332     isPremultipliedAlpha: function () {
333         return this._premultipliedAlpha;
334     },
335 
336     /**
337      * Sets skeleton data to sp.Skeleton.
338      * @param {spine.SkeletonData} skeletonData
339      * @param {spine.SkeletonData} ownsSkeletonData
340      */
341     setSkeletonData: function (skeletonData, ownsSkeletonData) {
342         if(skeletonData.width != null && skeletonData.height != null)
343             this.setContentSize(skeletonData.width / cc.director.getContentScaleFactor(), skeletonData.height / cc.director.getContentScaleFactor());
344 
345         this._skeleton = new spine.Skeleton(skeletonData);
346         this._skeleton.updateWorldTransform();
347         this._rootBone = this._skeleton.getRootBone();
348         this._ownsSkeletonData = ownsSkeletonData;
349 
350         this._renderCmd._createChildFormSkeletonData();
351     },
352 
353     /**
354      * Return the renderer of attachment.
355      * @param {spine.RegionAttachment|spine.BoundingBoxAttachment} regionAttachment
356      * @returns {cc.Node}
357      */
358     getTextureAtlas: function (regionAttachment) {
359         return regionAttachment.rendererObject.page.rendererObject;
360     },
361 
362     /**
363      * Returns the blendFunc of sp.Skeleton.
364      * @returns {cc.BlendFunc}
365      */
366     getBlendFunc: function () {
367         return this._blendFunc;
368     },
369 
370     /**
371      * Sets the blendFunc of sp.Skeleton.
372      * @param {cc.BlendFunc|Number} src
373      * @param {Number} [dst]
374      */
375     setBlendFunc: function (src, dst) {
376         var locBlendFunc = this._blendFunc;
377         if (dst === undefined) {
378             locBlendFunc.src = src.src;
379             locBlendFunc.dst = src.dst;
380         } else {
381             locBlendFunc.src = src;
382             locBlendFunc.dst = dst;
383         }
384     },
385 
386     /**
387      * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live".
388      * @param {Number} dt Delta time since last update
389      */
390     update: function (dt) {
391         this._skeleton.update(dt);
392     }
393 });
394 
395 /**
396  * Creates a skeleton object.
397  * @deprecated since v3.0, please use new sp.Skeleton(skeletonDataFile, atlasFile, scale) instead.
398  * @param {spine.SkeletonData|String} skeletonDataFile
399  * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData
400  * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations.
401  * @returns {sp.Skeleton}
402  */
403 sp.Skeleton.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) {
404     return new sp.Skeleton(skeletonDataFile, atlasFile, scale);
405 };
406