Advertisement
Guest User

Griffpatch Box2D EXT (PUBLIC)

a guest
Feb 10th, 2016
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JSON 15.44 KB | None | 0 0
  1. // GriffpatchBox2D.js
  2. // Griffpatch, January 2016
  3. // Extension to enable Box2D integration in Scratch
  4.  
  5. (function (ext) {
  6.  
  7.     var box2dscript = box2dscript;
  8.     if (!box2dscript) {
  9.         box2dscript = document.createElement("script");
  10.         box2dscript.type="text/javascript";
  11.         box2dscript.src="http://griffpatch.github.io/Box2D.js-Scratch2-Extension/Box2d.min.js";
  12.         //box2dscript.src=document.extURLs.box2d;
  13.         document.body.appendChild(box2dscript);
  14.     }
  15.  
  16.     ext.available = function() {
  17.         return !!Box2D;
  18.     }
  19.    
  20.     ext._stop = function () {};
  21.     ext._shutdown = function () {};
  22.  
  23.     var b2Vec2, b2AABB, b2BodyDef, b2Body, b2FixtureDef, b2Fixture, b2World, b2MassData, b2PolygonShape, b2CircleShape, b2DebugDraw, b2MouseJointDef;
  24.     var world, fixDef, zoom;
  25.  
  26.     var fixDef;
  27.     var bodyDef;
  28.  
  29.     var uid_seq = 0;
  30.     var ujid_seq = 0;
  31.  
  32.     var bodies = {};
  33.     var joints = {};
  34.  
  35.     var categorySeq = 1;
  36.     var categories = {'default':1}
  37.    
  38.     var bodyCategoryBits = 1;
  39.     var bodyMaskBits = 1;
  40.     var noCollideSeq = 0;
  41.    
  42.     const toRad = Math.PI / 180;
  43.  
  44.     ext._getStatus = function () {
  45.         if (Box2D) {
  46.             return {status: 2, msg: ' Ready'};
  47.         } else {
  48.             return {status: 1, msg: ' Failed to load Box2D'};
  49.         }
  50.     };
  51.    
  52.     ext.init = function( scale, gravity, scene ) {
  53.         b2Vec2 = Box2D.Common.Math.b2Vec2;
  54.         b2AABB = Box2D.Collision.b2AABB;
  55.         b2BodyDef = Box2D.Dynamics.b2BodyDef;
  56.         b2Body = Box2D.Dynamics.b2Body;
  57.         b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
  58.         b2Fixture = Box2D.Dynamics.b2Fixture;
  59.         b2World = Box2D.Dynamics.b2World;
  60.         b2MassData = Box2D.Collision.Shapes.b2MassData;
  61.         b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
  62.         b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
  63.         b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
  64.         b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef;
  65.  
  66.         world = new b2World(
  67.             new b2Vec2(0, gravity)    //gravity (10)
  68.          ,  true                 //allow sleep
  69.         );
  70.    
  71.         zoom = scale;
  72.    
  73.         fixDef = new b2FixtureDef;
  74.         fixDef.density = 1.0;       // 1.0
  75.         fixDef.friction = 0.5;      // 0.5
  76.         fixDef.restitution = 0.2;   // 0.2
  77.  
  78.         bodyDef = new b2BodyDef;
  79.  
  80.         if (scene=='stage') {
  81.  
  82.             //create ground
  83.             bodyDef.type = b2Body.b2_staticBody;
  84.             fixDef.shape = new b2PolygonShape;
  85.             fixDef.shape.SetAsBox(250/zoom, 10/zoom);
  86.             bodyDef.position.Set(0,-190/zoom);
  87.             world.CreateBody(bodyDef).CreateFixture(fixDef);
  88.             bodyDef.position.Set(0,1000/zoom);
  89.             world.CreateBody(bodyDef).CreateFixture(fixDef);
  90.             fixDef.shape.SetAsBox(10/zoom, 800/zoom);
  91.             bodyDef.position.Set(-250/zoom,540/zoom);
  92.             world.CreateBody(bodyDef).CreateFixture(fixDef);
  93.             bodyDef.position.Set(250/zoom,540/zoom);
  94.             world.CreateBody(bodyDef).CreateFixture(fixDef);
  95.         }
  96.        
  97.         bodies = {};
  98.         joints = {};
  99.         uid_seq = 0;
  100.         ujid_seq = 0;
  101.        
  102.         categorySeq = 1;
  103.         categories = {'default':1}
  104.         bodyCategoryBits = 1;
  105.         noCollideSeq = 0;
  106.  
  107.         bodyDef.type = b2Body.b2_dynamicBody;
  108.     };
  109.  
  110.     ext.setBodyAttrs = function(stat, dens, fric, rest) {
  111.         bodyDef.type = stat==='static' ? b2Body.b2_staticBody : b2Body.b2_dynamicBody;
  112.         fixDef.density = dens;      // 1.0
  113.         fixDef.friction = fric;     // 0.5
  114.         fixDef.restitution = rest;  // 0.2
  115.     };
  116.    
  117.     ext.setBodyAttr = function(attr, bodyIDs, val) {
  118.         var bds = bodyIDs.split(' ');
  119.         for (var i=0; i<bds.length; i++) {
  120.             var id = bds[i];
  121.             if (id.length>0) {
  122.                 var body = bodies[id];
  123.                 if (body) {
  124.                     switch (attr) {
  125.                         case 'damping': body.SetLinearDamping( val ); break;
  126.                         case 'rotational damping': body.GetAngularDamping( val ); break;
  127.                     }
  128.                 }
  129.             }
  130.         }
  131.     };
  132.    
  133.     ext.defineCategory = function(categoryIDs) {
  134.         var cids = categoryIDs.split(' ');
  135.         bodyCategoryBits = 0;
  136.         for (var i=0; i<cids.length; i++) {
  137.             var cid = cids[i];
  138.             if (cid.length>0) {
  139.                 var cat = categories[cid];
  140.                 if (!cat) {
  141.                     cat = categories[cid] = categorySeq = categorySeq*2;
  142.                 }
  143.                 bodyCategoryBits |= cat;
  144.             }
  145.         }
  146.     };
  147.    
  148.     ext.defineMask = function(categoryIDs) {
  149.         var cids = categoryIDs.split(' ');
  150.         bodyMaskBits = 0;
  151.         for (var i=0; i<cids.length; i++) {
  152.             var cid = cids[i];
  153.             if (cid.length>0) {
  154.                 var cat = categories[cid];
  155.                 if (!cat) {
  156.                     cat = categories[cid] = categorySeq = categorySeq*2;
  157.                 }
  158.                 bodyMaskBits |= cat;
  159.             }
  160.         }
  161.     };
  162.    
  163.     ext.definePoly = function(points) {
  164.         fixDef.shape = new b2PolygonShape;
  165.        
  166.         var pts = points.split(' ');
  167.         for (var i = 0; i < pts.length; i++) {
  168.             if (pts[i].length==0) {        
  169.                 pts.splice(i, 1);
  170.                 i--;
  171.             }
  172.         }
  173.  
  174.         // console.log(pts);
  175.        
  176.         var vertices = [];
  177.        
  178.         for (var i=pts.length; i>0;i-=2) {
  179.             vertices.push( new b2Vec2(parseFloat(pts[i-2])/zoom, parseFloat(pts[i-1])/zoom) );
  180.         }
  181.        
  182.         // console.log(vertices);
  183.        
  184.         fixDef.shape.SetAsArray( vertices );
  185.     };
  186.  
  187.     ext.defineRect = function(w,h) {
  188.         fixDef.shape = new b2PolygonShape;
  189.         fixDef.shape.SetAsBox( w/2/zoom, h/2/zoom );
  190.     };
  191.  
  192.     ext.defineCircle = function(d) {
  193.         fixDef.shape = new b2CircleShape;
  194.         fixDef.shape.SetRadius( d/2/zoom );
  195.     };
  196.  
  197.     ext.placeBody = function(id, x,y, dir) {
  198.         if (bodies[id]) {
  199.             world.DestroyBody( bodies[id] );
  200.         }
  201.        
  202.         fixDef.filter.categoryBits = bodyCategoryBits;
  203.         fixDef.filter.maskBits = bodyMaskBits;
  204.        
  205.         bodyDef.position.x = x/zoom;
  206.         bodyDef.position.y = y/zoom;
  207.         bodyDef.angle = (90-dir)*toRad;
  208.         var body = world.CreateBody(bodyDef);
  209.         body.uid = id;
  210.         body.CreateFixture(fixDef);
  211.         bodies[id] = body;
  212.     };
  213.    
  214.     ext.destroyBody = function(id) {
  215.         if (bodies[id]) {
  216.             world.DestroyBody( bodies[id] );
  217.             delete bodies[id];
  218.         }
  219.     };
  220.  
  221.     ext.getBodyAttr = function(attr, id) {
  222.         var body = bodies[id];
  223.         if (!body) return '';
  224.         switch (attr) {
  225.             case 'x': return body.GetPosition().x * zoom;
  226.             case 'y': return body.GetPosition().y * zoom;
  227.             case 'direction': return 90-(body.GetAngle()/toRad);
  228.             case 'awake': return body.IsAwake() ? 1 : 0;
  229.         }
  230.         return '';
  231.     };
  232.  
  233.     var mousePVec, selectedBody;
  234.    
  235.     function getBodyCB(fixture) {
  236.         if(fixture.GetBody().GetType() != b2Body.b2_staticBody) {
  237.             if(fixture.GetShape().TestPoint(fixture.GetBody().GetTransform(), mousePVec)) {
  238.                 selectedBody = fixture.GetBody();
  239.                 return false;
  240.             }
  241.         }
  242.         return true;
  243.     };
  244.  
  245.     ext.getBodyIDAt = function(x,y) {
  246.         mousePVec = new b2Vec2(x/zoom, y/zoom);
  247.         var aabb = new b2AABB();
  248.         aabb.lowerBound.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
  249.         aabb.upperBound.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
  250.  
  251.         // Query the world for overlapping shapes.
  252.         selectedBody = null;
  253.         world.QueryAABB(getBodyCB, aabb);
  254.        
  255.         return selectedBody ? selectedBody.uid : '';
  256.     };
  257.  
  258. /*  ext.createJointBetween = function(bodyID, x,y, bodyID2, x2,y2) {
  259.         if (bodyID=='') {
  260.             bodyID = null;
  261.         }
  262.         var body = bodyID ? bodies[bodyID] : world.GetGroundBody();
  263.         var body2 = bodies[bodyID2];
  264.        
  265.         if (body) {
  266.             var md = new Box2D.Dynamics.Joints.b2RevoluteJointDef();
  267.             md.bodyA = body;
  268.             md.bodyB = body2;
  269.             md.localAnchorA = {x:x/zoom, y:y/zoom};
  270.             md.localAnchorB = {x:x2/zoom, y:y2/zoom};
  271.             //md.collideConnected = true;
  272.             //md.maxForce = 300.0 * body.GetMass();
  273.             var joint = world.CreateJoint(md);
  274.             if (bodyID) {
  275.                 body.SetAwake(true);
  276.             }
  277.             body2.SetAwake(true);
  278.             joints[++ujid_seq] = joint;
  279.             return ''+ujid_seq;
  280.         }
  281.         return '';
  282.     };*/
  283.    
  284.     // ['',  'Define Spring Length: %n  Damping: %n  Freq: %n',             'defineSpring',     100, 0.5, 8],
  285.     var defSpring = {len:100, damp:0.7, freq: 5};
  286.     ext.defineSpring = function( len, damp, freq ) {
  287.         defSpring.len = len<0.1 ? 0.1 : len / zoom;
  288.         defSpring.damp = damp<0 ? 0.7 : damp;
  289.         defSpring.freq = freq>0 ? freq : 5;
  290.     }
  291.    
  292.     ext.createJointOfType = function(jName, typ, bodyID, x,y, bodyID2, x2,y2) {
  293.        
  294.         if (jName.length>0) ext.destroyJoint(jName);
  295.  
  296.         if (bodyID=='') bodyID = null;
  297.         if (bodyID2=='') bodyID2 = null;
  298.         if (!bodyID && !bodyID2) return '';
  299.            
  300.         var body = bodyID ? bodies[bodyID] : world.GetGroundBody();
  301.         var body2 = bodyID2 ? bodies[bodyID2] : world.GetGroundBody();
  302.        
  303.         if (!body || !body2) return '';
  304.        
  305.         var md;
  306.         switch (typ) {
  307.             case 'Spring':
  308.                 md = new Box2D.Dynamics.Joints.b2DistanceJointDef();
  309.                 md.length = defSpring.len;
  310.                 md.dampingRatio = defSpring.damp;
  311.                 md.frequencyHz = defSpring.freq;
  312.                 md.bodyA = body;
  313.                 md.bodyB = body2;
  314.                 md.localAnchorA = {x:x/zoom, y:y/zoom};
  315.                 md.localAnchorB = {x:x2/zoom, y:y2/zoom};
  316.                 break;
  317.                
  318.             case 'Rotating':
  319.                 md = new Box2D.Dynamics.Joints.b2RevoluteJointDef();
  320.                 md.bodyA = body;
  321.                 md.bodyB = body2;
  322.                 md.localAnchorA = {x:x/zoom, y:y/zoom};
  323.                 md.localAnchorB = {x:x2/zoom, y:y2/zoom};
  324.                 break;
  325.                
  326.             case 'Mouse':
  327.                 var md = new b2MouseJointDef();
  328.                 if (bodyID=='') {
  329.                     md.bodyB = body2;
  330.                     md.target.Set(x2/zoom, y2/zoom);
  331.                 } else {
  332.                     md.bodyB = body;
  333.                     md.target.Set(x/zoom, y/zoom);
  334.                 }
  335.                 md.bodyA = world.GetGroundBody();
  336.                 md.collideConnected = true;
  337.                 md.maxForce = 300.0 * body.GetMass();
  338.                 break;
  339.         }
  340.        
  341.         //md.collideConnected = true;
  342.         //md.maxForce = 300.0 * body.GetMass();
  343.         var joint = world.CreateJoint(md);
  344.         if (bodyID.length>0) {
  345.             body.SetAwake(true);
  346.         }
  347.         if (bodyID2.length>0) {
  348.             body2.SetAwake(true);
  349.         }
  350.        
  351.         if (jName.length==0) jName = '_'+(++ujid_seq);
  352.         joints[jName] = joint;
  353.     };
  354.    
  355.     /*ext.createJointAt = function(bodyID, x, y) {
  356.         var body = bodies[bodyID];
  357.         if (body) {
  358.             var md = new b2MouseJointDef();
  359.             md.bodyA = world.GetGroundBody();
  360.             md.bodyB = body;
  361.             md.target.Set(x/zoom, y/zoom);
  362.             md.collideConnected = true;
  363.             md.maxForce = 300.0 * body.GetMass();
  364.             var joint = world.CreateJoint(md);
  365.             body.SetAwake(true);
  366.             joints[++ujid_seq] = joint;
  367.             return ''+ujid_seq;
  368.         }
  369.         return '';
  370.     };*/
  371.    
  372.     ext.setJointTarget = function(jointID, x, y) {
  373.         var joint = joints[jointID];
  374.         if (joint) {
  375.             joint.SetTarget(new b2Vec2(x/zoom, y/zoom));
  376.         }
  377.     };
  378.        
  379.     ext.setJointAttr = function(attr, jointID, val) {
  380.         // JointAttr: ['Motor On','Motor Speed','Max Torque', 'Limits On','Lower Limit','Upper Limit'],
  381.  
  382.         var jointids = jointID.split(' ');
  383.         for (var i=0;i<jointids.length;i++) {
  384.             var joint = joints[jointids[i]];
  385.             if (joint) {
  386.                 switch(attr) {
  387.                 case 'Motor On': joint.EnableMotor(val>0); break;
  388.                 case 'Motor Speed': joint.SetMotorSpeed(val); break;
  389.                 case 'Max Torque': joint.SetMaxMotorTorque(val); break;
  390.                
  391.                 case 'Limits On': joint.EnableLimit(val>0); break;
  392.                 case 'Lower Limit': joint.SetLimits(joint.GetJointAngle()+val*toRad, joint.GetUpperLimit()); break;
  393.                 case 'Upper Limit': joint.SetLimits(joint.GetLowerLimit(),joint.GetJointAngle()+val*toRad); break;
  394.             }
  395.             }
  396.         }
  397.     };
  398.        
  399.     ext.getJointAttr = function(attr, jointID) {
  400.         // JointAttrRead: ['Angle','Speed','Motor Torque', 'Reaction Torque'],
  401.  
  402.         var joint = joints[jointID];
  403.         if (joint) {
  404.             switch(attr) {
  405.                 case 'Angle': return joint.GetJointAngle()/toRad;
  406.                 case 'Speed': return joint.GetJointSpeed();
  407.                 case 'Motor Torque': return joint.GetMotorTorque();
  408.                 case 'Reaction Torque': return joint.GetReactionTorque();
  409.                
  410. //              case 'Lower Limit': return joint.GetLowerLimit()/toRad;
  411. //              case 'Upper Limit': return joint.GetUpperLimit()/toRad;
  412.             }
  413.         }
  414.     };
  415.        
  416.     ext.destroyJoint = function(jointID) {
  417.         var joint = joints[jointID];
  418.         if (joint) {
  419.             world.DestroyJoint(joint);
  420.             delete joints[jointID];
  421.         }
  422.     };
  423.  
  424.     ext.applyForceToBody = function(ftype, bodyID, x, y, pow, dir) {
  425.         var body = bodies[bodyID];
  426.         if (!body)
  427.             return;
  428.  
  429.         dir = (90-dir)*toRad;
  430.            
  431.         if (ftype==='Impulse') {
  432.             body.ApplyImpulse( {x:pow*Math.cos(dir),y:pow*Math.sin(dir)}, body.GetWorldPoint({x:x/zoom,y:y/zoom}) );           
  433.         } else if (ftype==='World Impulse') {
  434.             body.ApplyForce( {x:pow*Math.cos(dir),y:pow*Math.sin(dir)}, {x:x/zoom,y:y/zoom} );         
  435.         }
  436.     };
  437.    
  438.     ext.applyAngForceToBody = function(ftype, bodyID, pow) {
  439.         var body = bodies[bodyID];
  440.         if (!body)
  441.             return;
  442.  
  443.         if (ftype==='Impulse') {
  444.             //console.log(body);
  445.             body.ApplyTorque( -pow );          
  446.         }
  447.     };
  448.    
  449.     ext.createNoCollideSet = function(set) {
  450.         noCollideSeq--;
  451.         var bids = set.split(' ');
  452.         for (var i=0; i<bids.length; i++) {
  453.             var bid = bids[i];
  454.             if (bid.length>0) {
  455.                 var body = bodies[bid];
  456.                 if (body) {
  457.                     var fix = body.GetFixtureList();
  458.                     while (fix) {
  459.                         var fdata = fix.GetFilterData();
  460.                         fdata.groupIndex = noCollideSeq;
  461.                         fix.SetFilterData(fdata);
  462.                         fix = fix.GetNext();
  463.                     }
  464.                 }
  465.             }
  466.         }
  467.     };
  468.    
  469.     ext.stepSimulation = function() {
  470.         world.Step(1/30, 10, 10);
  471.         world.ClearForces();
  472.     };
  473.    
  474.     var descriptor = {
  475.         blocks: [
  476.             ['b', 'Box2D Available',                                                'available'],
  477.             ['',  'Init World; scale 1m= %n  gravity= %n  scene= %m.sceneType',     'init',         50, -10, 'stage'],
  478.             ["-"],
  479.             ["-"],
  480.             ['',  'Define Type %m.BodyTypePK  Density %n  Friction %n  Bounce %n',  'setBodyAttrs', 'dynamic', 1.0, 0.5, 0.2],
  481.             ["-"],
  482. /*          ['',  'Define Category %s',                                             'defineCategory',   'default'],
  483.             ['',  'Define Collide Category %s',                                     'defineCollideCategory',    'default'],
  484.             ["-"],*/
  485.             ['',  'Define Circle, size: %n',                                        'defineCircle', 100],
  486.             ['',  'Define Box, width: %n height: %n',                               'defineRect',   100, 100],
  487.             ['',  'Define Polygon, points: %s',                                     'definePoly',   '0 50   40 -50   -40 -50'],
  488.             ["-"],
  489.             ['',  'Create Body %s  at x: %n  y: %n  dir: %n',                       'placeBody',    'name', 0,0, 90],
  490.             ['',  'Create no collide set %s',                                       'createNoCollideSet',   'name1 name2 name3'],
  491.             ['',  'Destroy Body %s',                                                'destroyBody',  'name'],
  492.             ["-"],
  493.             ["-"],
  494.             ["-"],
  495.             ['',  'Set %m.bodyAttr  of body %s to %n',                              'setBodyAttr',  'damping', 'name', '0.1'],
  496.             ['r', 'Get %m.bodyAttrRead  from body %s',                              'getBodyAttr',  'x',    'name'],
  497.             ["-"],
  498.             ['r', 'Get id of body at x: %n  y: %n',                                 'getBodyIDAt',  0, 0],
  499.             ["-"],
  500.             ["-"],
  501.             ["-"],
  502.             ['',  'Apply %m.ForceType to Body %s  at x: %n  y: %n  power: %n  dir: %n', 'applyForceToBody', 'Impulse', 'name', 0, 0, 50, 90],
  503.             ['',  'Apply Angular %m.ForceType to Body %s  power: %n',               'applyAngForceToBody',  'Impulse', 'name', 0],
  504.             ["-"],
  505.             ["-"],
  506.             ["-"],
  507.             ['',  'Define Spring Length: %n  Damping: %n  Freq: %n',                'defineSpring',     100, 0.7, 5],
  508.             ['',  'Create Joint %s of type %m.JointType between %s at %n %n and %s at %n %n',   'createJointOfType', 'JointID', 'Rotating', 'BodyID', 0,0, 'BodyID', 0,0],
  509.             ['',  'Destroy Joint ID %s',                                            'destroyJoint',     'Joint ID'],
  510.             ["-"],
  511.             ['',  'Set Joint %m.JointAttr of joint %s to %n',                       'setJointAttr',     'Motor On', 'Joint ID', 0, 0],
  512.             ['r', 'Get Joint %m.JointAttrRead of joint %s',                         'getJointAttr',     'Angle', 'Joint ID'],
  513.             ['',  'Set Mouse Joint Target %s to x: %n  y: %n',                      'setJointTarget',   'Joint ID', 0, 0],
  514.             ["-"],
  515.             ["-"],
  516.             ["-"],
  517.             ['',  'Step Simulation',                                                'stepSimulation'],
  518.         ],
  519.         menus: {
  520.             sceneType: ['stage', 'nothing'],
  521.             BodyTypePK: ['dynamic', 'static'],
  522.             bodyAttr: ['damping', 'rotational damping'],
  523.             bodyAttrRead: ['x', 'y', 'direction', 'awake'],
  524.             ForceType: ['Impulse', 'World Impulse'],
  525.             AngForceType: ['Impulse'],
  526.             JointType: ['Rotating','Spring','Mouse'],
  527.             JointAttr: ['Motor On','Motor Speed','Max Torque', 'Limits On','Lower Limit','Upper Limit'],
  528.             JointAttrRead: ['Angle','Speed','Motor Torque', 'Reaction Torque'],
  529.         },
  530. /*        url: 'www.griffpatch.co.uk'*/
  531.     };
  532.    
  533.     if (ScratchExtensions) {
  534. //        ScratchExtensions.unregister('Griffpatch Box2D');
  535.         ScratchExtensions.register('Griffpatch Box2D', descriptor, ext);
  536.     }
  537. })({});
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement