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