Advertisement
liuwong

portal: PortalManager

May 4th, 2013
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package example.portal
  2. {
  3.     import flash.events.WeakFunctionClosure;
  4.     import flash.printing.PrintJob;
  5.     import nape.callbacks.CbEvent;
  6.     import nape.callbacks.CbType;
  7.     import nape.callbacks.InteractionCallback;
  8.     import nape.callbacks.InteractionListener;
  9.     import nape.callbacks.InteractionType;
  10.     import nape.callbacks.PreCallback;
  11.     import nape.callbacks.PreFlag;
  12.     import nape.callbacks.PreListener;
  13.     import nape.constraint.Constraint;
  14.     import nape.constraint.ConstraintIterator;
  15.     import nape.dynamics.Arbiter;
  16.     import nape.dynamics.CollisionArbiter;
  17.     import nape.dynamics.Contact;
  18.     import nape.dynamics.ContactList;
  19.     import nape.geom.Vec2;
  20.     import nape.phys.Body;
  21.     import nape.shape.Shape;
  22.     import nape.shape.ShapeIterator;
  23.     import nape.space.Space;
  24.    
  25.     /**
  26.      * ...
  27.      * @author liu wong
  28.      *
  29.      */
  30.    
  31.     public class PortalManager
  32.     {
  33.         public var PORTAL:CbType;
  34.         // CbType for a Shape that is permitted to pass through portals.
  35.         public var PORTABLE:CbType;
  36.  
  37.         // CbType for a Shape which is intersecting a portal sensor.
  38.         public var PARTIAL:CbType;
  39.  
  40.         private var portalID:int = 0;
  41.        
  42.         public function PortalManager(space:Space):void
  43.         {
  44.             PORTAL = new CbType();
  45.             PORTABLE = new CbType();
  46.             PARTIAL = new CbType();
  47.            
  48.             space.listeners.add(new InteractionListener(
  49.                 CbEvent.BEGIN,
  50.                 InteractionType.SENSOR,
  51.                 PORTAL,
  52.                 PORTABLE,
  53.                 portalBegin
  54.                 ));
  55.  
  56.             space.listeners.add(new InteractionListener(
  57.                 CbEvent.END,
  58.                 InteractionType.SENSOR,
  59.                 PORTAL,
  60.                 PORTABLE,
  61.                 portalEnd
  62.                 ));
  63.  
  64.             space.listeners.add(new PreListener(
  65.                 InteractionType.COLLISION,
  66.                 PARTIAL,
  67.                 CbType.ANY_SHAPE,
  68.                 backCollision
  69.                 ));
  70.         }
  71.        
  72.         private function portalBegin(cb:InteractionCallback):void
  73.         {
  74.             //---
  75.             var portalSensor:Shape = cb.int1.castShape;
  76.             var shape:Shape = cb.int2.castShape;
  77.             var body:Body = shape.body;
  78.  
  79.             // Can happen in rare circumstances where an END is processed before the BEGIN occurs with CCD
  80.             if (body == null) {
  81.                 return;
  82.             }
  83.  
  84.             //trace(portalSensor.userData, shape.userData, body, body.userData);
  85.            
  86.             var portalData:* = portalSensor.userData;
  87.             var shapeData:* = shape.userData;
  88.             var bodyData:* = body.userData;
  89.             var portal:Portal = portalData.portal;
  90.  
  91.             // Cases to consider:
  92.             //
  93.             // Either this shape is already interacting via this portal
  94.             // (Still has a cloned shape on otherside) and has started to
  95.             // overlap again on other side.
  96.             //
  97.             // This shape has just started interacting via the portal and:
  98.             //    This is the first shape of the body to interact
  99.             //    The body is already interacting via this portal.
  100.             //
  101.             // Also possible for shape which has passed through back of portal
  102.             // sensor, to then intersect the target portal sensor before exiting
  103.             // and must be ignored.
  104.             //
  105.             // A further fucking case occurs when we have portals very close together
  106.             // and we can have portals arrange like || || with a body passing from left
  107.             // to right, at the point where the body (partially passed through first two)
  108.             // started intersecting the next portal in the line, then moves backwards.
  109.             // the cloned body from second set of portals then enters 'back' into the first
  110.             // set. We want to treat this as one continuous 'chain' and not permit such
  111.             // back portalling. (#). We cannot however permit cyclic portalling.
  112.  
  113.             // Search for existing PortalPair for this (portal,body) pair
  114.             var portalPair:PortalPair = null;
  115.             if (bodyData.portal_pairs != null)
  116.             {  
  117.                 for each(var pair2:PortalPair in bodyData.portal_pairs)
  118.                 //for (pair in bodyData.portal_pairs)
  119.                 {
  120.                     if ((pair2.portalA == portal || pair2.portalB == portal)
  121.                      && (pair2.bodyA == body || pair2.bodyB == body)) {
  122.                         portalPair = pair2;
  123.                         break;
  124.                     }
  125.                 }
  126.             }
  127.  
  128.             // Ensure we're not entering a portal that is behind one we're currently going through.
  129.             if (portalPair == null && shapeData.portal_id != null && shape.cbTypes.has(PARTIAL)) {
  130.                 for each(var portalDt:PortalShapeData in shapeData.portals)
  131.                 //for (portalData in shapeData.portals)
  132.                 {
  133.                     if (behindPortal(portalDt.portal, portal.sensor.worldCOM))
  134.                     {
  135.                         return;
  136.                     }
  137.                 }
  138.             }
  139.  
  140.             var targetPortal:Portal = portal.target;
  141.             var scale:Number = targetPortal.width / portal.width;
  142.             if (portalPair == null)
  143.             {
  144.                 // Check we don't have case (#)
  145.                 if (bodyData.portal_pairs != null) {
  146.                     var stack:Array = [body];
  147.                     var visited:Array = [body];
  148.                     var longChain:Boolean = false;
  149.                     while (stack.length > 0 && !longChain)
  150.                     {
  151.                         //fixed zver
  152.                         //var body:Body = stack.pop();
  153.                         var bdy:Body = stack.pop();
  154.                         var bodyData2:* = bdy.userData;
  155.                        
  156.                         for each(var pair:PortalPair in bodyData2.portal_pairs)
  157.                         //for (pair in bodyData.__portal_pairs)
  158.                         {
  159.                             var otherBody:Body;
  160.                             if (pair.bodyA == bdy)
  161.                                 otherBody = pair.bodyB
  162.                             else
  163.                                 otherBody = pair.bodyA;
  164.                                
  165.                             if (visited.indexOf(otherBody) == -1)
  166.                             //if (Lambda.indexOf(visited, otherBody) == -1)
  167.                             {
  168.                                 visited.push(otherBody);
  169.                                 stack.push(otherBody);
  170.                             }
  171.  
  172.                             if (pair.portalA == portal || pair.portalB == portal) {
  173.                                 longChain = true;
  174.                                 break;
  175.                             }
  176.                         }
  177.                     }
  178.  
  179.                     if (longChain)
  180.                     {
  181.                         return;
  182.                     }
  183.                 }
  184.  
  185.  
  186.                 // This is a brand new interaction between this body and portal.
  187.  
  188.                 // Create cloned body with initial cloned shape.
  189.                 var clone:Body = new Body();
  190.                 var cloneShape:Shape = shape.copy();
  191.                 cloneShape.scale(scale, scale);
  192.                 cloneShape.body = clone;
  193.                 clone.space = body.space;
  194.  
  195.                 //trace("create portal joint");
  196.                
  197.                 //trace(portal.position, portal.direction, targetPortal.position, targetPortal.direction, scale);
  198.                
  199.                 // Create portal constraint.
  200.                 var pcon:PortalConstraint = new PortalConstraint(
  201.                     portal.body, portal.position, portal.direction,
  202.                     targetPortal.body, targetPortal.position, targetPortal.direction,
  203.                     scale, body, clone
  204.                 );
  205.                 pcon.space = clone.space;
  206.  
  207.                 // Set properties of clone to satisfy constraint.
  208.                 pcon.setProperties(clone, body);
  209.                
  210.                 //trace("info:", clone, body, clone.position, body.position);
  211.  
  212.                 // Create portal pair.
  213.                 portalPair = new PortalPair(portal, targetPortal, body, clone);
  214.  
  215.                 // Assign to pair lists.
  216.                 if (bodyData.portal_pairs == null)
  217.                 {
  218.                     bodyData.portal_pairs = [];
  219.                 }
  220.                 bodyData.portal_pairs.push(portalPair);
  221.                 var cloneData:* = clone.userData;
  222.                 cloneData.portal_pairs = [portalPair];
  223.  
  224.                 // Assign id if not existing.
  225.                 var id:* = shapeData.portal_id;
  226.                 var portals:Array/*PortalShapeData*/ = shapeData.portals;
  227.                 //if (id == 0)
  228.                 if (id == null)
  229.                 {
  230.                     id = shapeData.portal_id = portalID++;
  231.                     portals = shapeData.portals = [];
  232.                     shapeData.portal_active = 0;
  233.                 }
  234.                 portals.push(
  235.                     new PortalShapeData(portal, false)
  236.                     );
  237.                 shapeData.portal_active++;
  238.  
  239.                 var cloneShapeData:* = cloneShape.userData;
  240.                 cloneShapeData.portal_id = id;
  241.                 cloneShapeData.portals = [
  242.                     new PortalShapeData(portal.target, true)
  243.                     ];
  244.                 cloneShapeData.portal_active = 0;
  245.  
  246.                 // Add PARTIAL CbTypes if it is not already present.
  247.                 if (!shape.cbTypes.has(PARTIAL))
  248.                 {
  249.                     shape.cbTypes.add(PARTIAL);
  250.                     cloneShape.cbTypes.add(PARTIAL);
  251.                 }
  252.             }
  253.             else {
  254.                 // Portal interaction exists for this body and portal.
  255.  
  256.                 // Check shape, passed through front is not now just intersecting back
  257.                 // (Occurs if portals placed very close together into eachother)
  258.                 //
  259.                 // Also need to check that if shape pair exists, then at least one of
  260.                 // the portals is the same to avoid incrementing count. Can occur if
  261.                 // if the portals are not symmetric.
  262.                 var anyEqual:Boolean = false;
  263.                 if (shapeData.portals != null)
  264.                 {
  265.                     for each(var portalDt2:PortalShapeData in shapeData.portals)
  266.                     //for (portalData in shapeData.__portals)
  267.                     {
  268.                         if (portalDt2.portal.target == portal)
  269.                         {
  270.                             return;
  271.                         }
  272.                         if (portalDt2.portal == portal)
  273.                         {
  274.                             anyEqual = true;
  275.                         }
  276.                     }
  277.                 }
  278.  
  279.                 // Check to see if Shape is already part of the interaction.
  280.  
  281.                 var clone2:Body;
  282.                 if (portalPair.bodyA == body)
  283.                     clone2 = portalPair.bodyB
  284.                 else
  285.                     clone2 = portalPair.bodyA;
  286.                 var found:Boolean = false;
  287.                 //if (shapeData.portal_id != 0)
  288.                 if (shapeData.portal_id != null)
  289.                 {
  290.                     var si:ShapeIterator = clone2.shapes.iterator();
  291.                    
  292.                     while (si.hasNext())
  293.                     {
  294.                         var cloneShape2:Shape = si.next();
  295.                        
  296.                         var cloneShapeData2:* = cloneShape2.userData;
  297.                         if (cloneShapeData2.portal_id == shapeData.portal_id)
  298.                         {
  299.                             found = true;
  300.                             break;
  301.                         }
  302.                     }
  303.                     //for each(var cloneShape:Shape in clone.shapes)
  304.                     //for (cloneShape in clone.shapes)
  305.                    
  306.                 }
  307.  
  308.                 if (!found)
  309.                 {
  310.  
  311.                     // Shape is not part of the interaction, create its clone.
  312.                     var cloneShape3:Shape = shape.copy();
  313.                     cloneShape3.scale(scale, scale);
  314.                     cloneShape3.body = clone2;
  315.  
  316.                     //if (shapeData.portal_id == 0)
  317.                     if (shapeData.portal_id == null)
  318.                     {
  319.                         shapeData.portal_id = portalID++;
  320.                         shapeData.portals = [];
  321.                         shapeData.portal_active = 0;
  322.                     }
  323.  
  324.                     shapeData.portals.push(
  325.                         new PortalShapeData(portal, false)
  326.                         );
  327.                     shapeData.portal_active++;
  328.  
  329.                     var cloneShapeData3:* = cloneShape3.userData;
  330.                     cloneShapeData3.portal_id = shapeData.portal_id;
  331.                     cloneShapeData3.portals = [
  332.                         new PortalShapeData(portal.target, true)
  333.                         ];
  334.                     cloneShapeData3.portal_active = 0;
  335.  
  336.                     // Add PARTIAL CbTypes if it is not already present.
  337.                     if (!shape.cbTypes.has(PARTIAL))
  338.                     {
  339.                         shape.cbTypes.add(PARTIAL);
  340.                         cloneShape3.cbTypes.add(PARTIAL);
  341.                     }
  342.                 }
  343.                 else
  344.                     if (anyEqual)
  345.                     {
  346.                         shapeData.portal_active++;
  347.                     }
  348.             }
  349.             //---
  350.         }
  351.        
  352.         private function portalEnd(cb:InteractionCallback):void
  353.         {
  354.             //---
  355.             var portalSensor:Shape = cb.int1.castShape;
  356.             var shape:Shape = cb.int2.castShape;
  357.             var body:Body = shape.body;
  358.  
  359.             // Can occur when an object has entered portal, then intersects
  360.             // target portal before exiting.
  361.             //
  362.             // The final clean up of shapes can leave some such dangling cases.
  363.             if (body == null)
  364.             {
  365.                 return;
  366.             }
  367.  
  368.             var portalData2:* = portalSensor.userData;
  369.             var shapeData:* = shape.userData;
  370.             //var bodyData:* = body.userData;
  371.             var portal:Portal = portalData2.portal;
  372.  
  373.             // Two main cases to consider:
  374.             //    The shape has exited the portal sensor from the front
  375.             //    and we keep it in its current body
  376.             //
  377.             //    Or the shape has exited through the back and should be
  378.             //    completely teleported to other side.
  379.             //
  380.             // This is complicated with multiple portals as we must
  381.             // ensure we do not introduce 'cuts' in the portal chains
  382.             // and so we choose the simpler option: Remove the shape
  383.             // only when this is the 'last' 'exit' in the chain for
  384.             // the shape.
  385.             //
  386.             // We use the userData field __portal_active to track this
  387.             // and for robustneed compute which side the shape should move
  388.             // to at this point, storing it in __portal_target boolean
  389.             // (true) if shape is moved to target side of portal pair.
  390.  
  391.             var portals:Array/*PortalShapeData*/ = shapeData.portals;
  392.             var portalData:PortalShapeData = null;
  393.             for each(var pData:PortalShapeData in portals)
  394.             //for (pData in portals)
  395.             {
  396.                 if (pData.portal == portal)
  397.                 {
  398.                     portalData = pData;
  399.                     break;
  400.                 }
  401.             }
  402.  
  403.             if (portalData == null)
  404.             {
  405.                 // can occur if object enters portal, then intersects target portal
  406.                 // too. We ignore this in begin interaction
  407.                 // and we ignore it in end too.
  408.                 return;
  409.             }
  410.  
  411.             // Just exited portal sensor, no longer active in chain.
  412.             shapeData.portal_active--;
  413.             portalData.portal_target = behindPortal(portal, shape.worldCOM);
  414.  
  415.             // Get list of all shapes in the portal chain.
  416.             var shapes:Array = [shape];
  417.             var stack:Array = [body];
  418.             while (stack.length > 0)
  419.             {
  420.                 var stackBody:Body = stack.pop();
  421.                 var stackData:* = stackBody.userData;
  422.                 for each(var pair:PortalPair in stackData.portal_pairs)
  423.                 //for (pair in stackData.portal_pairs)
  424.                 {
  425.                     var clone:Body;
  426.                     if (pair.bodyA == stackBody)
  427.                         clone = pair.bodyB
  428.                     else
  429.                         clone = pair.bodyA;
  430.                        
  431.                     //for each(var cloneShape:Shape in clone.shapes)
  432.                     //for (cloneShape in clone.shapes)
  433.                     var si:ShapeIterator = clone.shapes.iterator();
  434.                    
  435.                     while(si.hasNext())
  436.                     {
  437.                         var cloneShape:Shape = si.next();
  438.                        
  439.                         //var cloneShapeData:* = cloneShape.userData;
  440.                         // Possible new shape in chain.
  441.                         var sData:* = cloneShape.userData;
  442.                         if (sData.portal_active != null)
  443.                         {
  444.                             if (shapes.indexOf(cloneShape) == -1)
  445.                             //if (Lambda.indexOf(shapes, cloneShape) == -1)
  446.                             {
  447.                                 shapes.push(cloneShape);
  448.                                 stack.push(cloneShape.body);
  449.                             }
  450.                         }
  451.                     }
  452.                 }
  453.             }
  454.  
  455.             // Check whether all shapes in chain are inactive.
  456.             var anyActive:Boolean = false;
  457.             for each(var s:Shape in shapes)
  458.             //for (s in shapes)
  459.             {
  460.                 var sData2:* = s.userData;
  461.                 if (sData2.portal_active != 0)
  462.                 {
  463.                     anyActive = true;
  464.                     break;
  465.                 }
  466.             }
  467.  
  468.             // Nothing to do yet.
  469.             if (anyActive)
  470.             {
  471.                 return;
  472.             }
  473.  
  474.             // All shapes are inactive.
  475.             // We now proceed to remove all but 1 of the shapes in the chain
  476.             // The remaining shape being the final destination of the portal chain.
  477.             var survivors:Array = [];
  478.             for each(var shape2:Shape in shapes)
  479.             //for (shape in shapes)
  480.             {
  481.                 var sData3:* = shape2.userData;
  482.  
  483.                 var anyTarget:Boolean = false;
  484.                 for each(var portalDt:PortalShapeData in sData3.portals)
  485.                 //for (portalData in sData.portals)
  486.                 {
  487.                     if (portalDt.portal_target)
  488.                     {
  489.                         anyTarget = true;
  490.                         break;
  491.                     }
  492.                 }
  493.  
  494.                 if (anyTarget)
  495.                 {
  496.                    
  497.                     //fix zver
  498.                     //var body:Body = shape.body;
  499.                     var bdy:Body = shape2.body;
  500.                     shape2.body = null;
  501.                     if (bdy.shapes.empty())
  502.                     {
  503.                         // disable any constraint using body we're about to destroy.
  504.                         //for each (var c:Constraint in body.constraints)
  505.                         //for (c in body.constraints)
  506.                         var ci:ConstraintIterator = bdy.constraints.iterator();
  507.                        
  508.                         while (ci.hasNext())
  509.                         {
  510.                             var c:Constraint = ci.next();
  511.                            
  512.                             c.active = false;
  513.                         }
  514.                         bdy.space = null;
  515.                        
  516.                         //trace("portal joint destroy", body);
  517.                     }
  518.                 }
  519.                 else
  520.                 {
  521.                     // Shape survives to portal another data.
  522.                     survivors.push(shape2);
  523.                 }
  524.             }
  525.  
  526.             for each(var survivor:Shape in survivors)
  527.             //for (survivor in survivors)
  528.             {
  529.                 var survivorData:* = survivor.userData;
  530.                 survivorData.portal_id = null;
  531.                 survivorData.portals = [];
  532.                 survivor.cbTypes.remove(PARTIAL);
  533.  
  534.                 // Cull any dangling portal pairs.
  535.                 var survivorBody:Body = survivor.body;
  536.                 var bodyData:* = survivorBody.userData;
  537.                 var i:int = 0;
  538.                 var pk:int = bodyData.portal_pairs.length;
  539.                
  540.                 //while (i < bodyData.portal_pairs.length)
  541.                 while (i < pk)
  542.                 {  
  543.                     var pair2:PortalPair = bodyData.portal_pairs[i];
  544.                     var clone2:Body;
  545.                     if (pair2.bodyA == survivorBody)
  546.                         clone2 = pair.bodyB
  547.                     else
  548.                         clone2 = pair.bodyA;
  549.                     if (clone2.space == null)
  550.                     {
  551.                         // pair should be culled.
  552.                         bodyData.portal_pairs.splice(i, 1);
  553.                         pk--;
  554.                     }
  555.                     else
  556.                     {
  557.                         i++;
  558.                     }
  559.                 }
  560.             }
  561.             //---
  562.         }
  563.        
  564.         private function behindPortal(portal:Portal, position:Vec2):Boolean
  565.         {
  566.             var u:Vec2 = position.sub(portal.body.localPointToWorld(portal.position, true));
  567.             var v:Vec2 = portal.body.localVectorToWorld(portal.direction);
  568.             var behind:Boolean = (u.dot(v) <= 0);
  569.             u.dispose();
  570.             v.dispose();
  571.             return behind;
  572.         }
  573.        
  574.         private function handlePartial(partial:Shape, carb:CollisionArbiter, ret:PreFlag):PreFlag
  575.         {
  576.             var partialData:* = partial.userData;
  577.             var portals:Array/*PortalShapeData*/ = partialData.portals;
  578.            
  579.             for each(var portalData:PortalShapeData in portals)
  580.             //for (portalData in portals)
  581.             {
  582.                 var portal:Portal = portalData.portal;
  583.  
  584.                 var anyBehind:Boolean = false;
  585.  
  586.                 var i:int = 0;
  587.                
  588.                 while (i < carb.contacts.length)
  589.                 {
  590.                     var contact:Contact = carb.contacts.at(i);
  591.                     var scale:Number = ((partial == carb.shape1) ? 0.5 : -0.5);
  592.                     var pos:Vec2 = contact.position.addMul(carb.normal, contact.penetration * scale);
  593.                    
  594.                     if (behindPortal(portal, pos))
  595.                     {
  596.                         carb.contacts.remove(contact);
  597.                         anyBehind = true;
  598.                     }
  599.                     else
  600.                     {
  601.                         i++;
  602.                     }
  603.                     pos.dispose();
  604.                 }
  605.  
  606.                 // If there are any contact points behind,
  607.                 // we also cull any virtual ones that may cause issues.
  608.                 // We don't simply clear as we do want non-virtual ones.
  609.                 if (anyBehind)
  610.                 {
  611.                     i = 0;
  612.                     while (i < carb.contacts.length)
  613.                     {
  614.                         var contact2:Contact = carb.contacts.at(i);
  615.                         if (contact2.penetration < 0)
  616.                         {
  617.                             carb.contacts.remove(contact2);
  618.                         }
  619.                         else
  620.                         {
  621.                             i++;
  622.                         }
  623.                     }
  624.                 }
  625.  
  626.                 if (carb.contacts.empty()) {
  627.                     return PreFlag.IGNORE_ONCE;
  628.                 }
  629.             }
  630.  
  631.             return ret;
  632.         }
  633.        
  634.         private function backCollision(cb:PreCallback):PreFlag
  635.         {
  636.             //---
  637.             var partial:Shape = cb.int1.castShape;
  638.             var other:Shape = cb.int2.castShape;
  639.             var carb:CollisionArbiter = cb.arbiter.collisionArbiter;
  640.  
  641.             var partialData:* = partial.userData;
  642.             var portals:* = partialData.portals;
  643.            
  644.             for each(var portalData:PortalShapeData in portals)
  645.             //for (portalData in portals)
  646.             {
  647.                 var portal:Portal = portalData.portal;
  648.  
  649.                 // Special case for the portal sides and back.
  650.                 // We always permit collision with the portal sides
  651.                 // and never with the portal back.
  652.                 if (other.body == portal.body) {
  653.                     if (portal.collide.indexOf(other) != -1)
  654.                     //if (Lambda.indexOf(portal.collide, other) != -1)
  655.                     {
  656.                         return PreFlag.ACCEPT_ONCE;
  657.                     }
  658.                     else
  659.                         if (portal.ignore.indexOf(other) != -1)
  660.                         //if (Lambda.indexOf(portal.ignore, other) != -1)
  661.                         {
  662.                             return PreFlag.IGNORE_ONCE;
  663.                         }
  664.                 }
  665.             }
  666.  
  667.             var ret:PreFlag = PreFlag.ACCEPT_ONCE;
  668.             ret = handlePartial(partial, carb, ret);
  669.             if (other.cbTypes.has(PARTIAL))
  670.             {
  671.                 ret = handlePartial(other, carb, ret);
  672.             }
  673.             return ret;
  674.             //---
  675.         }
  676.     }
  677.    
  678. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement