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 this._computeRegionAttachmentWorldVertices(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 _computeRegionAttachmentWorldVertices : function(self, x, y, bone, vertices){ 235 var offset = self.offset, vertexIndex = sp.VERTEX_INDEX; 236 x += bone.worldX; 237 y += bone.worldY; 238 vertices[vertexIndex.X1] = offset[vertexIndex.X1] * bone.m00 + offset[vertexIndex.Y1] * bone.m01 + x; 239 vertices[vertexIndex.Y1] = offset[vertexIndex.X1] * bone.m10 + offset[vertexIndex.Y1] * bone.m11 + y; 240 vertices[vertexIndex.X2] = offset[vertexIndex.X2] * bone.m00 + offset[vertexIndex.Y2] * bone.m01 + x; 241 vertices[vertexIndex.Y2] = offset[vertexIndex.X2] * bone.m10 + offset[vertexIndex.Y2] * bone.m11 + y; 242 vertices[vertexIndex.X3] = offset[vertexIndex.X3] * bone.m00 + offset[vertexIndex.Y3] * bone.m01 + x; 243 vertices[vertexIndex.Y3] = offset[vertexIndex.X3] * bone.m10 + offset[vertexIndex.Y3] * bone.m11 + y; 244 vertices[vertexIndex.X4] = offset[vertexIndex.X4] * bone.m00 + offset[vertexIndex.Y4] * bone.m01 + x; 245 vertices[vertexIndex.Y4] = offset[vertexIndex.X4] * bone.m10 + offset[vertexIndex.Y4] * bone.m11 + y; 246 }, 247 248 /** 249 * Computes the world SRT from the local SRT for each bone. 250 */ 251 updateWorldTransform: function () { 252 this._skeleton.updateWorldTransform(); 253 }, 254 255 /** 256 * Sets the bones and slots to the setup pose. 257 */ 258 setToSetupPose: function () { 259 this._skeleton.setToSetupPose(); 260 }, 261 262 /** 263 * Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`. 264 */ 265 setBonesToSetupPose: function () { 266 this._skeleton.setBonesToSetupPose(); 267 }, 268 269 /** 270 * Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`. 271 */ 272 setSlotsToSetupPose: function () { 273 this._skeleton.setSlotsToSetupPose(); 274 }, 275 276 /** 277 * Finds a bone by name. This does a string comparison for every bone. 278 * @param {String} boneName 279 * @returns {spine.Bone} 280 */ 281 findBone: function (boneName) { 282 return this._skeleton.findBone(boneName); 283 }, 284 285 /** 286 * Finds a slot by name. This does a string comparison for every slot. 287 * @param {String} slotName 288 * @returns {spine.Slot} 289 */ 290 findSlot: function (slotName) { 291 return this._skeleton.findSlot(slotName); 292 }, 293 294 /** 295 * 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. 296 * @param {string} skinName 297 * @returns {spine.Skin} 298 */ 299 setSkin: function (skinName) { 300 return this._skeleton.setSkinByName(skinName); 301 }, 302 303 /** 304 * Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin. 305 * @param {String} slotName 306 * @param {String} attachmentName 307 * @returns {spine.RegionAttachment|spine.BoundingBoxAttachment} 308 */ 309 getAttachment: function (slotName, attachmentName) { 310 return this._skeleton.getAttachmentBySlotName(slotName, attachmentName); 311 }, 312 313 /** 314 * Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin. 315 * @param {String} slotName 316 * @param {String} attachmentName 317 */ 318 setAttachment: function (slotName, attachmentName) { 319 this._skeleton.setAttachment(slotName, attachmentName); 320 }, 321 322 /** 323 * Sets the premultiplied alpha value to sp.Skeleton. 324 * @param {Number} alpha 325 */ 326 setOpacityModifyRGB: function (alpha) { 327 this._premultipliedAlpha = alpha; 328 }, 329 330 /** 331 * Returns whether to enable premultiplied alpha. 332 * @returns {boolean} 333 */ 334 isOpacityModifyRGB: function () { 335 return this._premultipliedAlpha; 336 }, 337 338 /** 339 * Sets skeleton data to sp.Skeleton. 340 * @param {spine.SkeletonData} skeletonData 341 * @param {spine.SkeletonData} ownsSkeletonData 342 */ 343 setSkeletonData: function (skeletonData, ownsSkeletonData) { 344 if(skeletonData.width != null && skeletonData.height != null) 345 this.setContentSize(skeletonData.width / cc.director.getContentScaleFactor(), skeletonData.height / cc.director.getContentScaleFactor()); 346 347 this._skeleton = new spine.Skeleton(skeletonData); 348 this._skeleton.updateWorldTransform(); 349 this._rootBone = this._skeleton.getRootBone(); 350 this._ownsSkeletonData = ownsSkeletonData; 351 352 this._renderCmd._createChildFormSkeletonData(); 353 }, 354 355 /** 356 * Return the renderer of attachment. 357 * @param {spine.RegionAttachment|spine.BoundingBoxAttachment} regionAttachment 358 * @returns {cc.Node} 359 */ 360 getTextureAtlas: function (regionAttachment) { 361 return regionAttachment.rendererObject.page.rendererObject; 362 }, 363 364 /** 365 * Returns the blendFunc of sp.Skeleton. 366 * @returns {cc.BlendFunc} 367 */ 368 getBlendFunc: function () { 369 return this._blendFunc; 370 }, 371 372 /** 373 * Sets the blendFunc of sp.Skeleton. 374 * @param {cc.BlendFunc|Number} src 375 * @param {Number} [dst] 376 */ 377 setBlendFunc: function (src, dst) { 378 var locBlendFunc = this._blendFunc; 379 if (dst === undefined) { 380 locBlendFunc.src = src.src; 381 locBlendFunc.dst = src.dst; 382 } else { 383 locBlendFunc.src = src; 384 locBlendFunc.dst = dst; 385 } 386 }, 387 388 /** 389 * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live". 390 * @param {Number} dt Delta time since last update 391 */ 392 update: function (dt) { 393 this._skeleton.update(dt); 394 } 395 }); 396 397 /** 398 * Creates a skeleton object. 399 * @deprecated since v3.0, please use new sp.Skeleton(skeletonDataFile, atlasFile, scale) instead. 400 * @param {spine.SkeletonData|String} skeletonDataFile 401 * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData 402 * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations. 403 * @returns {sp.Skeleton} 404 */ 405 sp.Skeleton.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) { 406 return new sp.Skeleton(skeletonDataFile, atlasFile, scale); 407 };