1 /**************************************************************************** 2 Copyright (c) 2011-2012 cocos2d-x.org 3 Copyright (c) 2013-2014 Chukong Technologies Inc. 4 5 http://www.cocos2d-x.org 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 THE SOFTWARE. 24 ****************************************************************************/ 25 26 /** 27 * The main class of Armature, it plays armature animation, manages and updates bones' state. 28 * @class 29 * @extends ccs.Node 30 * 31 * @property {ccs.Bone} parentBone - The parent bone of the armature node 32 * @property {ccs.ArmatureAnimation} animation - The animation 33 * @property {ccs.ArmatureData} armatureData - The armature data 34 * @property {String} name - The name of the armature 35 * @property {cc.SpriteBatchNode} batchNode - The batch node of the armature 36 * @property {Number} version - The version 37 * @property {Object} body - The body of the armature 38 * @property {ccs.ColliderFilter} colliderFilter - <@writeonly> The collider filter of the armature 39 */ 40 ccs.Armature = ccs.Node.extend(/** @lends ccs.Armature# */{ 41 animation: null, 42 armatureData: null, 43 batchNode: null, 44 _textureAtlas: null, 45 _parentBone: null, 46 _boneDic: null, 47 _topBoneList: null, 48 _armatureIndexDic: null, 49 _offsetPoint: null, 50 version: 0, 51 _armatureTransformDirty: true, 52 _body: null, 53 _blendFunc: null, 54 _className: "Armature", 55 56 /** 57 * Create a armature node. 58 * Constructor of ccs.Armature 59 * @param {String} name 60 * @param {ccs.Bone} parentBone 61 * @example 62 * var armature = new ccs.Armature(); 63 */ 64 ctor: function (name, parentBone) { 65 cc.Node.prototype.ctor.call(this); 66 this._name = ""; 67 this._topBoneList = []; 68 this._armatureIndexDic = {}; 69 this._offsetPoint = cc.p(0, 0); 70 this._armatureTransformDirty = true; 71 this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 72 name && ccs.Armature.prototype.init.call(this, name, parentBone); 73 }, 74 75 /** 76 * Initializes a CCArmature with the specified name and CCBone 77 * @param {String} [name] 78 * @param {ccs.Bone} [parentBone] 79 * @return {Boolean} 80 */ 81 init: function (name, parentBone) { 82 cc.Node.prototype.init.call(this); 83 if (parentBone) 84 this._parentBone = parentBone; 85 this.removeAllChildren(); 86 this.animation = new ccs.ArmatureAnimation(); 87 this.animation.init(this); 88 89 this._boneDic = {}; 90 this._topBoneList.length = 0; 91 92 //this._name = name || ""; 93 var armatureDataManager = ccs.armatureDataManager; 94 95 var animationData; 96 if (name !== "") { 97 //animationData 98 animationData = armatureDataManager.getAnimationData(name); 99 cc.assert(animationData, "AnimationData not exist!"); 100 101 this.animation.setAnimationData(animationData); 102 103 //armatureData 104 var armatureData = armatureDataManager.getArmatureData(name); 105 cc.assert(armatureData, "ArmatureData not exist!"); 106 107 this.armatureData = armatureData; 108 109 //boneDataDic 110 var boneDataDic = armatureData.getBoneDataDic(); 111 for (var key in boneDataDic) { 112 var bone = this.createBone(String(key)); 113 114 //! init bone's Tween to 1st movement's 1st frame 115 do { 116 var movData = animationData.getMovement(animationData.movementNames[0]); 117 if (!movData) break; 118 119 var _movBoneData = movData.getMovementBoneData(bone.getName()); 120 if (!_movBoneData || _movBoneData.frameList.length <= 0) break; 121 122 var frameData = _movBoneData.getFrameData(0); 123 if (!frameData) break; 124 125 bone.getTweenData().copy(frameData); 126 bone.changeDisplayWithIndex(frameData.displayIndex, false); 127 } while (0); 128 } 129 130 this.update(0); 131 this.updateOffsetPoint(); 132 } else { 133 name = "new_armature"; 134 this.armatureData = new ccs.ArmatureData(); 135 this.armatureData.name = name; 136 137 animationData = new ccs.AnimationData(); 138 animationData.name = name; 139 140 armatureDataManager.addArmatureData(name, this.armatureData); 141 armatureDataManager.addAnimationData(name, animationData); 142 143 this.animation.setAnimationData(animationData); 144 } 145 146 this._renderCmd.initShaderCache(); 147 148 this.setCascadeOpacityEnabled(true); 149 this.setCascadeColorEnabled(true); 150 return true; 151 }, 152 153 addChild: function (child, localZOrder, tag) { 154 if(child instanceof ccui.Widget){ 155 cc.log("Armature doesn't support to add Widget as its child, it will be fix soon."); 156 return; 157 } 158 cc.Node.prototype.addChild.call(this, child, localZOrder, tag); 159 }, 160 161 /** 162 * create a bone with name 163 * @param {String} boneName 164 * @return {ccs.Bone} 165 */ 166 createBone: function (boneName) { 167 var existedBone = this.getBone(boneName); 168 if (existedBone) 169 return existedBone; 170 171 var boneData = this.armatureData.getBoneData(boneName); 172 var parentName = boneData.parentName; 173 174 var bone = null; 175 if (parentName) { 176 this.createBone(parentName); 177 bone = new ccs.Bone(boneName); 178 this.addBone(bone, parentName); 179 } else { 180 bone = new ccs.Bone(boneName); 181 this.addBone(bone, ""); 182 } 183 184 bone.setBoneData(boneData); 185 bone.getDisplayManager().changeDisplayWithIndex(-1, false); 186 return bone; 187 }, 188 189 /** 190 * Add a Bone to this Armature 191 * @param {ccs.Bone} bone The Bone you want to add to Armature 192 * @param {String} parentName The parent Bone's name you want to add to. If it's null, then set Armature to its parent 193 */ 194 addBone: function (bone, parentName) { 195 cc.assert(bone, "Argument must be non-nil"); 196 var locBoneDic = this._boneDic; 197 if(bone.getName()) 198 cc.assert(!locBoneDic[bone.getName()], "bone already added. It can't be added again"); 199 200 if (parentName) { 201 var boneParent = locBoneDic[parentName]; 202 if (boneParent) 203 boneParent.addChildBone(bone); 204 else 205 this._topBoneList.push(bone); 206 } else 207 this._topBoneList.push(bone); 208 bone.setArmature(this); 209 210 locBoneDic[bone.getName()] = bone; 211 this.addChild(bone); 212 }, 213 214 /** 215 * Remove a bone with the specified name. If recursion it will also remove child Bone recursively. 216 * @param {ccs.Bone} bone The bone you want to remove 217 * @param {Boolean} recursion Determine whether remove the bone's child recursion. 218 */ 219 removeBone: function (bone, recursion) { 220 cc.assert(bone, "bone must be added to the bone dictionary!"); 221 222 bone.setArmature(null); 223 bone.removeFromParent(recursion); 224 cc.arrayRemoveObject(this._topBoneList, bone); 225 226 delete this._boneDic[bone.getName()]; 227 this.removeChild(bone, true); 228 }, 229 230 /** 231 * Gets a bone with the specified name 232 * @param {String} name The bone's name you want to get 233 * @return {ccs.Bone} 234 */ 235 getBone: function (name) { 236 return this._boneDic[name]; 237 }, 238 239 /** 240 * Change a bone's parent with the specified parent name. 241 * @param {ccs.Bone} bone The bone you want to change parent 242 * @param {String} parentName The new parent's name 243 */ 244 changeBoneParent: function (bone, parentName) { 245 cc.assert(bone, "bone must be added to the bone dictionary!"); 246 247 var parentBone = bone.getParentBone(); 248 if (parentBone) { 249 cc.arrayRemoveObject(parentBone.getChildren(), bone); 250 bone.setParentBone(null); 251 } 252 253 if (parentName) { 254 var boneParent = this._boneDic[parentName]; 255 if (boneParent) { 256 boneParent.addChildBone(bone); 257 cc.arrayRemoveObject(this._topBoneList, bone); 258 } else 259 this._topBoneList.push(bone); 260 } 261 }, 262 263 /** 264 * Get CCArmature's bone dictionary 265 * @return {Object} Armature's bone dictionary 266 */ 267 getBoneDic: function () { 268 return this._boneDic; 269 }, 270 271 /** 272 * Set contentSize and Calculate anchor point. 273 */ 274 updateOffsetPoint: function () { 275 // Set contentsize and Calculate anchor point. 276 var rect = this.getBoundingBox(); 277 this.setContentSize(rect); 278 var locOffsetPoint = this._offsetPoint; 279 locOffsetPoint.x = -rect.x; 280 locOffsetPoint.y = -rect.y; 281 if (rect.width !== 0 && rect.height !== 0) 282 this.setAnchorPoint(locOffsetPoint.x / rect.width, locOffsetPoint.y / rect.height); 283 }, 284 285 getOffsetPoints: function(){ 286 return {x: this._offsetPoint.x, y: this._offsetPoint.y}; 287 }, 288 289 /** 290 * Sets animation to this Armature 291 * @param {ccs.ArmatureAnimation} animation 292 */ 293 setAnimation: function (animation) { 294 this.animation = animation; 295 }, 296 297 /** 298 * Gets the animation of this Armature. 299 * @return {ccs.ArmatureAnimation} 300 */ 301 getAnimation: function () { 302 return this.animation; 303 }, 304 305 /** 306 * armatureTransformDirty getter 307 * @returns {Boolean} 308 */ 309 getArmatureTransformDirty: function () { 310 return this._armatureTransformDirty; 311 }, 312 313 /** 314 * The update callback of ccs.Armature, it updates animation's state and updates bone's state. 315 * @override 316 * @param {Number} dt 317 */ 318 update: function (dt) { 319 this.animation.update(dt); 320 var locTopBoneList = this._topBoneList; 321 for (var i = 0; i < locTopBoneList.length; i++) 322 locTopBoneList[i].update(dt); 323 this._armatureTransformDirty = false; 324 }, 325 326 /** 327 * The callback when ccs.Armature enter stage. 328 * @override 329 */ 330 onEnter: function () { 331 cc.Node.prototype.onEnter.call(this); 332 this.scheduleUpdate(); 333 }, 334 335 /** 336 * The callback when ccs.Armature exit stage. 337 * @override 338 */ 339 onExit: function () { 340 cc.Node.prototype.onExit.call(this); 341 this.unscheduleUpdate(); 342 }, 343 344 /** 345 * This boundingBox will calculate all bones' boundingBox every time 346 * @returns {cc.Rect} 347 */ 348 getBoundingBox: function(){ 349 var minX, minY, maxX, maxY = 0; 350 var first = true; 351 352 var boundingBox = cc.rect(0, 0, 0, 0), locChildren = this._children; 353 354 var len = locChildren.length; 355 for (var i=0; i<len; i++) { 356 var bone = locChildren[i]; 357 if (bone) { 358 var r = bone.getDisplayManager().getBoundingBox(); 359 if (r.x === 0 && r.y === 0 && r.width === 0 && r.height === 0) 360 continue; 361 362 if(first) { 363 minX = r.x; 364 minY = r.y; 365 maxX = r.x + r.width; 366 maxY = r.y + r.height; 367 first = false; 368 } else { 369 minX = r.x < boundingBox.x ? r.x : boundingBox.x; 370 minY = r.y < boundingBox.y ? r.y : boundingBox.y; 371 maxX = r.x + r.width > boundingBox.x + boundingBox.width ? 372 r.x + r.width : boundingBox.x + boundingBox.width; 373 maxY = r.y + r.height > boundingBox.y + boundingBox.height ? 374 r.y + r.height : boundingBox.y + boundingBox.height; 375 } 376 377 boundingBox.x = minX; 378 boundingBox.y = minY; 379 boundingBox.width = maxX - minX; 380 boundingBox.height = maxY - minY; 381 } 382 } 383 return cc.rectApplyAffineTransform(boundingBox, this.getNodeToParentTransform()); 384 }, 385 386 /** 387 * when bone contain the point ,then return it. 388 * @param {Number} x 389 * @param {Number} y 390 * @returns {ccs.Bone} 391 */ 392 getBoneAtPoint: function (x, y) { 393 var locChildren = this._children; 394 for (var i = locChildren.length - 1; i >= 0; i--) { 395 var child = locChildren[i]; 396 if (child instanceof ccs.Bone && child.getDisplayManager().containPoint(x, y)) 397 return child; 398 } 399 return null; 400 }, 401 402 /** 403 * Sets parent bone of this Armature 404 * @param {ccs.Bone} parentBone 405 */ 406 setParentBone: function (parentBone) { 407 this._parentBone = parentBone; 408 var locBoneDic = this._boneDic; 409 for (var key in locBoneDic) { 410 locBoneDic[key].setArmature(this); 411 } 412 }, 413 414 /** 415 * Return parent bone of ccs.Armature. 416 * @returns {ccs.Bone} 417 */ 418 getParentBone: function () { 419 return this._parentBone; 420 }, 421 422 /** 423 * draw contour 424 */ 425 drawContour: function () { 426 cc._drawingUtil.setDrawColor(255, 255, 255, 255); 427 cc._drawingUtil.setLineWidth(1); 428 var locBoneDic = this._boneDic; 429 for (var key in locBoneDic) { 430 var bone = locBoneDic[key]; 431 var detector = bone.getColliderDetector(); 432 if(!detector) 433 continue; 434 var bodyList = detector.getColliderBodyList(); 435 for (var i = 0; i < bodyList.length; i++) { 436 var body = bodyList[i]; 437 var vertexList = body.getCalculatedVertexList(); 438 cc._drawingUtil.drawPoly(vertexList, vertexList.length, true); 439 } 440 } 441 }, 442 443 setBody: function (body) { 444 if (this._body === body) 445 return; 446 447 this._body = body; 448 this._body.data = this; 449 var child, displayObject, locChildren = this._children; 450 for (var i = 0; i < locChildren.length; i++) { 451 child = locChildren[i]; 452 if (child instanceof ccs.Bone) { 453 var displayList = child.getDisplayManager().getDecorativeDisplayList(); 454 for (var j = 0; j < displayList.length; j++) { 455 displayObject = displayList[j]; 456 var detector = displayObject.getColliderDetector(); 457 if (detector) 458 detector.setBody(this._body); 459 } 460 } 461 } 462 }, 463 464 getShapeList: function () { 465 if (this._body) 466 return this._body.shapeList; 467 return null; 468 }, 469 470 getBody: function () { 471 return this._body; 472 }, 473 474 /** 475 * Sets the blendFunc to ccs.Armature 476 * @param {cc.BlendFunc|Number} blendFunc 477 * @param {Number} [dst] 478 */ 479 setBlendFunc: function (blendFunc, dst) { 480 if(dst === undefined){ 481 this._blendFunc.src = blendFunc.src; 482 this._blendFunc.dst = blendFunc.dst; 483 } else { 484 this._blendFunc.src = blendFunc; 485 this._blendFunc.dst = dst; 486 } 487 }, 488 489 /** 490 * Returns the blendFunc of ccs.Armature 491 * @returns {cc.BlendFunc} 492 */ 493 getBlendFunc: function () { 494 return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); 495 }, 496 497 /** 498 * set collider filter 499 * @param {ccs.ColliderFilter} filter 500 */ 501 setColliderFilter: function (filter) { 502 var locBoneDic = this._boneDic; 503 for (var key in locBoneDic) 504 locBoneDic[key].setColliderFilter(filter); 505 }, 506 507 /** 508 * Returns the armatureData of ccs.Armature 509 * @return {ccs.ArmatureData} 510 */ 511 getArmatureData: function () { 512 return this.armatureData; 513 }, 514 515 /** 516 * Sets armatureData to this Armature 517 * @param {ccs.ArmatureData} armatureData 518 */ 519 setArmatureData: function (armatureData) { 520 this.armatureData = armatureData; 521 }, 522 523 getBatchNode: function () { 524 return this.batchNode; 525 }, 526 527 setBatchNode: function (batchNode) { 528 this.batchNode = batchNode; 529 }, 530 531 /** 532 * version getter 533 * @returns {Number} 534 */ 535 getVersion: function () { 536 return this.version; 537 }, 538 539 /** 540 * version setter 541 * @param {Number} version 542 */ 543 setVersion: function (version) { 544 this.version = version; 545 }, 546 547 _createRenderCmd: function(){ 548 if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) 549 return new ccs.Armature.CanvasRenderCmd(this); 550 else 551 return new ccs.Armature.WebGLRenderCmd(this); 552 } 553 }); 554 555 var _p = ccs.Armature.prototype; 556 557 /** @expose */ 558 _p.parentBone; 559 cc.defineGetterSetter(_p, "parentBone", _p.getParentBone, _p.setParentBone); 560 /** @expose */ 561 _p.body; 562 cc.defineGetterSetter(_p, "body", _p.getBody, _p.setBody); 563 /** @expose */ 564 _p.colliderFilter; 565 cc.defineGetterSetter(_p, "colliderFilter", null, _p.setColliderFilter); 566 567 _p = null; 568 569 /** 570 * Allocates an armature, and use the ArmatureData named name in ArmatureDataManager to initializes the armature. 571 * @param {String} [name] Bone name 572 * @param {ccs.Bone} [parentBone] the parent bone 573 * @return {ccs.Armature} 574 * @deprecated since v3.1, please use new construction instead 575 */ 576 ccs.Armature.create = function (name, parentBone) { 577 return new ccs.Armature(name, parentBone); 578 }; 579