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