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.Bone objects.
 27  * @class
 28  * @extends ccs.NodeRGBA
 29  */
 30 ccs.Bone = ccs.NodeRGBA.extend(/** @lends ccs.Bone# */{
 31     _boneData:null,
 32     _armature:null,
 33     _childArmature:null,
 34     _displayManager:null,
 35     _ignoreMovementBoneData:false,
 36     _tween:null,
 37     _tweenData:null,
 38     _name:"",
 39     _childrenBone:null,
 40     _parentBone:null,
 41     _boneTransformDirty:false,
 42     _worldTransform:null,
 43     _blendFunc:0,
 44     _blendDirty:false,
 45     _worldInfo:null,
 46     _armatureParentBone:null,
 47     _dataVersion:0,
 48     ctor:function () {
 49         cc.NodeRGBA.prototype.ctor.call(this);
 50         this._boneData = null;
 51         this._armature = null;
 52         this._childArmature = null;
 53         this._displayManager = null;
 54         this._ignoreMovementBoneData = false;
 55         this._tween = null;
 56         this._tweenData = null;
 57         this._name = "";
 58         this._childrenBone = [];
 59         this._parentBone = null;
 60         this._boneTransformDirty = true;
 61         this._worldTransform = cc.AffineTransformMake(1, 0, 0, 1, 0, 0);
 62         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
 63         this._blendDirty = false;
 64     },
 65 
 66     /**
 67      * release objects
 68      */
 69     release:function () {
 70         CC_SAFE_RELEASE(this._tweenData);
 71         for (var i = 0; i < this._childrenBone.length; i++) {
 72             CC_SAFE_RELEASE(this._childrenBone[i]);
 73         }
 74         this._childrenBone = [];
 75         CC_SAFE_RELEASE(this._tween);
 76         CC_SAFE_RELEASE(this._displayManager);
 77         CC_SAFE_RELEASE(this._boneData);
 78         CC_SAFE_RELEASE(this._childArmature);
 79     },
 80 
 81     /**
 82      * Initializes a CCBone with the specified name
 83      * @param {String} name
 84      * @return {Boolean}
 85      */
 86     init:function (name) {
 87         cc.NodeRGBA.prototype.init.call(this);
 88         if (name) {
 89             this._name = name;
 90         }
 91         this._tweenData = new ccs.FrameData();
 92         this._tween = new ccs.Tween();
 93         this._tween.init(this);
 94         this._displayManager = new ccs.DisplayManager();
 95         this._displayManager.init(this);
 96         this._worldInfo = new ccs.BaseData();
 97         this._boneData = new ccs.BaseData();
 98         return true;
 99     },
100 
101     /**
102      * set the boneData
103      * @param {ccs.BoneData} boneData
104      */
105     setBoneData:function (boneData) {
106         if (!boneData) {
107             cc.log("boneData must not be null");
108             return;
109         }
110         this._boneData = boneData;
111         this._name = this._boneData.name;
112         this._zOrder = this._boneData.zOrder;
113         this._displayManager.initDisplayList(boneData);
114     },
115 
116     /**
117      * boneData getter
118      * @return {ccs.BoneData}
119      */
120     getBoneData:function () {
121         return this._boneData;
122     },
123 
124     /**
125      * set the armature
126      * @param {ccs.Armature} armature
127      */
128     setArmature:function (armature) {
129         this._armature = armature;
130         if(armature){
131             this._tween.setAnimation(this._armature.getAnimation());
132             this._dataVersion = this._armature.getArmatureData().dataVersion;
133             this._armatureParentBone = this._armature.getParentBone();
134         }else{
135             this._armatureParentBone = null;
136         }
137     },
138 
139     /**
140      * armature getter
141      * @return {ccs.Armature}
142      */
143     getArmature:function () {
144         return this._armature;
145     },
146 
147     /**
148      * update worldTransform
149      * @param dt
150      */
151     update:function (dt) {
152         var locParentBone = this._parentBone;
153         var locArmature = this._armature;
154         var locTweenData = this._tweenData;
155         var locWorldTransform = this._worldTransform;
156         var locWorldInfo = this._worldInfo;
157         var locArmatureParentBone = this._armatureParentBone;
158 
159         if (locParentBone) {
160             this._boneTransformDirty = this._boneTransformDirty || locParentBone.isTransformDirty();
161         }
162         if (locArmatureParentBone && !this._boneTransformDirty){
163             this._boneTransformDirty = locArmatureParentBone.isTransformDirty();
164         }
165         if (this._boneTransformDirty) {
166             if (this._dataVersion >= ccs.CONST_VERSION_COMBINED) {
167                 var locBoneData = this._boneData;
168                 locTweenData.x += locBoneData.x;
169                 locTweenData.y += locBoneData.y;
170                 locTweenData.skewX += locBoneData.skewX;
171                 locTweenData.skewY += locBoneData.skewY;
172                 locTweenData.scaleX += locBoneData.scaleX;
173                 locTweenData.scaleY += locBoneData.scaleY;
174 
175                 locTweenData.scaleX -= 1;
176                 locTweenData.scaleY -= 1;
177             }
178 
179             locWorldInfo.x = locTweenData.x + this._position.x;
180             locWorldInfo.y = locTweenData.y + this._position.y;
181             locWorldInfo.scaleX = locTweenData.scaleX * this._scaleX;
182             locWorldInfo.scaleY = locTweenData.scaleY * this._scaleY;
183             locWorldInfo.skewX = locTweenData.skewX + this._skewX + this._rotationX;
184             locWorldInfo.skewY = locTweenData.skewY + this._skewY - this._rotationY;
185 
186             if (this._parentBone) {
187                 this.applyParentTransform(this._parentBone);
188             }
189             else {
190                 if (locArmatureParentBone) {
191                     this.applyParentTransform(locArmatureParentBone);
192                 }
193             }
194 
195             ccs.TransformHelp.nodeToMatrix(locWorldInfo, locWorldTransform);
196 
197             if (locArmatureParentBone) {
198                 this._worldTransform = cc.AffineTransformConcat(locWorldTransform, locArmature.nodeToParentTransform());
199             }
200         }
201         ccs.DisplayFactory.updateDisplay(this, dt, this._boneTransformDirty || locArmature.getArmatureTransformDirty());
202 
203         var locChildrenBone = this._childrenBone;
204         for (var i = 0; i < locChildrenBone.length; i++) {
205             locChildrenBone[i].update(dt);
206         }
207         this._boneTransformDirty = false;
208     },
209 
210     applyParentTransform: function (parent) {
211         var locWorldInfo = this._worldInfo;
212         var locParentWorldTransform = parent._worldTransform;
213         var locParentWorldInfo = parent._worldInfo;
214         var x = locWorldInfo.x;
215         var y = locWorldInfo.y;
216         locWorldInfo.x = x * locParentWorldTransform.a + y * locParentWorldTransform.c + locParentWorldInfo.x;
217         locWorldInfo.y = x * locParentWorldTransform.b + y * locParentWorldTransform.d + locParentWorldInfo.y;
218         locWorldInfo.scaleX = locWorldInfo.scaleX * locParentWorldInfo.scaleX;
219         locWorldInfo.scaleY = locWorldInfo.scaleY * locParentWorldInfo.scaleY;
220         locWorldInfo.skewX = locWorldInfo.skewX + locParentWorldInfo.skewX;
221         locWorldInfo.skewY = locWorldInfo.skewY + locParentWorldInfo.skewY;
222     },
223 
224     /**
225      * Rewrite visit ,when node draw, g_NumberOfDraws is changeless
226      */
227     visit:function (ctx) {
228         var node = this.getDisplayManager().getDisplayRenderNode();
229         if (node) {
230             node.visit(ctx);
231         }
232     },
233 
234     /**
235      * update display color
236      * @param {cc.c3b} color
237      */
238     updateDisplayedColor:function (color) {
239         this._realColor = cc.c3b(255,255,255);
240         cc.NodeRGBA.prototype.updateDisplayedColor.call(this, color);
241         this.updateColor();
242     },
243 
244     /**
245      * update display opacity
246      * @param {Number} opacity
247      */
248     updateDisplayedOpacity:function (opacity) {
249         this._realOpacity = 255;
250         cc.NodeRGBA.prototype.updateDisplayedOpacity.call(this, opacity);
251         this.updateColor();
252     },
253 
254     /**
255      * set display color
256      * @param {cc.c3b} color
257      */
258     setColor: function (color) {
259         cc.NodeRGBA.prototype.setColor.call(this, color);
260         this.updateColor();
261     },
262 
263     /**
264      * set display opacity
265      * @param {Number} opacity  0-255
266      */
267     setOpacity: function (opacity) {
268         cc.NodeRGBA.prototype.setOpacity.call(this, opacity);
269         this.updateColor();
270     },
271 
272     /**
273      * update display color
274      */
275     updateColor:function () {
276         var display = this._displayManager.getDisplayRenderNode();
277         if (display && display.RGBAProtocol) {
278             var locDisplayedColor = this._displayedColor;
279             var locTweenData = this._tweenData;
280             var locOpacity = this._displayedOpacity * locTweenData.a / 255;
281             var locColor = cc.c3b(locDisplayedColor.r * locTweenData.r / 255, locDisplayedColor.g * locTweenData.g / 255, locDisplayedColor.b * locTweenData.b / 255);
282             display.setOpacity(locOpacity);
283             display.setColor(locColor);
284         }
285     },
286 
287     /**
288      * update display zOrder
289      */
290     updateZOrder: function () {
291         if (this._armature.getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) {
292             var zorder = this._tweenData.zOrder + this._boneData.zOrder;
293             this.setZOrder(zorder);
294         }
295         else {
296             this.setZOrder(this._tweenData.zOrder);
297         }
298     },
299 
300     /**
301      * Add a child to this bone, and it will let this child call setParent(ccs.Bone) function to set self to it's parent
302      * @param {ccs.Bone} child
303      */
304     addChildBone:function (child) {
305         if (!child) {
306             cc.log("Argument must be non-nil");
307             return;
308         }
309         if (child._parentBone) {
310             cc.log("child already added. It can't be added again");
311             return;
312         }
313         if (cc.ArrayGetIndexOfObject(this._childrenBone, child) < 0) {
314             this._childrenBone.push(child);
315             child.setParentBone(this);
316         }
317     },
318 
319     /**
320      * Removes a child bone
321      * @param {ccs.Bone} bone
322      * @param {Boolean} recursion
323      */
324     removeChildBone:function (bone, recursion) {
325         for (var i = 0; i < this._childrenBone.length; i++) {
326             if (this._childrenBone[i] == bone) {
327                 if (recursion) {
328                     var ccbones = bone._childrenBone;
329                     for (var j = 0; j < ccbones.length; j++) {
330                         bone.removeChildBone(ccbones[j], recursion);
331                     }
332                 }
333                 bone.setParentBone(null);
334                 bone.getDisplayManager().setCurrentDecorativeDisplay(null);
335                 cc.ArrayRemoveObject(this._childrenBone, bone);
336             }
337         }
338     },
339 
340     /**
341      * Remove itself from its parent CCBone.
342      * @param {Boolean} recursion
343      */
344     removeFromParent:function (recursion) {
345         if (this._parentBone) {
346             this._parentBone.removeChildBone(this, recursion);
347         }
348     },
349 
350     /**
351      * Set parent bone.
352      * If _parent is NUll, then also remove this bone from armature.
353      * It will not set the CCArmature, if you want to add the bone to a CCArmature, you should use ccs.Armature.addBone(bone, parentName).
354      * @param {ccs.Bone}  parent  the parent bone.
355      */
356     setParentBone:function (parent) {
357         this._parentBone = parent;
358     },
359 
360     /**
361      * parent bone getter
362      * @return {ccs.Bone}
363      */
364     getParentBone:function () {
365         return this._parentBone;
366     },
367 
368     /**
369      * child armature setter
370      * @param {ccs.Armature} armature
371      */
372     setChildArmature:function (armature) {
373         if (this._childArmature != armature) {
374             if (armature == null && this._childArmature)            {
375                 this._childArmature.setParentBone(null);
376             }
377             this._childArmature = armature;
378         }
379     },
380 
381     /**
382      * child armature getter
383      * @return {ccs.Armature}
384      */
385     getChildArmature:function () {
386         return this._childArmature;
387     },
388 
389     /**
390      * child bone getter
391      * @return {Array}
392      */
393     getChildrenBone:function () {
394         return this._childrenBone;
395     },
396 
397     /**
398      * tween getter
399      * @return {ccs.Tween}
400      */
401     getTween:function () {
402         return this._tween;
403     },
404 
405     /**
406      * zOrder setter
407      * @param {Number}
408         */
409     setZOrder:function (zOrder) {
410         if (this._zOrder != zOrder)
411             cc.Node.prototype.setZOrder.call(this, zOrder);
412     },
413 
414     /**
415      * transform dirty setter
416      * @param {Boolean}
417         */
418     setTransformDirty:function (dirty) {
419         this._boneTransformDirty = dirty;
420     },
421 
422     /**
423      * transform dirty getter
424      * @return {Boolean}
425      */
426     isTransformDirty:function () {
427         return this._boneTransformDirty;
428     },
429 
430     /**
431      * return world transform
432      * @return {{a:0.b:0,c:0,d:0,tx:0,ty:0}}
433      */
434     nodeToArmatureTransform:function () {
435         return this._worldTransform;
436     },
437 
438     /**
439      * Returns the world affine transform matrix. The matrix is in Pixels.
440      * @returns {cc.AffineTransform}
441      */
442     nodeToWorldTransform: function () {
443         return cc.AffineTransformConcat(this._worldTransform, this._armature.nodeToWorldTransform());
444     },
445 
446     /**
447      * get render node
448      * @returns {cc.Node}
449      */
450     getDisplayRenderNode: function () {
451         return this._displayManager.getDisplayRenderNode();
452     },
453 
454     /**
455      * get render node type
456      * @returns {Number}
457      */
458     getDisplayRenderNodeType: function () {
459         return this._displayManager.getDisplayRenderNodeType();
460     },
461 
462     /**
463      * Add display and use  _displayData init the display.
464      * If index already have a display, then replace it.
465      * If index is current display index, then also change display to _index
466      * @param {cc.Display} displayData it include the display information, like DisplayType.
467      *          If you want to create a sprite display, then create a CCSpriteDisplayData param
468      *@param {Number}    index the index of the display you want to replace or add to
469      *          -1 : append display from back
470      */
471     addDisplay:function (displayData, index) {
472         index = index || 0;
473         return this._displayManager.addDisplay(displayData, index);
474     },
475 
476     /**
477      * remove display
478      * @param {Number} index
479      */
480     removeDisplay: function (index) {
481         this._displayManager.removeDisplay(index);
482     },
483 
484     addSkin:function (skin, index) {
485         index = index||0;
486         return this._displayManager.addSkin(skin, index);
487     },
488 
489     /**
490      * change display by index
491      * @param {Number} index
492      * @param {Boolean} force
493      */
494     changeDisplayByIndex:function (index, force) {
495         cc.log("changeDisplayByIndex is deprecated. Use changeDisplayWithIndex instead.");
496         this.changeDisplayWithIndex(index, force);
497     },
498 
499     /**
500      * change display with index
501      * @param {Number} index
502      * @param {Boolean} force
503      */
504     changeDisplayWithIndex:function (index, force) {
505         this._displayManager.changeDisplayWithIndex(index, force);
506     },
507 
508     /**
509      * change display with name
510      * @param {String} name
511      * @param {Boolean} force
512      */
513     changeDisplayWithName:function (name, force) {
514         this._displayManager.changeDisplayWithName(name, force);
515     },
516 
517     /**
518      * get the collider body list in this bone.
519      * @returns {*}
520      */
521     getColliderBodyList: function () {
522         var decoDisplay = this._displayManager.getCurrentDecorativeDisplay()
523         if (decoDisplay) {
524             var detector = decoDisplay.getColliderDetector()
525             if (detector) {
526                 return detector.getColliderBodyList();
527             }
528         }
529         return [];
530     },
531 
532     /**
533      * collider filter setter
534      * @param {cc.ColliderFilter} filter
535      */
536     setColliderFilter: function (filter) {
537         var displayList = this._displayManager.getDecorativeDisplayList();
538         for (var i = 0; i < displayList.length; i++) {
539             var locDecoDisplay = displayList[i];
540             var locDetector = locDecoDisplay.getColliderDetector();
541             if (locDetector) {
542                 locDetector.setColliderFilter(filter);
543             }
544         }
545 
546     },
547 
548     /**
549      * collider filter getter
550      * @returns {cc.ColliderFilter}
551      */
552     getColliderFilter: function () {
553         var decoDisplay = this._displayManager.getCurrentDecorativeDisplay();
554         if (decoDisplay) {
555             var detector = decoDisplay.getColliderDetector();
556             if (detector) {
557                 return detector.getColliderFilter();
558             }
559         }
560         return null;
561     },
562 
563     /**
564      * displayManager setter
565      * @param {ccs.DisplayManager}
566         */
567     setDisplayManager:function (displayManager) {
568         this._displayManager = displayManager;
569     },
570 
571     /**
572      * displayManager dirty getter
573      * @return {ccs.DisplayManager}
574      */
575     getDisplayManager:function () {
576         return this._displayManager;
577     },
578 
579     /**
580      *    When CCArmature play a animation, if there is not a CCMovementBoneData of this bone in this CCMovementData, this bone will hide.
581      *    Set IgnoreMovementBoneData to true, then this bone will also show.
582      * @param {Boolean} bool
583      */
584     setIgnoreMovementBoneData:function (bool) {
585         this._ignoreMovementBoneData = bool;
586     },
587 
588     /**
589      * ignoreMovementBoneData  getter
590      * @return {Boolean}
591      */
592     getIgnoreMovementBoneData:function () {
593         return this._ignoreMovementBoneData;
594     },
595 
596     /**
597      * tweenData  getter
598      * @return {ccs.FrameData}
599      */
600     getTweenData:function () {
601         return this._tweenData;
602     },
603 
604     /**
605      * name  setter
606      * @param {String} name
607      */
608     setName:function (name) {
609         this._name = name;
610     },
611 
612     /**
613      * name  getter
614      * @return {String}
615      */
616     getName:function () {
617         return this._name;
618     },
619 
620     /**
621      * BlendFunc  setter
622      * @param {cc.BlendFunc} blendFunc
623      */
624     setBlendFunc:function (blendFunc) {
625         if (this._blendFunc.src != blendFunc.src || this._blendFunc.dst != blendFunc.dst)        {
626             this._blendFunc = blendFunc;
627             this._blendDirty = true;
628         }
629     },
630 
631     /**
632      * blendType  getter
633      * @return {cc.BlendFunc}
634      */
635     getBlendFunc:function () {
636         return this._blendFunc;
637     },
638 
639     setBlendDirty:function(dirty){
640         this._blendDirty = dirty;
641     },
642 
643     isBlendDirty:function(){
644         return this._blendDirty;
645     }
646 });
647 
648 /**
649  * allocates and initializes a bone.
650  * @constructs
651  * @return {ccs.Bone}
652  * @example
653  * // example
654  * var bone = ccs.Bone.create();
655  */
656 ccs.Bone.create = function (name) {
657     var bone = new ccs.Bone();
658     if (bone && bone.init(name)) {
659         return bone;
660     }
661     return null;
662 };