1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 4 http://www.cocos2d-x.org 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 ****************************************************************************/ 24 25 /** 26 * Base class for ccs.Armature objects. 27 * @class 28 * @extends ccs.NodeRGBA 29 */ 30 ccs.Armature = ccs.NodeRGBA.extend(/** @lends ccs.Armature# */{ 31 _animation:null, 32 _armatureData:null, 33 _batchNode:null, 34 _name:"", 35 _textureAtlas:null, 36 _parentBone:null, 37 _boneDic:null, 38 _topBoneList:null, 39 _armatureIndexDic:null, 40 _offsetPoint:null, 41 _version:0, 42 _armatureTransformDirty:true, 43 _body:null, 44 _textureAtlasDic:null, 45 _blendFunc:null, 46 ctor:function () { 47 cc.NodeRGBA.prototype.ctor.call(this); 48 this._animation = null; 49 this._armatureData = null; 50 this._batchNode = null; 51 this._name = ""; 52 this._textureAtlas = null; 53 this._parentBone = null; 54 this._boneDic = null; 55 this._topBoneList = null; 56 this._armatureIndexDic = {}; 57 this._offsetPoint = cc.p(0, 0); 58 this._version = 0; 59 this._armatureTransformDirty = true; 60 this._body = null; 61 this._textureAtlasDic = null; 62 this._blendFunc = null; 63 }, 64 65 /** 66 * Initializes a CCArmature with the specified name and CCBone 67 * @param {String} name 68 * @param {ccs.Bone} parentBone 69 * @return {Boolean} 70 */ 71 init:function (name, parentBone) { 72 cc.NodeRGBA.prototype.init.call(this); 73 if (parentBone) { 74 this._parentBone = parentBone; 75 } 76 this.removeAllChildren(); 77 this._animation = new ccs.ArmatureAnimation(); 78 this._animation.init(this); 79 this._boneDic = {}; 80 this._topBoneList = []; 81 this._textureAtlasDic = {}; 82 this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 83 this._name = (!name) ? "" : name; 84 var armatureDataManager = ccs.ArmatureDataManager.getInstance(); 85 if (name != "") { 86 //animationData 87 var animationData = armatureDataManager.getAnimationData(name); 88 if (!animationData) { 89 cc.log("AnimationData of " + name + " not exist! "); 90 return false; 91 } 92 this._animation.setAnimationData(animationData); 93 94 //armatureData 95 var armatureData = armatureDataManager.getArmatureData(name); 96 this._armatureData = armatureData; 97 98 //boneDataDic 99 var boneDataDic = armatureData.getBoneDataDic(); 100 for (var key in boneDataDic) { 101 var bone = this.createBone(String(key)); 102 //! init bone's Tween to 1st movement's 1st frame 103 do { 104 var movData = animationData.getMovement(animationData.movementNames[0]); 105 if (!movData) { 106 break; 107 } 108 var _movBoneData = movData.getMovementBoneData(bone.getName()); 109 if (!_movBoneData || _movBoneData.frameList.length <= 0) { 110 break; 111 } 112 var frameData = _movBoneData.getFrameData(0); 113 if (!frameData) { 114 break; 115 } 116 bone.getTweenData().copy(frameData); 117 bone.changeDisplayWithIndex(frameData.displayIndex, false); 118 } while (0); 119 } 120 this.update(0); 121 this.updateOffsetPoint(); 122 } else { 123 this._name = "new_armature"; 124 this._armatureData = new ccs.ArmatureData(); 125 this._armatureData.name = this._name; 126 127 var animationData = new ccs.AnimationData(); 128 animationData.name = this._name; 129 130 armatureDataManager.addArmatureData(this._name, this._armatureData); 131 armatureDataManager.addAnimationData(this._name, animationData); 132 133 this._animation.setAnimationData(animationData); 134 } 135 if (cc.renderContextType === cc.WEBGL) { 136 this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR)); 137 } 138 139 this.setCascadeOpacityEnabled(true); 140 this.setCascadeColorEnabled(true); 141 return true; 142 }, 143 onEnter:function(){ 144 cc.NodeRGBA.prototype.onEnter.call(this); 145 this.scheduleUpdate(); 146 }, 147 onExit:function(){ 148 cc.NodeRGBA.prototype.onExit.call(this); 149 this.unscheduleUpdate(); 150 }, 151 /** 152 * create a bone 153 * @param {String} boneName 154 * @return {ccs.Bone} 155 */ 156 createBone:function (boneName) { 157 var existedBone = this.getBone(boneName); 158 if (existedBone) { 159 return existedBone; 160 } 161 var boneData = this._armatureData.getBoneData(boneName); 162 var parentName = boneData.parentName; 163 var bone = null; 164 if (parentName != "") { 165 this.createBone(parentName); 166 bone = ccs.Bone.create(boneName); 167 this.addBone(bone, parentName); 168 } else { 169 bone = ccs.Bone.create(boneName); 170 this.addBone(bone, ""); 171 } 172 173 bone.setBoneData(boneData); 174 bone.getDisplayManager().changeDisplayWithIndex(-1, false); 175 return bone; 176 }, 177 178 /** 179 * add a bone 180 * @param {ccs.Bone} bone 181 * @param {String} parentName 182 */ 183 addBone:function (bone, parentName) { 184 if (!bone) { 185 cc.log("Argument must be non-nil"); 186 return; 187 } 188 if (this._boneDic[bone.getName()]) { 189 cc.log("bone already added. It can't be added again"); 190 return; 191 } 192 193 if (parentName) { 194 var boneParent = this._boneDic[parentName]; 195 if (boneParent) { 196 boneParent.addChildBone(bone); 197 } 198 else { 199 this._topBoneList.push(bone); 200 } 201 } 202 else { 203 this._topBoneList.push(bone); 204 } 205 bone.setArmature(this); 206 this._boneDic[bone.getName()] = bone; 207 this.addChild(bone); 208 }, 209 210 /** 211 * remove a bone 212 * @param {ccs.Bone} bone 213 * @param {Boolean} recursion 214 */ 215 removeBone:function (bone, recursion) { 216 if (!bone) { 217 cc.log("bone must be added to the bone dictionary!"); 218 return; 219 } 220 221 bone.setArmature(null); 222 bone.removeFromParent(recursion); 223 cc.ArrayRemoveObject(this._topBoneList, bone); 224 delete this._boneDic[bone.getName()]; 225 this.removeChild(bone, true); 226 }, 227 228 /** 229 * get a bone by name 230 * @param {String} name 231 * @return {ccs.Bone} 232 */ 233 getBone:function (name) { 234 return this._boneDic[name]; 235 }, 236 237 /** 238 * Change a bone's parent with the specified parent name. 239 * @param {ccs.Bone} bone 240 * @param {String} parentName 241 */ 242 changeBoneParent:function (bone, parentName) { 243 if (!bone) { 244 cc.log("bone must be added to the bone dictionary!"); 245 return; 246 } 247 var parentBone = bone.getParentBone(); 248 if(parentBone){ 249 cc.ArrayRemoveObject(parentBone.getChildrenBone(), 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 /** 265 * Get CCArmature's bone dictionary 266 * @return {Object} 267 */ 268 getBoneDic:function () { 269 return this._boneDic; 270 }, 271 272 /** 273 * Set contentSize and Calculate anchor point. 274 */ 275 updateOffsetPoint:function () { 276 // Set contentsize and Calculate anchor point. 277 var rect = this.boundingBox(); 278 this.setContentSize(rect._size); 279 var locOffsetPoint = this._offsetPoint; 280 locOffsetPoint.x = -rect.x; 281 locOffsetPoint.y = -rect.y; 282 if (rect.width != 0 && rect.height != 0) { 283 this.setAnchorPoint(locOffsetPoint.x / rect.width, locOffsetPoint.y / rect.height); 284 } 285 }, 286 287 update:function (dt) { 288 this._animation.update(dt); 289 var locTopBoneList = this._topBoneList; 290 for (var i = 0; i < locTopBoneList.length; i++) { 291 locTopBoneList[i].update(dt); 292 } 293 this._armatureTransformDirty = false; 294 }, 295 296 297 nodeToParentTransform: null, 298 299 _nodeToParentTransformForWebGL:function () { 300 if (this._transformDirty) { 301 this._armatureTransformDirty = true; 302 // Translate values 303 var x = this._position.x; 304 var y = this._position.y; 305 var apx = this._anchorPointInPoints.x, napx = -apx; 306 var apy = this._anchorPointInPoints.y, napy = -apy; 307 var scx = this._scaleX, scy = this._scaleY; 308 309 if (this._ignoreAnchorPointForPosition) { 310 x += apx; 311 y += apy; 312 } 313 314 // Rotation values 315 // Change rotation code to handle X and Y 316 // If we skew with the exact same value for both x and y then we're simply just rotating 317 var cx = 1, sx = 0, cy = 1, sy = 0; 318 if (this._rotationX !== 0 || this._rotationY !== 0) { 319 cx = Math.cos(-this._rotationRadiansX); 320 sx = Math.sin(-this._rotationRadiansX); 321 cy = Math.cos(-this._rotationRadiansY); 322 sy = Math.sin(-this._rotationRadiansY); 323 } 324 325 // Add offset point 326 x += cy * this._offsetPoint.x * this._scaleX + -sx * this._offsetPoint.y * this._scaleY; 327 y += sy * this._offsetPoint.x * this._scaleX + cx * this._offsetPoint.y * this._scaleY; 328 329 var needsSkewMatrix = ( this._skewX || this._skewY ); 330 331 // optimization: 332 // inline anchor point calculation if skew is not needed 333 // Adjusted transform calculation for rotational skew 334 if (!needsSkewMatrix && (apx !== 0 || apy !== 0)) { 335 x += cy * napx * scx + -sx * napy * scy; 336 y += sy * napx * scx + cx * napy * scy; 337 } 338 339 // Build Transform Matrix 340 // Adjusted transform calculation for rotational skew 341 var t = {a:cy * scx, b:sy * scx, c:-sx * scy, d:cx * scy, tx:x, ty:y}; 342 343 // XXX: Try to inline skew 344 // If skew is needed, apply skew and then anchor point 345 if (needsSkewMatrix) { 346 t = cc.AffineTransformConcat({a:1.0, b:Math.tan(cc.DEGREES_TO_RADIANS(this._skewY)), 347 c:Math.tan(cc.DEGREES_TO_RADIANS(this._skewX)), d:1.0, tx:0.0, ty:0.0}, t); 348 349 // adjust anchor point 350 if (apx !== 0 || apy !== 0) 351 t = cc.AffineTransformTranslate(t, napx, napy); 352 } 353 354 if (this._additionalTransformDirty) { 355 t = cc.AffineTransformConcat(t, this._additionalTransform); 356 this._additionalTransformDirty = false; 357 } 358 this._transform = t; 359 this._transformDirty = false; 360 } 361 return this._transform; 362 }, 363 364 _nodeToParentTransformForCanvas:function () { 365 if (!this._transform) 366 this._transform = {a:1, b:0, c:0, d:1, tx:0, ty:0}; 367 if (this._transformDirty) { 368 this._armatureTransformDirty = true; 369 var t = this._transform;// quick reference 370 // base position 371 t.tx = this._position.x; 372 t.ty = this._position.y; 373 374 // rotation Cos and Sin 375 var Cos = 1, Sin = 0; 376 if (this._rotationX) { 377 Cos = Math.cos(-this._rotationRadiansX); 378 Sin = Math.sin(-this._rotationRadiansX); 379 } 380 381 // base abcd 382 t.a = t.d = Cos; 383 t.c = -Sin; 384 t.b = Sin; 385 386 var lScaleX = this._scaleX, lScaleY = this._scaleY; 387 var appX = this._anchorPointInPoints.x, appY = this._anchorPointInPoints.y; 388 389 // Firefox on Vista and XP crashes 390 // GPU thread in case of scale(0.0, 0.0) 391 var sx = (lScaleX < 0.000001 && lScaleX > -0.000001) ? 0.000001 : lScaleX, 392 sy = (lScaleY < 0.000001 && lScaleY > -0.000001) ? 0.000001 : lScaleY; 393 394 // Add offset point 395 t.tx += Cos * this._offsetPoint.x * lScaleX + -Sin * this._offsetPoint.y * lScaleY; 396 t.ty += Sin * this._offsetPoint.x * lScaleX + Cos * this._offsetPoint.y * lScaleY; 397 398 // skew 399 if (this._skewX || this._skewY) { 400 // offset the anchorpoint 401 var skx = Math.tan(-this._skewX * Math.PI / 180); 402 var sky = Math.tan(-this._skewY * Math.PI / 180); 403 var xx = appY * skx * sx; 404 var yy = appX * sky * sy; 405 t.a = Cos + -Sin * sky; 406 t.c = Cos * skx + -Sin; 407 t.b = Sin + Cos * sky; 408 t.d = Sin * skx + Cos; 409 t.tx += Cos * xx + -Sin * yy; 410 t.ty += Sin * xx + Cos * yy; 411 } 412 413 // scale 414 if (lScaleX !== 1 || lScaleY !== 1) { 415 t.a *= sx; 416 t.b *= sx; 417 t.c *= sy; 418 t.d *= sy; 419 } 420 421 // adjust anchorPoint 422 t.tx += Cos * -appX * sx + -Sin * -appY * sy; 423 t.ty += Sin * -appX * sx + Cos * -appY * sy; 424 425 // if ignore anchorPoint 426 if (this._ignoreAnchorPointForPosition) { 427 t.tx += appX 428 t.ty += appY; 429 } 430 431 if (this._additionalTransformDirty) { 432 this._transform = cc.AffineTransformConcat(this._transform, this._additionalTransform); 433 this._additionalTransformDirty = false; 434 } 435 436 t.tx = t.tx | 0; 437 t.ty = t.ty | 0; 438 this._transformDirty = false; 439 } 440 return this._transform; 441 }, 442 443 draw:function () { 444 //cc.g_NumberOfDraws++; 445 }, 446 447 /** 448 * conforms to cc.TextureProtocol protocol 449 * @param {cc.BlendFunc} blendFunc 450 */ 451 setBlendFunc: function (blendFunc) { 452 this._blendFunc = blendFunc; 453 }, 454 455 /** 456 * blendFunc getter 457 * @returns {cc.BlendFunc} 458 */ 459 getBlendFunc: function () { 460 return this._blendFunc; 461 }, 462 463 /** 464 * This boundingBox will calculate all bones' boundingBox every time 465 * @return {cc.rect} 466 */ 467 boundingBox:function () { 468 var minx = 0, miny = 0, maxx = 0, maxy = 0; 469 var first = true; 470 var boundingBox = cc.rect(0, 0, 0, 0); 471 for (var i = 0; i < this._children.length; i++) { 472 var bone = this._children[i]; 473 if (bone instanceof ccs.Bone) { 474 var r = bone.getDisplayManager().getBoundingBox(); 475 if (first) { 476 minx = cc.rectGetMinX(r); 477 miny = cc.rectGetMinY(r); 478 maxx = cc.rectGetMaxX(r); 479 maxy = cc.rectGetMaxY(r); 480 481 first = false; 482 } 483 else { 484 minx = cc.rectGetMinX(r) < cc.rectGetMinX(boundingBox) ? cc.rectGetMinX(r) : cc.rectGetMinX(boundingBox); 485 miny = cc.rectGetMinY(r) < cc.rectGetMinY(boundingBox) ? cc.rectGetMinY(r) : cc.rectGetMinY(boundingBox); 486 maxx = cc.rectGetMaxX(r) > cc.rectGetMaxX(boundingBox) ? cc.rectGetMaxX(r) : cc.rectGetMaxX(boundingBox); 487 maxy = cc.rectGetMaxY(r) > cc.rectGetMaxY(boundingBox) ? cc.rectGetMaxY(r) : cc.rectGetMaxY(boundingBox); 488 } 489 boundingBox = cc.rect(minx, miny, maxx - minx, maxy - miny); 490 } 491 } 492 return cc.RectApplyAffineTransform(boundingBox, this.nodeToParentTransform()); 493 }, 494 495 /** 496 * when bone contain the point ,then return it. 497 * @param {Number} x 498 * @param {Number} y 499 * @returns {ccs.Bone} 500 */ 501 getBoneAtPoint: function (x, y) { 502 for (var i = this._children.length - 1; i >= 0; i--) { 503 var child = this._children[i]; 504 if (child instanceof ccs.Bone) { 505 if (child.getDisplayManager().containPoint(x, y)) { 506 return child; 507 } 508 } 509 } 510 return null; 511 }, 512 513 getTexureAtlasWithTexture:function(){ 514 return null; 515 }, 516 517 /** 518 * parent bone setter 519 * @param {ccs.Bone} parentBone 520 */ 521 setParentBone: function (parentBone) { 522 this._parentBone = parentBone; 523 for (var key in this._boneDic) { 524 var bone = this._boneDic[key]; 525 bone.setArmature(this); 526 } 527 }, 528 529 /** 530 * set collider filter 531 * @param {ccs.ColliderFilter} filter 532 */ 533 setColliderFilter: function (filter) { 534 for (var key in this._boneDic) { 535 var bone = this._boneDic[key]; 536 bone.setColliderFilter(filter); 537 } 538 }, 539 540 /** 541 * draw contour 542 */ 543 drawContour: function () { 544 cc.drawingUtil.setDrawColor4B(255, 255, 255, 255); 545 cc.drawingUtil.setLineWidth(1); 546 for (var key in this._boneDic) { 547 var bone = this._boneDic[key]; 548 var bodyList = bone.getColliderBodyList(); 549 for (var i = 0; i < bodyList.length; i++) { 550 var body = bodyList[i]; 551 var vertexList = body.getCalculatedVertexList(); 552 cc.drawingUtil.drawPoly(vertexList, vertexList.length, true); 553 } 554 } 555 }, 556 557 /** 558 * return parent bone 559 * @returns {ccs.Bone} 560 */ 561 getParentBone:function(){ 562 return this._parentBone; 563 }, 564 565 /** 566 * armatureAnimation getter 567 * @return {ccs.ArmatureAnimation} 568 */ 569 getAnimation:function () { 570 return this._animation; 571 }, 572 573 /** 574 * armatureAnimation setter 575 * @param {ccs.ArmatureAnimation} animation 576 */ 577 setAnimation:function (animation) { 578 this._animation = animation; 579 }, 580 581 /** 582 * armatureData getter 583 * @return {ccs.ArmatureData} 584 */ 585 getArmatureData:function () { 586 return this._armatureData; 587 }, 588 589 /** 590 * armatureData setter 591 * @param {ccs.ArmatureData} armatureData 592 */ 593 setArmatureData:function (armatureData) { 594 this._armatureData = armatureData; 595 }, 596 getName:function () { 597 return this._name; 598 }, 599 setName:function (name) { 600 this._name = name; 601 }, 602 getBatchNode:function () { 603 return this._batchNode; 604 }, 605 setBatchNode:function (batchNode) { 606 this._batchNode = batchNode; 607 }, 608 609 /** 610 * version getter 611 * @returns {Number} 612 */ 613 getVersion:function () { 614 return this._version; 615 }, 616 617 /** 618 * version setter 619 * @param {Number} version 620 */ 621 setVersion:function (version) { 622 this._version = version; 623 }, 624 625 /** 626 * armatureTransformDirty getter 627 * @returns {Boolean} 628 */ 629 getArmatureTransformDirty:function () { 630 return this._armatureTransformDirty; 631 }, 632 getBody:function(){ 633 return this._body; 634 }, 635 636 setBody:function(body){ 637 if (this._body == body) 638 return; 639 640 this._body = body; 641 this._body.data = this; 642 var child,displayObject; 643 for (var i = 0; i < this._children.length; i++) { 644 child = this._children[i]; 645 if (child instanceof ccs.Bone) { 646 var displayList = child.getDisplayManager().getDecorativeDisplayList(); 647 for (var j = 0; j < displayList.length; j++) { 648 displayObject = displayList[j]; 649 var detector = displayObject.getColliderDetector(); 650 if (detector) 651 detector.setBody(this._body); 652 } 653 } 654 } 655 }, 656 getShapeList:function(){ 657 if(this._body) 658 return this._body.shapeList; 659 return []; 660 } 661 662 }); 663 664 665 if(cc.Browser.supportWebGL){ 666 //WebGL 667 ccs.Armature.prototype.nodeToParentTransform = ccs.Armature.prototype._nodeToParentTransformForWebGL; 668 }else{ 669 //Canvas 670 ccs.Armature.prototype.nodeToParentTransform = ccs.Armature.prototype._nodeToParentTransformForCanvas; 671 } 672 673 /** 674 * allocates and initializes a armature. 675 * @constructs 676 * @return {ccs.Armature} 677 * @example 678 * // example 679 * var armature = ccs.Armature.create(); 680 */ 681 ccs.Armature.create = function (name, parentBone) { 682 var armature = new ccs.Armature(); 683 if (armature && armature.init(name, parentBone)) { 684 return armature; 685 } 686 return null; 687 }; 688