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