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