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