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 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.changeDisplayByIndex(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.unscheduleUpdate(); 140 this.scheduleUpdate(); 141 142 this.setCascadeOpacityEnabled(true); 143 this.setCascadeColorEnabled(true); 144 return true; 145 }, 146 147 /** 148 * create a bone 149 * @param {String} boneName 150 * @return {ccs.Bone} 151 */ 152 createBone:function (boneName) { 153 var existedBone = this.getBone(boneName); 154 if (existedBone) { 155 return existedBone; 156 } 157 var boneData = this._armatureData.getBoneData(boneName); 158 var parentName = boneData.parentName; 159 var bone = null; 160 if (parentName != "") { 161 this.createBone(parentName); 162 bone = ccs.Bone.create(boneName); 163 this.addBone(bone, parentName); 164 } else { 165 bone = ccs.Bone.create(boneName); 166 this.addBone(bone, ""); 167 } 168 169 bone.setBoneData(boneData); 170 bone.getDisplayManager().changeDisplayByIndex(-1, false); 171 return bone; 172 }, 173 174 /** 175 * add a bone 176 * @param {ccs.Bone} bone 177 * @param {String} parentName 178 */ 179 addBone:function (bone, parentName) { 180 if (!bone) { 181 cc.log("Argument must be non-nil"); 182 return; 183 } 184 if (this._boneDic[bone.getName()]) { 185 cc.log("bone already added. It can't be added again"); 186 return; 187 } 188 189 if (parentName) { 190 var boneParent = this._boneDic[parentName]; 191 if (boneParent) { 192 boneParent.addChildBone(bone); 193 } 194 else { 195 this._topBoneList.push(bone); 196 } 197 } 198 else { 199 this._topBoneList.push(bone); 200 } 201 bone.setArmature(this); 202 this._boneDic[bone.getName()] = bone; 203 this.addChild(bone); 204 }, 205 206 /** 207 * remove a bone 208 * @param {ccs.Bone} bone 209 * @param {Boolean} recursion 210 */ 211 removeBone:function (bone, recursion) { 212 if (!bone) { 213 cc.log("bone must be added to the bone dictionary!"); 214 return; 215 } 216 217 bone.setArmature(null); 218 bone.removeFromParent(recursion); 219 cc.ArrayRemoveObject(this._topBoneList, bone); 220 delete this._boneDic[bone.getName()]; 221 this.removeChild(bone, true); 222 }, 223 224 /** 225 * get a bone by name 226 * @param {String} name 227 * @return {ccs.Bone} 228 */ 229 getBone:function (name) { 230 return this._boneDic[name]; 231 }, 232 233 /** 234 * Change a bone's parent with the specified parent name. 235 * @param {ccs.Bone} bone 236 * @param {String} parentName 237 */ 238 changeBoneParent:function (bone, parentName) { 239 if (!bone) { 240 cc.log("bone must be added to the bone dictionary!"); 241 return; 242 } 243 var parentBone = bone.getParentBone(); 244 if(parentBone){ 245 cc.ArrayRemoveObject(parentBone.getChildrenBone(), bone); 246 bone.setParentBone(null); 247 } 248 249 if (parentName) { 250 var boneParent = this._boneDic[parentName]; 251 if (boneParent) { 252 boneParent.addChildBone(bone); 253 cc.ArrayRemoveObject(this._topBoneList,bone); 254 }else{ 255 this._topBoneList.push(bone); 256 } 257 } 258 }, 259 260 /** 261 * Get CCArmature's bone dictionary 262 * @return {Object} 263 */ 264 getBoneDic:function () { 265 return this._boneDic; 266 }, 267 268 /** 269 * Set contentSize and Calculate anchor point. 270 */ 271 updateOffsetPoint:function () { 272 // Set contentsize and Calculate anchor point. 273 var rect = this.boundingBox(); 274 this.setContentSize(rect.size); 275 var locOffsetPoint = this._offsetPoint; 276 locOffsetPoint.x = -rect.x; 277 locOffsetPoint.y = -rect.y; 278 if (rect.width != 0 && rect.height != 0) { 279 this.setAnchorPoint(cc.p(locOffsetPoint.x / rect.width, locOffsetPoint.y / rect.height)); 280 } 281 }, 282 283 update:function (dt) { 284 this._animation.update(dt); 285 var locTopBoneList = this._topBoneList; 286 for (var i = 0; i < locTopBoneList.length; i++) { 287 locTopBoneList[i].update(dt); 288 } 289 this._armatureTransformDirty = false; 290 }, 291 292 nodeToParentTransform:function () { 293 return cc.Browser.supportWebGL ? this.nodeToParentTransformWEBGL() : this.nodeToParentTransformCanvas(); 294 }, 295 296 nodeToParentTransformWEBGL:function () { 297 if (this._transformDirty) { 298 this._armatureTransformDirty = true; 299 // Translate values 300 var x = this._position.x; 301 var y = this._position.y; 302 var apx = this._anchorPointInPoints.x, napx = -apx; 303 var apy = this._anchorPointInPoints.y, napy = -apy; 304 var scx = this._scaleX, scy = this._scaleY; 305 306 if (this._ignoreAnchorPointForPosition) { 307 x += apx; 308 y += apy; 309 } 310 311 // Rotation values 312 // Change rotation code to handle X and Y 313 // If we skew with the exact same value for both x and y then we're simply just rotating 314 var cx = 1, sx = 0, cy = 1, sy = 0; 315 if (this._rotationX !== 0 || this._rotationY !== 0) { 316 cx = Math.cos(-this._rotationRadiansX); 317 sx = Math.sin(-this._rotationRadiansX); 318 cy = Math.cos(-this._rotationRadiansY); 319 sy = Math.sin(-this._rotationRadiansY); 320 } 321 322 // Add offset point 323 x += cy * this._offsetPoint.x * this._scaleX + -sx * this._offsetPoint.y * this._scaleY; 324 y += sy * this._offsetPoint.x * this._scaleX + cx * this._offsetPoint.y * this._scaleY; 325 326 var needsSkewMatrix = ( this._skewX || this._skewY ); 327 328 // optimization: 329 // inline anchor point calculation if skew is not needed 330 // Adjusted transform calculation for rotational skew 331 if (!needsSkewMatrix && (apx !== 0 || apy !== 0)) { 332 x += cy * napx * scx + -sx * napy * scy; 333 y += sy * napx * scx + cx * napy * scy; 334 } 335 336 // Build Transform Matrix 337 // Adjusted transform calculation for rotational skew 338 var t = {a:cy * scx, b:sy * scx, c:-sx * scy, d:cx * scy, tx:x, ty:y}; 339 340 // XXX: Try to inline skew 341 // If skew is needed, apply skew and then anchor point 342 if (needsSkewMatrix) { 343 t = cc.AffineTransformConcat({a:1.0, b:Math.tan(cc.DEGREES_TO_RADIANS(this._skewY)), 344 c:Math.tan(cc.DEGREES_TO_RADIANS(this._skewX)), d:1.0, tx:0.0, ty:0.0}, t); 345 346 // adjust anchor point 347 if (apx !== 0 || apy !== 0) 348 t = cc.AffineTransformTranslate(t, napx, napy); 349 } 350 351 if (this._additionalTransformDirty) { 352 t = cc.AffineTransformConcat(t, this._additionalTransform); 353 this._additionalTransformDirty = false; 354 } 355 this._transform = t; 356 this._transformDirty = false; 357 } 358 return this._transform; 359 }, 360 361 nodeToParentTransformCanvas:function () { 362 if (!this._transform) 363 this._transform = {a:1, b:0, c:0, d:1, tx:0, ty:0}; 364 if (this._transformDirty) { 365 this._armatureTransformDirty = true; 366 var t = this._transform;// quick reference 367 // base position 368 t.tx = this._position.x; 369 t.ty = this._position.y; 370 371 // rotation Cos and Sin 372 var Cos = 1, Sin = 0; 373 if (this._rotationX) { 374 Cos = Math.cos(this._rotationRadiansX); 375 Sin = Math.sin(this._rotationRadiansX); 376 } 377 378 // base abcd 379 t.a = t.d = Cos; 380 t.c = -Sin; 381 t.b = Sin; 382 383 var lScaleX = this._scaleX, lScaleY = this._scaleY; 384 var appX = this._anchorPointInPoints.x, appY = this._anchorPointInPoints.y; 385 386 // Firefox on Vista and XP crashes 387 // GPU thread in case of scale(0.0, 0.0) 388 var sx = (lScaleX < 0.000001 && lScaleX > -0.000001) ? 0.000001 : lScaleX, 389 sy = (lScaleY < 0.000001 && lScaleY > -0.000001) ? 0.000001 : lScaleY; 390 391 392 // Add offset point 393 t.tx += Cos * this._offsetPoint.x * lScaleX + -Sin * this._offsetPoint.y * lScaleY; 394 t.ty += Sin * this._offsetPoint.x * lScaleX + Cos * this._offsetPoint.y * lScaleY; 395 396 397 // skew 398 if (this._skewX || this._skewY) { 399 // offset the anchorpoint 400 var skx = Math.tan(-this._skewX * Math.PI / 180); 401 var sky = Math.tan(-this._skewY * Math.PI / 180); 402 var xx = appY * skx * sx; 403 var yy = appX * sky * sy; 404 t.a = Cos + -Sin * sky; 405 t.c = Cos * skx + -Sin; 406 t.b = Sin + Cos * sky; 407 t.d = Sin * skx + Cos; 408 t.tx += Cos * xx + -Sin * yy; 409 t.ty += Sin * xx + Cos * yy; 410 } 411 412 // scale 413 if (lScaleX !== 1 || lScaleY !== 1) { 414 t.a *= sx; 415 t.b *= sx; 416 t.c *= sy; 417 t.d *= sy; 418 } 419 420 // adjust anchorPoint 421 t.tx += Cos * -appX * sx + -Sin * appY * sy; 422 t.ty -= Sin * -appX * sx + Cos * appY * sy; 423 424 // if ignore anchorPoint 425 if (this._ignoreAnchorPointForPosition) { 426 t.tx += appX 427 t.ty += appY; 428 } 429 430 if (this._additionalTransformDirty) { 431 this._transform = cc.AffineTransformConcat(this._transform, this._additionalTransform); 432 this._additionalTransformDirty = false; 433 } 434 435 t.tx = t.tx | 0; 436 t.ty = t.ty | 0; 437 this._transformDirty = false; 438 } 439 return this._transform; 440 }, 441 442 draw:function () { 443 //cc.g_NumberOfDraws++; 444 }, 445 446 /** 447 * update blendType 448 * @param {ccs.BlendType} blendType 449 */ 450 updateBlendType: function (blendType) { 451 var blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); 452 switch (blendType) { 453 case ccs.BlendType.normal: 454 blendFunc.src = cc.BLEND_SRC; 455 blendFunc.dst = cc.BLEND_DST; 456 break; 457 case ccs.BlendType.add: 458 blendFunc.src = gl.SRC_ALPHA; 459 blendFunc.dst = gl.ONE; 460 break; 461 case ccs.BlendType.multiply: 462 blendFunc.src = gl.ONE_MINUS_SRC_ALPHA; 463 blendFunc.dst = gl.ONE_MINUS_DST_COLOR; 464 break; 465 case ccs.BlendType.screen: 466 blendFunc.src = gl.ONE; 467 blendFunc.dst = gl.ONE_MINUS_DST_COLOR; 468 break; 469 default: 470 break; 471 } 472 this.setBlendFunc(blendFunc.src, blendFunc.dst); 473 }, 474 475 /** 476 * conforms to cc.TextureProtocol protocol 477 * @param {cc.BlendFunc} blendFunc 478 */ 479 setBlendFunc: function (blendFunc) { 480 this._blendFunc = blendFunc; 481 }, 482 483 /** 484 * blendFunc getter 485 * @returns {cc.BlendFunc} 486 */ 487 getBlendFunc: function () { 488 return this._blendFunc; 489 }, 490 491 /** 492 * This boundingBox will calculate all bones' boundingBox every time 493 * @return {cc.rect} 494 */ 495 boundingBox:function () { 496 var minx, miny, maxx, maxy = 0; 497 var first = true; 498 var boundingBox = cc.rect(0, 0, 0, 0); 499 for (var i = 0; i < this._children.length; i++) { 500 var bone = this._children[i]; 501 if (bone instanceof ccs.Bone) { 502 var r = bone.getDisplayManager().getBoundingBox(); 503 if (first) { 504 minx = cc.rectGetMinX(r); 505 miny = cc.rectGetMinY(r); 506 maxx = cc.rectGetMaxX(r); 507 maxy = cc.rectGetMaxY(r); 508 509 first = false; 510 } 511 else { 512 minx = cc.rectGetMinX(r) < cc.rectGetMinX(boundingBox) ? cc.rectGetMinX(r) : cc.rectGetMinX(boundingBox); 513 miny = cc.rectGetMinY(r) < cc.rectGetMinY(boundingBox) ? cc.rectGetMinY(r) : cc.rectGetMinY(boundingBox); 514 maxx = cc.rectGetMaxX(r) > cc.rectGetMaxX(boundingBox) ? cc.rectGetMaxX(r) : cc.rectGetMaxX(boundingBox); 515 maxy = cc.rectGetMaxY(r) > cc.rectGetMaxY(boundingBox) ? cc.rectGetMaxY(r) : cc.rectGetMaxY(boundingBox); 516 } 517 boundingBox = cc.rect(minx, miny, maxx - minx, maxy - miny); 518 } 519 } 520 return cc.RectApplyAffineTransform(boundingBox, this.nodeToParentTransform()); 521 }, 522 523 /** 524 * when bone contain the point ,then return it. 525 * @param {Number} x 526 * @param {Number} y 527 * @returns {ccs.Bone} 528 */ 529 getBoneAtPoint: function (x, y) { 530 for (var i = this._children.length - 1; i >= 0; i--) { 531 var child = this._children[i]; 532 if (child instanceof ccs.Bone) { 533 if (child.getDisplayManager().containPoint(x, y)) { 534 return child; 535 } 536 } 537 } 538 return null; 539 }, 540 541 getTexureAtlasWithTexture:function(){ 542 return null; 543 }, 544 545 /** 546 * parent bone setter 547 * @param {ccs.Bone} parentBone 548 */ 549 setParentBone: function (parentBone) { 550 this._parentBone = parentBone; 551 for (var key in this._boneDic) { 552 var bone = this._boneDic[key]; 553 bone.setArmature(this); 554 } 555 }, 556 557 /** 558 * set collider filter 559 * @param {ccs.ColliderFilter} filter 560 */ 561 setColliderFilter: function (filter) { 562 for (var key in this._boneDic) { 563 var bone = this._boneDic[key]; 564 bone.setColliderFilter(filter); 565 } 566 }, 567 568 /** 569 * return parent bone 570 * @returns {ccs.Bone} 571 */ 572 getParentBone:function(){ 573 return this._parentBone; 574 }, 575 576 /** 577 * armatureAnimation getter 578 * @return {ccs.ArmatureAnimation} 579 */ 580 getAnimation:function () { 581 return this._animation; 582 }, 583 584 /** 585 * armatureAnimation setter 586 * @param {ccs.ArmatureAnimation} animation 587 */ 588 setAnimation:function (animation) { 589 this._animation = animation; 590 }, 591 592 /** 593 * armatureData getter 594 * @return {ccs.ArmatureData} 595 */ 596 getArmatureData:function () { 597 return this._armatureData; 598 }, 599 600 /** 601 * armatureData setter 602 * @param {ccs.ArmatureData} armatureData 603 */ 604 setArmatureData:function (armatureData) { 605 this._armatureData = armatureData; 606 }, 607 getName:function () { 608 return this._name; 609 }, 610 setName:function (name) { 611 this._name = name; 612 }, 613 getBatchNode:function () { 614 return this._batchNode; 615 }, 616 setBatchNode:function (batchNode) { 617 this._batchNode = batchNode; 618 }, 619 620 /** 621 * version getter 622 * @returns {Number} 623 */ 624 getVersion:function () { 625 return this._version; 626 }, 627 628 /** 629 * version setter 630 * @param {Number} version 631 */ 632 setVersion:function (version) { 633 this._version = version; 634 }, 635 636 /** 637 * armatureTransformDirty getter 638 * @returns {Boolean} 639 */ 640 getArmatureTransformDirty:function () { 641 return this._armatureTransformDirty; 642 }, 643 getBody:function(){ 644 return this._body; 645 }, 646 647 setBody:function(body){ 648 if (this._body == body) 649 return; 650 651 this._body = body; 652 this._body.data = this; 653 var child,displayObject; 654 for (var i = 0; i < this._children.length; i++) { 655 child = this._children[i]; 656 if (child instanceof ccs.Bone) { 657 var displayList = child.getDisplayManager().getDecorativeDisplayList(); 658 for (var j = 0; j < displayList.length; j++) { 659 displayObject = displayList[j]; 660 var detector = displayObject.getColliderDetector(); 661 if (detector) 662 detector.setBody(this._body); 663 } 664 } 665 } 666 }, 667 getShapeList:function(){ 668 if(this._body) 669 return this._body.shapeList; 670 return []; 671 } 672 673 }); 674 675 /** 676 * allocates and initializes a armature. 677 * @constructs 678 * @return {ccs.Armature} 679 * @example 680 * // example 681 * var armature = ccs.Armature.create(); 682 */ 683 ccs.Armature.create = function (name, parentBone) { 684 var armature = new ccs.Armature(); 685 if (armature && armature.init(name, parentBone)) { 686 return armature; 687 } 688 return null; 689 };