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 };