1 /**
  2  * The Render Engine
  3  * BaseBodyComponent
  4  *
  5  * @fileoverview The base component type for all physical bodies which can be used
  6  *               in a {@link Simulation}.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  *
 10  * @author: $Author: bfattori $
 11  * @version: $Revision: 1555 $
 12  *
 13  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 14  *
 15  * Permission is hereby granted, free of charge, to any person obtaining a copy
 16  * of this software and associated documentation files (the "Software"), to deal
 17  * in the Software without restriction, including without limitation the rights
 18  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 19  * copies of the Software, and to permit persons to whom the Software is
 20  * furnished to do so, subject to the following conditions:
 21  *
 22  * The above copyright notice and this permission notice shall be included in
 23  * all copies or substantial portions of the Software.
 24  *
 25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 31  * THE SOFTWARE.
 32  *
 33  */
 34 
 35 // The class this file defines and its required classes
 36 R.Engine.define({
 37     "class":"R.components.physics.BaseBody",
 38     "requires":[
 39         "R.components.Transform2D",
 40         "R.math.Point2D",
 41         "R.math.Vector2D",
 42         "R.math.Rectangle2D",
 43         "R.physics.Simulation"
 44     ]
 45 });
 46 
 47 /**
 48  * @class The base component which initializes rigid bodies
 49  *        for use in a {@link R.physics.Simulation}.
 50  *
 51  * @param name {String} Name of the component
 52  * @param fixtureDef {Box2D.Dynamics.b2FixtureDef} The fixture definition.
 53  *
 54  * @extends R.components.Transform2D
 55  * @constructor
 56  * @description All physical body components should extend from this component type
 57  *              to inherit such values as density and friction, and gain access to position and rotation.
 58  */
 59 R.components.physics.BaseBody = function () {
 60     return R.components.Transform2D.extend(/** @scope R.components.physics.BaseBody.prototype */{
 61 
 62         bodyDef:null,
 63         fixtureDef:null,
 64         simulation:null,
 65         body:null,
 66         rotVec:null,
 67         bodyPos:null,
 68         renderComponent:null,
 69         origin:null,
 70 
 71         scaledPoint:null,
 72         _states:null,
 73 
 74         /**
 75          * @private
 76          */
 77         constructor:function (name, fixtureDef) {
 78             this.base(name || "BaseBody");
 79 
 80             this.bodyDef = new Box2D.Dynamics.b2BodyDef();
 81             this.bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;
 82 
 83             this.fixtureDef = fixtureDef;
 84             this.fixtureDef.restitution = R.components.physics.BaseBody.DEFAULT_RESTITUTION;
 85             this.fixtureDef.density = R.components.physics.BaseBody.DEFAULT_DENSITY;
 86             this.fixtureDef.friction = R.components.physics.BaseBody.DEFAULT_FRICTION;
 87             this.simulation = null;
 88             this.rotVec = R.math.Vector2D.create(0, 0);
 89             this.bodyPos = R.math.Point2D.create(0, 0);
 90             this.origin = R.math.Point2D.create(0, 0);
 91             this.scaledPoint = R.math.Point2D.create(0, 0);
 92 
 93             // 0: Active, 1: Sleeping
 94             this._states = [false, false];
 95         },
 96 
 97         /**
 98          * Destroy the object
 99          */
100         destroy:function () {
101             if (this.simulation) {
102                 this.stopSimulation();
103             }
104 
105             if (this.renderComponent != null) {
106                 this.renderComponent.destroy();
107             }
108 
109             this.rotVec.destroy();
110             this.bodyPos.destroy();
111             this.origin.destroy();
112             this.scaledPoint.destroy();
113 
114             this.base();
115         },
116 
117         /**
118          * Releases the object back into the pool
119          */
120         release:function () {
121             this.base();
122 
123             this.rotVec = null;
124             this.bodyPos = null;
125             this.origin = null;
126             this._states = null;
127         },
128 
129         /**
130          * Start simulating the body.  If the body isn't a part of the simulation,
131          * it is added and simulation occurs.  Position and rotation will be updated.
132          */
133         startSimulation:function () {
134             if (!this.simulation) {
135                 this.simulation = this.getGameObject().getSimulation();
136                 this.body = this.simulation.addBody(this.getBodyDef(), this.getFixtureDef());
137 
138                 // Add something to the body so we can get back to this object
139                 this.body.__$backRef$__ = this;
140             }
141         },
142 
143         /**
144          * Stop simulating the body.  If the body is a part of a simulation,
145          * it is removed and simulation stops.  The position and rotation of
146          * the body will not be updated.
147          */
148         stopSimulation:function () {
149             if (this.simulation) {
150                 this.simulation.removeBody(this.getBody());
151                 this.simulation = null;
152             }
153         },
154 
155         /**
156          * Set the associated render component for this body.  This is typically used by the
157          * {@link PhysicsActor} to link a body to a renderer so that each body can have an
158          * associated renderer applied.
159          *
160          * @param renderComponent {R.components.Render} The render component to associate with this body
161          */
162         setRenderComponent:function (renderComponent) {
163             this.renderComponent = renderComponent;
164             if (renderComponent != null) {
165                 this.renderComponent.setGameObject(this.getGameObject());
166             }
167         },
168 
169         /**
170          * Get the associated render component for this body.
171          * @return {R.components.Render} or <code>null</code>
172          */
173         getRenderComponent:function () {
174             return this.renderComponent;
175         },
176 
177         /**
178          * Set the origin of the rigid body.  By default, the origin is the top left corner of
179          * the bounding box for the body.  Most times the origin should be set to the center
180          * of the body.
181          *
182          * @param x {Number|R.math.Point2D} The X coordinate or a <tt>Point2D</tt>
183          * @param y {Number} The Y coordinate or <tt>null</tt> if X is a <tt>Point2D</tt>
184          */
185         setLocalOrigin:function (x, y) {
186             this.origin.set(x, y);
187         },
188 
189         /**
190          * Get the local origin of the body.
191          * @return {R.math.Point2D}
192          */
193         getLocalOrigin:function () {
194             return this.origin;
195         },
196 
197         /**
198          * Get the center of the body.
199          * @return {R.math.Point2D}
200          */
201         getCenter:function () {
202             return R.clone(this.getPosition());
203         },
204 
205         /**
206          * [ABSTRACT] Get a box which bounds the body.
207          * @return {R.math.Rectangle2D}
208          */
209         getBoundingBox:function () {
210             return R.math.Rectangle2D.create(0, 0, 1, 1);
211         },
212 
213         /**
214          * Get the Box2d fixture definition object.
215          * @return {Box2D.Dynamics.b2FixtureDef}
216          */
217         getFixtureDef:function () {
218             return this.fixtureDef;
219         },
220 
221         /**
222          * Get the Box2d body definition object.
223          * @return {b2BodyDef}
224          */
225         getBodyDef:function () {
226             return this.bodyDef;
227         },
228 
229         /**
230          * Get the Box2d body object which was added to the simulation.
231          * @return {b2Body}
232          */
233         getBody:function () {
234             return this.body;
235         },
236 
237         /**
238          * Update the fixture on a simulated body. Doing so may cause a hiccup in simulation
239          * @protected
240          */
241         updateFixture:function () {
242             if (this.simulation) {
243                 // Destroy the current fixture, then recreate it ()
244                 this.getBody().DestroyFixture(this.fixtureDef);
245                 this.getBody().CreateFixture(this.fixtureDef);
246             }
247         },
248 
249         /**
250          * Set the resitution (bounciness) of the body.  The value should be between
251          * zero and one.  Values higher than one are accepted, but produce objects which
252          * are unrealistically bouncy.
253          *
254          * @param restitution {Number} A value between 0.0 and 1.0
255          */
256         setRestitution:function (restitution) {
257             this.fixtureDef.restitution = restitution;
258             this.updateFixture();
259         },
260 
261         /**
262          * Get the resitution (bounciness) value for the body.
263          * @return {Number}
264          */
265         getRestitution:function () {
266             return this.fixtureDef.restitution;
267         },
268 
269         /**
270          * Set the density of the body.
271          *
272          * @param density {Number} The density of the body
273          */
274         setDensity:function (density) {
275             this.fixtureDef.density = density;
276             this.updateFixture();
277         },
278 
279         /**
280          * Get the density of the body.
281          * @return {Number}
282          */
283         getDensity:function () {
284             return this.fixtureDef.density;
285         },
286 
287         /**
288          * Set the friction of the body.  Lower values slide easily across other bodies.
289          * Higher values will cause a body to stop moving as it slides across other bodies.
290          * However, even a body which has high friction will keep sliding across a body
291          * with no friction.
292          *
293          * @param friction {Number} The friction of the body
294          */
295         setFriction:function (friction) {
296             this.fixtureDef.friction = friction;
297             this.updateFixture();
298         },
299 
300         /**
301          * Get the friction of the body.
302          * @return {Number}
303          */
304         getFriction:function () {
305             return this.fixtureDef.friction;
306         },
307 
308         /**
309          * Set the initial position of the body.  Once a body is in motion, updating
310          * its position should be avoided since it doesn't fit with physical simulation.
311          * To change an object's position, try applying forces or impulses to the body.
312          *
313          * @param point {R.math.Point2D} The initial position of the body
314          */
315         setPosition:function (point) {
316             if (!this.simulation) {
317                 this.scaledPoint.set(point).div(R.physics.Simulation.WORLD_SIZE);
318                 this.getBodyDef().position.Set(this.scaledPoint.x, this.scaledPoint.y);
319             } else {
320                 this.scaledPoint.set(point).div(R.physics.Simulation.WORLD_SIZE);
321                 var bv = new Box2D.Common.Math.b2Vec2(this.scaledPoint.x, this.scaledPoint.y);
322                 this.getBody().SetPosition(bv);
323             }
324         },
325 
326         /**
327          * Get the position of the body during simulation.  This value is updated
328          * as the simulation is stepped.
329          * @return {R.math.Point2D}
330          */
331         getPosition:function () {
332             if (this.simulation) {
333                 var bp = this.getBody().GetPosition();
334                 this.scaledPoint.set(bp.x, bp.y).mul(R.physics.Simulation.WORLD_SIZE);
335                 this.bodyPos.set(this.scaledPoint);
336             } else {
337                 this.scaledPoint.set(this.getBodyDef().position.x, this.getBodyDef().position.y).mul(R.physics.Simulation.WORLD_SIZE);
338                 this.bodyPos.set(this.scaledPoint);
339             }
340             return this.bodyPos;
341         },
342 
343         /**
344          * Get the rotation of the body.  This value is updated as the simulation is stepped.
345          * @return {Number}
346          */
347         getRotation:function () {
348             if (this.simulation) {
349                 return R.math.Math2D.radToDeg(this.getBody().GetAngle());
350             } else {
351                 return this.getBodyDef().angle;
352             }
353         },
354 
355         /**
356          * Set the angle of rotation for the body, in degrees.
357          * @param angle {Number} The rotation angle in degrees
358          */
359         setRotation:function (angle) {
360             if (this.simulation) {
361                 this.getBody().SetAngle(R.math.Math2D.degToRad(angle));
362             } else {
363                 this.getBodyDef().angle = R.math.Math2D.degToRad(angle);
364             }
365         },
366 
367         /**
368          * Apply a force at a world point. If the force is not applied at the center of mass,
369          * it will generate a torque and affect the angular velocity. This wakes up the body.
370          * Forces are comprised of a force vector and
371          * a position.  The force vector is the direction in which the force is
372          * moving, while the position is where on the body the force is acting.
373          * Forces act upon a body from world coordinates.
374          *
375          * @param forceVector {R.math.Vector2D} The force vector
376          * @param position {R.math.Point2D} The position where the force is acting upon the body
377          */
378         applyForce:function (forceVector, position) {
379             var fv = new Box2D.Common.Math.b2Vec2(forceVector.x, forceVector.y);
380             var dv = new Box2D.Common.Math.b2Vec2(position.x, position.y);
381             this.getBody().ApplyForce(fv, dv);
382         },
383 
384         /**
385          * Apply an impulse at a point. This immediately modifies the velocity. It also modifies
386          * the angular velocity if the point of application is not at the center of mass. This wakes
387          * up the body.  Impulses are comprised of an impulse vector and
388          * a position.  The impulse vector is the direction of the impulse, while the position
389          * is where on the body the impulse will be applied.
390          * Impulses act upon a body locally, adjusting its velocity.
391          *
392          * @param impulseVector {R.math.Vector2D} The impulse vectory
393          * @param position {R.math.Point2D} the position where the impulse is originating from in the body
394          */
395         applyImpulse:function (impulseVector, position) {
396             var iv = new Box2D.Common.Math.b2Vec2(impulseVector.x, impulseVector.y);
397             var dv = new Box2D.Common.Math.b2Vec2(position.x, position.y);
398             this.getBody().ApplyImpulse(iv, dv);
399         },
400 
401         /**
402          * Apply torque to the body. This affects the angular velocity without affecting the
403          * linear velocity of the center of mass.
404          *
405          * @param torque {Number} The amount of torque to apply to the body
406          */
407         applyTorque:function (torque) {
408             this.getBody().ApplyTorque(torque);
409         },
410 
411         /**
412          * Get the total mass of the body.  If the body is not simulating, this
413          * returns <code>Infinity</code>.
414          *
415          * @return {Number} The mass of the body, or <code>Infinity</code>
416          */
417         getMass:function () {
418             if (this.simulation) {
419                 return this.getBody().GetMass();
420             } else {
421                 return this.getBodyDef().massData.mass;
422             }
423         },
424 
425         /**
426          * Set the total mass of the body in kilograms.
427          * @param mass {Number} The mass of the body in kg
428          */
429         setMass:function (mass) {
430             if (this.simulation) {
431                 var mData = new Box2D.Dynamics.b2MassData();
432                 this.getBody().GetMassData(mData);
433                 mData.mass = mass;
434                 this.getBody().SetMassData(mData);
435                 mData = null;
436             } else {
437                 this.getBodyDef().massData.mass = mass;
438             }
439         },
440 
441         /**
442          * Get the linear damping of the body.
443          * @return {Number}
444          */
445         getLinearDamping:function () {
446             if (this.simulation) {
447                 return this.getBody().GetLinearDamping();
448             } else {
449                 return this.getBodyDef().linearDamping;
450             }
451         },
452 
453         /**
454          * Get the angular damping of the body.
455          * @return {Number}
456          */
457         getAngularDamping:function () {
458             if (this.simulation) {
459                 return this.getBody().GetAngularDamping();
460             } else {
461                 return this.getBodyDef().angularDamping;
462             }
463         },
464 
465         /**
466          * Sets the linear and angular damping of a body. Damping is used to reduce the
467          * world velocity of bodies.  Damping differs from friction in that friction
468          * only occurs when two surfaces are in contact.  Damping is not a replacement
469          * for friction.  A value between 0 and <code>Infinity</code> can be used, but
470          * normally the value is between 0 and 1.0.
471          *
472          * @param linear {Number} The amount of linear damping
473          * @param angular {Number} The amount of angular damping
474          */
475         setDamping:function (linear, angular) {
476             if (this.simulation) {
477                 this.getBody().SetLinearDamping(linear);
478                 this.getBody().SetAngularDamping(linear);
479             } else {
480                 this.getBodyDef().linearDamping = linear;
481                 this.getBodyDef().angularDamping = angular;
482             }
483         },
484 
485         /**
486          * Set a body to be static or dynamic.  A static body will not move around.
487          * @param state {Boolean} <code>true</code> to set the body as static, <code>false</code> for
488          *        dynamic.
489          */
490         setStatic:function (state) {
491             this.getBodyDef().type = state ? Box2D.Dynamics.b2Body.b2_staticBody : Box2D.Dynamics.b2Body.b2_dynamicBody;
492         },
493 
494         /**
495          * Returns <code>true</code> if the body is static.  A body is static if it
496          * isn't updated part of the simulation during contacts.
497          * @return {Boolean}
498          */
499         isStatic:function () {
500             if (this.simulation) {
501                 return this.getBody().GetType() == Box2D.Dynamics.b2Body.b2_staticBody;
502             } else {
503                 return this.getBodyDef().type == Box2D.Dynamics.b2Body.b2_staticBody;
504             }
505         },
506 
507         /**
508          * Returns <code>true</code> if the body is sleeping.  A body is sleeping if it
509          * has settled to the point where no movement is being calculated.  If you want
510          * to perform an action upon a body, other than applying force, torque, or impulses,
511          * you must call {@link #wakeUp}.
512          * @return {Boolean}
513          */
514         isSleeping:function () {
515             if (this.simulation) {
516                 return !this.getBody().IsAwake();
517             } else {
518                 return !this.getBodyDef().awake;
519             }
520         },
521 
522         /**
523          * Returns <code>true</code> if the body is active.  An active body is updated during
524          * the simulation and can be collided with.
525          * @return {Boolean}
526          */
527         isActive:function () {
528             if (this.simulation) {
529                 return this.getBody().IsActive();
530             } else {
531                 return this.getBodyDef().active;
532             }
533         },
534 
535         /**
536          * Wake up a body, adding it back into the collection of bodies being simulated.
537          * If the body is not being simulated, this does nothing.
538          */
539         wakeUp:function () {
540             if (this.simulation) {
541                 this.getBody().SetAwake(true);
542             }
543         },
544 
545         /**
546          * Sets the active state of a body.  Setting the active flag to <tt>false</tt> will
547          * remove the object from simulation.  Setting it to true will add it back into the
548          * simulation.
549          * @param active {Boolean} The activity flag
550          */
551         setActive:function (active) {
552             if (this.simulation) {
553                 this.getBody().SetActive(active);
554             } else {
555                 this.getBodyDef().active = active;
556             }
557         },
558 
559         /**
560          * Checks a couple of flags on the body and triggers events when they change.  Fires
561          * the <code>active</code> event on the game object when this body changes its "active" state.
562          * Fires the <code>sleeping</code> event on the game object when this body changes its
563          * "sleeping" state.  Both events are passed the body which changed state, and a flag
564          * indicating the current state.
565          *
566          * @param renderContext {R.rendercontexts.AbstractRenderContext} The rendering context
567          * @param time {Number} The engine time in milliseconds
568          * @param dt {Number} The delta between the world time and the last time the world was updated
569          *          in milliseconds.
570          */
571         execute:function (renderContext, time, dt) {
572             this.base(renderContext, time, dt);
573 
574             // Check the sleeping and active states so we can trigger events
575             var activeChange = false, sleepChange = false;
576             if (!this._states[0] && this.isActive()) {
577                 this._states[0] = true;
578                 activeChange = true;
579             } else if (this._states[0] && !this.isActive()) {
580                 this._states[0] = false;
581                 activeChange = true;
582             }
583 
584             if (!this._states[1] && this.isSleeping()) {
585                 this._states[1] = true;
586                 sleepChange = true;
587             } else if (this._states[1] && !this.isSleeping()) {
588                 this._states[1] = false;
589                 sleepChange = true;
590             }
591 
592             if (activeChange)
593                 this.getGameObject().triggerEvent("active", [this, this._states[0]]);
594 
595             if (sleepChange)
596                 this.getGameObject().triggerEvent("sleeping", [this, this._states[1]]);
597         }
598 
599     }, { /** @scope R.components.physics.BaseBody.prototype */
600 
601         /**
602          * Get the class name of this object
603          *
604          * @return {String} "R.components.physics.BaseBody"
605          */
606         getClassName:function () {
607             return "R.components.physics.BaseBody";
608         },
609 
610         /**
611          * The default restitution (bounciness) of a body
612          * @type {Number}
613          */
614         DEFAULT_RESTITUTION:0.48,
615 
616         /**
617          * The default density of a body
618          */
619         DEFAULT_DENSITY:1.0,
620 
621         /**
622          * The default friction of a body
623          * @type {Number}
624          */
625         DEFAULT_FRICTION:0.5
626 
627     });
628 };