1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * @ignore
 28  */
 29 ccs.PT_RATIO = 32;
 30 
 31 /**
 32  * Base class for ccs.ColliderFilter
 33  * @class
 34  * @extends ccs.Class
 35  */
 36 ccs.ColliderFilter = ccs.Class.extend(/** @lends ccs.ColliderFilter# */{
 37     _collisionType: 0,
 38     _group: 0,
 39     _categoryBits: 0,
 40     _groupIndex: 0,
 41     _maskBits: 0,
 42 
 43     ctor: function (collisionType, group) {
 44         this._collisionType = collisionType || 0;
 45         this._group = group || 0;
 46     },
 47 
 48     updateShape: function (shape) {
 49         if(shape instanceof cp.Shape){
 50             shape.collision_type = this._collisionType;
 51             shape.group = this._group;
 52         }else if(shape instanceof Box2D.b2FilterData){
 53             var filter = new Box2D.b2FilterData();
 54             filter.categoryBits = this._categoryBits;
 55             filter.groupIndex = this._groupIndex;
 56             filter.maskBits = this._maskBits;
 57 
 58             shape.SetFilterData(filter);
 59         }
 60     }
 61 });
 62 
 63 /**
 64  * Base class for ccs.ColliderBody
 65  * @class
 66  * @extends ccs.Class
 67  *
 68  * @property {ccs.ContourData}      contourData     - The contour data of collider body
 69  * @property {ccs.Shape}            shape           - The shape of collider body
 70  * @property {ccs.ColliderFilter}   colliderFilter  - The collider filter of collider body
 71  *
 72  */
 73 ccs.ColliderBody = ccs.Class.extend(/** @lends ccs.ColliderBody# */{
 74     shape: null,
 75     coutourData: null,
 76     colliderFilter: null,
 77     _calculatedVertexList: null,
 78     ctor: function (contourData) {
 79         this.shape = null;
 80         this.coutourData = contourData;
 81         this.colliderFilter = new ccs.ColliderFilter();
 82         if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) {
 83             this._calculatedVertexList = [];
 84         }
 85     },
 86 
 87     /**
 88      * contourData getter
 89      * @returns {ccs.ContourData}
 90      */
 91     getContourData: function () {
 92         return this.coutourData;
 93     },
 94 
 95     /**
 96      * colliderFilter setter
 97      * @param {ccs.ColliderFilter} colliderFilter
 98      */
 99     setColliderFilter: function (colliderFilter) {
100         this.colliderFilter = colliderFilter;
101     },
102 
103     /**
104      * get calculated vertex list
105      * @returns {Array}
106      */
107     getCalculatedVertexList: function () {
108         return this._calculatedVertexList;
109     },
110 
111     setB2Fixture: function(fixture){
112         this._fixture = fixture;
113     },
114 
115     getB2Fixture: function(){
116         return this._fixture;
117     },
118 
119     /**
120      * shape getter
121      * @param {ccs.Shape} shape
122      */
123     setShape: function (shape) {
124         this.shape = shape;
125     },
126 
127     /**
128      * shape setter
129      * @return {ccs.Shape}
130      */
131     getShape: function () {
132         return this.shape;
133     },
134 
135     /**
136      * contourData setter
137      * @param {ccs.ContourData} contourData
138      */
139     setContourData: function (contourData) {
140         this.coutourData = contourData;
141     },
142 
143     /**
144      * colliderFilter getter
145      * @returns {ccs.ColliderFilter}
146      */
147     getColliderFilter: function () {
148         return this.colliderFilter;
149     }
150 });
151 
152 /**
153  * Base class for ccs.ColliderDetector
154  * @class
155  * @extends ccs.Class
156  *
157  * @param {ccs.Bone} [bone]
158  *
159  * @property {ccs.ColliderFilter}   colliderFilter  - The collider filter of the collider detector
160  * @property {Boolean}              active          - Indicate whether the collider detector is active
161  * @property {Object}               body            - The collider body
162  */
163 ccs.ColliderDetector = ccs.Class.extend(/** @lends ccs.ColliderDetector# */{
164     _colliderBodyList: null,
165     _bone: null,
166     _body: null,
167     _active: false,
168     _filter: null,
169     helpPoint: cc.p(0, 0),
170 
171     ctor: function (bone) {
172         this._colliderBodyList = [];
173         this._bone = null;
174         this._body = null;
175         this._active = false;
176         this._filter = null;
177 
178         ccs.ColliderDetector.prototype.init.call(this, bone);
179     },
180     init: function (bone) {
181         this._colliderBodyList.length = 0;
182         if (bone)
183             this._bone = bone;
184         this._filter = new ccs.ColliderFilter();
185         return true;
186     },
187 
188     /**
189      *  add contourData
190      * @param {ccs.ContourData} contourData
191      */
192     addContourData: function (contourData) {
193         var colliderBody = new ccs.ColliderBody(contourData);
194         this._colliderBodyList.push(colliderBody);
195 
196         if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) {
197             var calculatedVertexList = colliderBody.getCalculatedVertexList();
198             var vertexList = contourData.vertexList;
199             for (var i = 0; i < vertexList.length; i++) {
200                 var newVertex = new ccs.ContourVertex2(0, 0);
201                 calculatedVertexList.push(newVertex);
202             }
203         }
204     },
205 
206     /**
207      * add contourData
208      * @param {Array} contourDataList
209      */
210     addContourDataList: function (contourDataList) {
211         for (var i = 0; i < contourDataList.length; i++) {
212             this.addContourData(contourDataList[i]);
213         }
214     },
215 
216     /**
217      * remove contourData
218      * @param contourData
219      */
220     removeContourData: function (contourData) {
221         var eraseList = [], i, locBodyList = this._colliderBodyList;
222         for (i = 0; i < locBodyList.length; i++) {
223             var body = locBodyList[i];
224             if (body && body.getContourData() === contourData)
225                 eraseList.push(body);
226         }
227 
228         for (i=0; i<eraseList.length; i++)
229             cc.arrayRemoveObject(locBodyList, eraseList[i]);
230     },
231 
232     /**
233      * remove all body
234      */
235     removeAll: function () {
236         this._colliderBodyList.length = 0;
237     },
238 
239     setActive: function (active) {
240         if (this._active === active)
241             return;
242         this._active = active;
243 
244         var locBody = this._body;
245         var locShape;
246         if (locBody) {
247             var colliderBody = null;
248             if (this._active) {
249                 for (var i = 0; i < this._colliderBodyList.length; i++) {
250                     colliderBody = this._colliderBodyList[i];
251                     locShape = colliderBody.getShape();
252                     locBody.space.addShape(locShape);
253                 }
254             } else {
255                 for (var i = 0; i < this._colliderBodyList.length; i++) {
256                     colliderBody = this._colliderBodyList[i];
257                     locShape = colliderBody.getShape();
258                     locBody.space.removeShape(locShape);
259                 }
260             }
261         }
262     },
263 
264     getActive: function () {
265         return this._active;
266     },
267 
268     getColliderBodyList: function(){
269         return this._colliderBodyList;
270     },
271 
272     /**
273      * set colliderFilter
274      * @param {ccs.ColliderFilter} filter
275      */
276     setColliderFilter: function (filter) {
277         this._filter = filter;
278         var locBodyList = this._colliderBodyList;
279         for(var i=0; i< locBodyList.length; i++){
280             var colliderBody = locBodyList[i];
281             colliderBody.setColliderFilter(filter);
282             if (colliderBody.getShape())
283                 colliderBody.getColliderFilter().updateShape(colliderBody.getShape());
284         }
285     },
286 
287     /**
288      * get colliderFilter
289      * @returns {ccs.ColliderFilter}
290      */
291     getColliderFilter: function () {
292         return this._filter;
293     },
294 
295     updateTransform: function (t) {
296         if (!this._active)
297             return;
298 
299         var colliderBody = null;
300         var locBody = this._body;
301         var locHelpPoint = this.helpPoint;
302         for (var i = 0; i < this._colliderBodyList.length; i++) {
303 
304             colliderBody = this._colliderBodyList[i];
305             var contourData = colliderBody.getContourData();
306 
307             //default physics engine: Chipmunk
308             var shape = null;
309             if (locBody) {
310                 //Box2d shape = (b2PolygonShape *)colliderBody->getB2Fixture()->GetShape();
311                 shape = colliderBody.getShape();
312             }
313 
314             var vs = contourData.vertexList;
315             var cvs = colliderBody.getCalculatedVertexList();
316 
317             for (var j = 0; j < vs.length; j++) {
318                 locHelpPoint.x = vs[j].x;
319                 locHelpPoint.y = vs[j].y;
320                 locHelpPoint = cc.pointApplyAffineTransform(locHelpPoint, t);
321 
322                 if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) {
323                     var v = cc.p(0, 0);
324                     v.x = locHelpPoint.x;
325                     v.y = locHelpPoint.y;
326                     cvs[j] = v;
327                 }
328 
329                 if (shape) {
330                     shape.verts[j * 2] = locHelpPoint.x;
331                     shape.verts[j * 2 + 1] = locHelpPoint.y;
332                 }
333             }
334             if (shape) {
335                 for (var j = 0; j < vs.length; j++) {
336                     var b = shape.verts[(j + 1) % shape.verts.length];
337                     var n = cp.v.normalize(cp.v.perp(cp.v.sub(b, shape.verts[j])));
338 
339                     if(shape.planes){
340                         shape.planes[j].n = n;
341                         shape.planes[j].d = cp.v.dot(n, shape.verts[j]);
342                     }
343 //                    var b = shape.verts[(i + 1) % shape.numVerts];
344 //                    var n = cp.v.normalize(cp.v.perp(cp.v.sub(b, shape.verts[i])));
345 //
346 //                    shape.planes[i].n = n;
347 //                    shape.planes[i].d = cp.v.dot(n, shape.verts[i]);
348                 }
349             }
350         }
351     },
352 
353     setBody: function (body) {
354         this._body = body;
355         var colliderBody, locBodyList = this._colliderBodyList;
356         for (var i = 0; i < locBodyList.length; i++) {
357             colliderBody = locBodyList[i];
358             var contourData = colliderBody.getContourData(), verts = [];
359             var vs = contourData.vertexList;
360             for (var j = 0; j < vs.length; j++) {
361                 var v = vs[j];
362                 verts.push(v.x);
363                 verts.push(v.y);
364             }
365             var shape = new cp.PolyShape(this._body, verts, cp.vzero);
366             shape.sensor = true;
367             shape.data = this._bone;
368             if (this._active)
369                 this._body.space.addShape(shape);
370             colliderBody.setShape(shape);
371             colliderBody.getColliderFilter().updateShape(shape);
372         }
373     },
374 
375     getBody: function () {
376         return this._body;
377     }
378 });
379 
380 var _p = ccs.ColliderDetector.prototype;
381 
382 // Extended properties
383 /** @expose */
384 _p.colliderFilter;
385 cc.defineGetterSetter(_p, "colliderFilter", _p.getColliderFilter, _p.setColliderFilter);
386 /** @expose */
387 _p.active;
388 cc.defineGetterSetter(_p, "active", _p.getActive, _p.setActive);
389 /** @expose */
390 _p.body;
391 cc.defineGetterSetter(_p, "body", _p.getBody, _p.setBody);
392 
393 _p = null;
394 
395 ccs.ColliderDetector.create = function (bone) {
396     return new ccs.ColliderDetector(bone);
397 };