Author Topic: 2D Physics Engine  (Read 7225 times)

0 Members and 1 Guest are viewing this topic.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
2D Physics Engine
« on: April 20, 2021, 04:07:08 pm »
Hello

I've ported a simple 2D physics engine from JAVA/C++ to QB. I was thinking about using it for a larger project I have in mind, but I thought others might find some use in it or just use it to mess around.

It's a bit slow and it only handles convex polygons and circles. In future iterations I hope to add more features and optimzations.

The orginal code was written by Randy Gaul, and he has nothing to do with this project. His website is http://RandyGaul.net and it has a lot programming resources and articles.


Code: QB64: [Select]
  1. '/*
  2. '    Copyright (c) 2013 Randy Gaul http://RandyGaul.net
  3.  
  4. '    This software is provided 'as-is', without any express or implied
  5. '    warranty. In no event will the authors be held liable for any damages
  6. '    arising from the use of this software.
  7.  
  8. '    Permission is granted to anyone to use this software for any purpose,
  9. '    including commercial applications, and to alter it and redistribute it
  10. '    freely, subject to the following restrictions:
  11. '      1. The origin of this software must not be misrepresented; you must not
  12. '         claim that you wrote the original software. If you use this software
  13. '         in a product, an acknowledgment in the product documentation would be
  14. '         appreciated but is not required.
  15. '      2. Altered source versions must be plainly marked as such, and must not be
  16. '         misrepresented as being the original software.
  17. '      3. This notice may not be removed or altered from any source distribution.
  18.  
  19. '    Port to Java by Philip Diffenderfer http://magnos.org
  20. '    Port to QB64 by justsomeguy
  21. '*/
  22. '
  23.  
  24. '**********************************************************************************************
  25. '   Setup Types and Variables
  26. '**********************************************************************************************
  27. Type tVECTOR2d
  28.     x As _Float
  29.     y As _Float
  30.  
  31. Type tLINE2d ' Not used
  32.     a As tVECTOR2d
  33.     b As tVECTOR2d
  34.  
  35. Type tFACE2d ' Not used
  36.     f0 As tVECTOR2d
  37.     f1 As tVECTOR2d
  38.  
  39. Type tTRIANGLE ' Not used
  40.     a As tVECTOR2d
  41.     b As tVECTOR2d
  42.     c As tVECTOR2d
  43.  
  44. Type tMATRIX2d
  45.     m00 As _Float
  46.     m01 As _Float
  47.     m10 As _Float
  48.     m11 As _Float
  49.  
  50. Type tSHAPE
  51.     ty As Integer ' cSHAPE_CIRCLE = 1, cSHAPE_POLYGON = 2
  52.     radius As _Float ' Only necessary for circle shapes
  53.     u As tMATRIX2d ' Only neceassary for polygons
  54.  
  55. Type tPOLY 'list of vertices for all objects in simulation
  56.     vert As tVECTOR2d
  57.     norm As tVECTOR2d
  58.  
  59. Type tPOLYATTRIB 'keep track of polys in monlithic list of vertices
  60.     start As Integer ' starting vertex of the polygon
  61.     count As Integer ' number of vertices in polygon
  62.  
  63. Type tBODY
  64.     position As tVECTOR2d
  65.     velocity As tVECTOR2d
  66.     force As tVECTOR2d
  67.     angularVelocity As _Float
  68.     torque As _Float
  69.     orient As _Float
  70.     mass As _Float
  71.     invMass As _Float
  72.     inertia As _Float
  73.     invInertia As _Float
  74.     staticFriction As _Float
  75.     dynamicFriction As _Float
  76.     restitution As _Float
  77.     shape As tSHAPE
  78.     c As Long ' color
  79.     visible As Integer 'Hide a body ;)
  80.  
  81. Type tMANIFOLD
  82.     A As Integer
  83.     B As Integer
  84.     penetration As _Float
  85.     normal As tVECTOR2d
  86.     contactCount As Integer
  87.     e As _Float
  88.     df As _Float
  89.     sf As _Float
  90.  
  91. Const cSHAPE_CIRCLE = 1
  92. Const cSHAPE_POLYGON = 2
  93. Const cPRECISION = 100
  94. Const cMAXNUMOFTRIANGLES = 100
  95. Const cMAXNUMBEROFOBJECTS = 1000 ' Max number of objects at one time
  96. Const cMAXNUMBEROFPOLYGONS = 10000 ' Max number of total vertices included in all objects
  97. Const cPI = 3.14159
  98. Const cEPSILON = 0.0001
  99. Const cEPSILON_SQ = cEPSILON * cEPSILON
  100. Const cBIAS_RELATIVE = 0.95
  101. Const cBIAS_ABSOLUTE = 0.01
  102. Const cDT = 1.0 / 60.0
  103. Const cITERATIONS = 10
  104. Const cPENETRATION_ALLOWANCE = 0.05
  105. Const cPENETRATION_CORRECTION = 0.4 ' misspelled in original code
  106.  
  107. Dim Shared sGRAVITY As tVECTOR2d: Call vectorSet(sGRAVITY, 0.0, 100.0)
  108. Dim Shared sRESTING As _Float: Dim o As tVECTOR2d: Call vectorMultiplyScalarND(o, sGRAVITY, cDT): sRESTING = vectorLengthSq(o) + cEPSILON
  109. Dim Shared sNUMBEROFBODIES As Integer: sNUMBEROFBODIES = 80 ' 0 is included - Don't go under 10 for the current scene
  110.  
  111. Dim poly(cMAXNUMBEROFPOLYGONS) As tPOLY
  112. Dim polyattributes(cMAXNUMBEROFPOLYGONS) As tPOLYATTRIB
  113. Dim body(cMAXNUMBEROFOBJECTS) As tBODY
  114.  
  115.  
  116. Const cSCENE_USER = 0
  117. Const cSCENE_FLOOR = 1
  118. Const cSCENE_SPINNEROFDEATH = 2
  119. Const cSCENE_RIGHTRAMP = 3
  120. Const cSCENE_LEFTRAMP = 4
  121. Const cSCENE_DOZEROFDOOM = 5
  122. Const cSCENE_ELEVATOROFTERROR = 6
  123. Const cSCENE_RIGHTWALL = 7
  124. Const cSCENE_ELEVATORKICKER = 8
  125.  
  126.  
  127. '**********************************************************************************************
  128. _Title "phyNGN"
  129. Screen _NewImage(1024, 768, 32)
  130. Call buildSimpleScene(poly(), polyattributes(), body())
  131. '**********************************************************************************************
  132.     Cls , _RGB32(28, 28, 22)
  133.     Call animateScene(body())
  134.     Call renderBodies(poly(), polyattributes(), body())
  135.     Call impulseStep(poly(), polyattributes(), body(), cDT, cITERATIONS)
  136.     _Display
  137.     _Limit 240
  138. '**********************************************************************************************
  139. '   End of Main loop
  140. '**********************************************************************************************
  141.  
  142. '**********************************************************************************************
  143. '   Scene Creation and Handling Ahead
  144. '**********************************************************************************************
  145.  
  146. Sub animateScene (body() As tBODY)
  147.     If _MouseInput Then ' Terrible use interaction
  148.         Call vectorSet(body(cSCENE_USER).velocity, _MouseMovementX * 100, _MouseMovementY * 100)
  149.     End If
  150.  
  151.     body(cSCENE_SPINNEROFDEATH).orient = body(cSCENE_SPINNEROFDEATH).orient + (cPI / 90): If body(cSCENE_SPINNEROFDEATH).orient > 2 * cPI Then body(cSCENE_SPINNEROFDEATH).orient = 0
  152.     Call matrixSetRadians(body(cSCENE_SPINNEROFDEATH).shape.u, body(cSCENE_SPINNEROFDEATH).orient)
  153.     body(cSCENE_DOZEROFDOOM).position.x = body(cSCENE_DOZEROFDOOM).position.x - 1
  154.     If body(cSCENE_DOZEROFDOOM).position.x < 120 Then body(cSCENE_DOZEROFDOOM).position.x = _Width
  155.  
  156.     body(cSCENE_ELEVATOROFTERROR).position.y = body(cSCENE_ELEVATOROFTERROR).position.y - 1
  157.     If body(cSCENE_ELEVATOROFTERROR).position.y < 50 Then body(cSCENE_ELEVATOROFTERROR).position.y = _Height - 25
  158.  
  159. Sub buildSimpleScene (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY)
  160.     Dim i, ty As Integer
  161.  
  162.     Call createBoxBodies(p(), pa(), body(), cSCENE_USER, 10, 10)
  163.     Call vectorSet(body(cSCENE_USER).position, (_Width / 2), (_Height / 2))
  164.     Call vectorSet(body(cSCENE_USER).velocity, 0.0, 0.0)
  165.  
  166.     Call createBoxBodies(p(), pa(), body(), cSCENE_FLOOR, 1000, 5)
  167.     Call vectorSet(body(cSCENE_FLOOR).position, (_Width / 2), (_Height - 20))
  168.     Call bodySetStatic(body(cSCENE_FLOOR))
  169.  
  170.     Call createBoxBodies(p(), pa(), body(), cSCENE_SPINNEROFDEATH, 75, 5)
  171.     Call vectorSet(body(cSCENE_SPINNEROFDEATH).position, (_Width / 2), (_Height - 100))
  172.     Call bodySetStatic(body(cSCENE_SPINNEROFDEATH))
  173.  
  174.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTRAMP, 350, 5)
  175.     Call vectorSet(body(cSCENE_RIGHTRAMP).position, (_Width * .75) + 35, (_Height / 2) + 105)
  176.     body(cSCENE_RIGHTRAMP).orient = (cPI * .75)
  177.     Call matrixSetRadians(body(cSCENE_RIGHTRAMP).shape.u, body(cSCENE_RIGHTRAMP).orient)
  178.     Call bodySetStatic(body(cSCENE_RIGHTRAMP))
  179.  
  180.     Call createBoxBodies(p(), pa(), body(), cSCENE_LEFTRAMP, 225, 5)
  181.     Call vectorSet(body(cSCENE_LEFTRAMP).position, (_Width * .25) + 55, (_Height / 2))
  182.     body(cSCENE_LEFTRAMP).orient = (cPI * .1)
  183.     Call matrixSetRadians(body(cSCENE_LEFTRAMP).shape.u, body(cSCENE_LEFTRAMP).orient)
  184.     Call bodySetStatic(body(cSCENE_LEFTRAMP))
  185.  
  186.     Call createBoxBodies(p(), pa(), body(), cSCENE_DOZEROFDOOM, 5, 50)
  187.     Call vectorSet(body(cSCENE_DOZEROFDOOM).position, 0, _Height - 75)
  188.     Call bodySetStatic(body(cSCENE_DOZEROFDOOM))
  189.  
  190.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATOROFTERROR, 50, 5)
  191.     Call vectorSet(body(cSCENE_ELEVATOROFTERROR).position, 55, _Height - 25)
  192.     body(cSCENE_ELEVATOROFTERROR).orient = (cPI * .98)
  193.     Call matrixSetRadians(body(cSCENE_ELEVATOROFTERROR).shape.u, body(cSCENE_ELEVATOROFTERROR).orient)
  194.     Call bodySetStatic(body(cSCENE_ELEVATOROFTERROR))
  195.  
  196.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTWALL, 5, 375)
  197.     Call vectorSet(body(cSCENE_RIGHTWALL).position, 5, _Height / 2 - 20)
  198.     Call bodySetStatic(body(cSCENE_RIGHTWALL))
  199.  
  200.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATORKICKER, 100, 5)
  201.     Call vectorSet(body(cSCENE_ELEVATORKICKER).position, 55, 120)
  202.     body(cSCENE_ELEVATORKICKER).orient = (cPI * .70)
  203.     Call matrixSetRadians(body(cSCENE_ELEVATORKICKER).shape.u, body(cSCENE_ELEVATORKICKER).orient)
  204.     Call bodySetStatic(body(cSCENE_ELEVATORKICKER))
  205.  
  206.     For i = 9 To sNUMBEROFBODIES
  207.         If Rnd > .25 Then ' Circles are must faster than polygons
  208.             ty = cSHAPE_CIRCLE
  209.         Else
  210.             ty = cSHAPE_POLYGON
  211.         End If
  212.         Call createBodies(p(), pa(), body(), i, ty)
  213.         Call vectorSet(body(i).position, (Rnd * _Width / 2) + (_Width / 4), ((Rnd * _Height / 2) + (_Height / 4)) - 200)
  214.         Call vectorSet(body(i).velocity, Rnd * 100 - 50, Rnd * 100 - 50)
  215.     Next
  216.  
  217.  
  218. Sub createCircleBody (pa() As tPOLYATTRIB, body() As tBODY, index As Integer, radius As _Float)
  219.     Dim shape As tSHAPE
  220.     Call shapeCreate(shape, cSHAPE_CIRCLE, radius)
  221.     Call bodyCreate(body(), index, shape)
  222.     Call circleInitialize(body(), index)
  223.     ' Even though circles do not have Polygons, they still must be included in the Vertices
  224.     If index = 0 Then
  225.         pa(index).start = 0
  226.     Else
  227.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  228.     End If
  229.     pa(index).count = 1
  230.     body(index).c = _RGB32(255, 255, 255)
  231.  
  232. Sub createBoxBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, xs As _Float, ys As _Float)
  233.     Dim shape As tSHAPE
  234.     Call shapeCreate(shape, cSHAPE_POLYGON, 0)
  235.     Call bodyCreate(body(), index, shape)
  236.     Call boxCreate(p(), pa(), index, xs, ys)
  237.     Call polygonInitialize(body(), p(), pa(), index)
  238.     body(index).c = _RGB32(255, 255, 255)
  239.  
  240. Sub createBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, ty As Integer)
  241.     If ty = cSHAPE_CIRCLE Then
  242.         Call createCircleBody(pa(), body(), index, 10 + Rnd * 20)
  243.     Else
  244.         If ty = cSHAPE_POLYGON Then
  245.             Call createBoxBodies(p(), pa(), body(), index, 5 + Rnd * 10, 5 + Rnd * 10)
  246.         End If
  247.     End If
  248.     body(index).c = _RGB32(55 + Rnd * 200, 55 + Rnd * 200, 55 + Rnd * 200)
  249.  
  250. '**********************************************************************************************
  251. '   Physics and Collision Stuff Ahead
  252. '**********************************************************************************************
  253.  
  254. Sub impulseIntegrateForces (b As tBODY, dt As _Float)
  255.     If b.invMass = 0.0 Then Exit Sub
  256.     Dim dts As _Float
  257.     dts = dt * .5
  258.     Call vectorAddVectorScalar(b.velocity, b.force, b.invMass * dts)
  259.     Call vectorAddVectorScalar(b.velocity, sGRAVITY, dts)
  260.     b.angularVelocity = b.angularVelocity + (b.torque * b.invInertia * dts)
  261.  
  262. Sub impulseIntegrateVelocity (body As tBODY, dt As _Float)
  263.     If body.invMass = 0.0 Then Exit Sub
  264.     Call vectorAddVectorScalar(body.position, body.velocity, dt)
  265.     body.orient = body.orient + (body.angularVelocity * dt)
  266.     Call matrixSetRadians(body.shape.u, body.orient)
  267.     Call impulseIntegrateForces(body, dt)
  268.  
  269. Sub impulseStep (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, dt As _Float, iterations As Integer)
  270.     Dim A As tBODY
  271.     Dim B As tBODY
  272.     Dim c(cMAXNUMBEROFOBJECTS) As tVECTOR2d
  273.     Dim m As tMANIFOLD
  274.     Dim manifolds(sNUMBEROFBODIES * sNUMBEROFBODIES) As tMANIFOLD
  275.     Dim collisions(sNUMBEROFBODIES * sNUMBEROFBODIES, cMAXNUMBEROFOBJECTS) As tVECTOR2d
  276.     Dim manifoldCount As Integer: manifoldCount = 0
  277.     '    // Generate new collision info
  278.     Dim i, j, k As Integer
  279.     ' TODO: Implement hidden object logic
  280.     For i = 0 To sNUMBEROFBODIES ' number of bodies
  281.         A = body(i)
  282.         For j = i + 1 To sNUMBEROFBODIES
  283.             B = body(j)
  284.             If A.invMass = 0.0 And B.invMass = 0.0 Then _Continue
  285.             'Mainfold solve - handle collisions
  286.             If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_CIRCLE Then
  287.                 Call collisionCCHandle(m, c(), A, B)
  288.             Else
  289.                 If A.shape.ty = cSHAPE_POLYGON And B.shape.ty = cSHAPE_POLYGON Then
  290.                     Call collisionPPHandle(p(), pa(), body(), m, c(), i, j)
  291.                 Else
  292.                     If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_POLYGON Then
  293.                         Call collisionCPHandle(p(), pa(), body(), m, c(), i, j)
  294.                     Else
  295.                         If B.shape.ty = cSHAPE_CIRCLE And A.shape.ty = cSHAPE_POLYGON Then
  296.                             Call collisionPCHandle(p(), pa(), body(), m, c(), i, j)
  297.                         End If
  298.                     End If
  299.                 End If
  300.             End If
  301.             If m.contactCount > 0 Then
  302.                 m.A = i 'identify the index of objects
  303.                 m.B = j
  304.                 manifolds(manifoldCount) = m
  305.                 For k = 0 To m.contactCount
  306.                     collisions(manifoldCount, k) = c(k)
  307.                 Next
  308.                 manifoldCount = manifoldCount + 1
  309.             End If
  310.         Next
  311.     Next
  312.     '   // Integrate forces
  313.     For i = 0 To sNUMBEROFBODIES
  314.         Call impulseIntegrateForces(body(i), dt)
  315.     Next
  316.     '   // Initialize collision
  317.     For i = 0 To manifoldCount - 1
  318.         ' this is the stupidest thing ever since QB will not let you split arrays
  319.         For k = 0 To manifolds(i).contactCount - 1
  320.             c(k) = collisions(i, k)
  321.         Next
  322.         Call manifoldInit(manifolds(i), body(), c())
  323.     Next
  324.     '// Solve collisions
  325.     For j = 0 To iterations - 1
  326.         For i = 0 To manifoldCount - 1
  327.             For k = 0 To manifolds(i).contactCount - 1
  328.                 c(k) = collisions(i, k)
  329.             Next
  330.             Call manifoldApplyImpulse(manifolds(i), body(), c())
  331.         Next i
  332.     Next j
  333.     '// Integrate velocities
  334.     For i = 0 To sNUMBEROFBODIES
  335.         Call impulseIntegrateVelocity(body(i), dt)
  336.     Next
  337.     '// Correct positions
  338.     For i = 0 To manifoldCount - 1
  339.         Call manifoldPositionalCorrection(manifolds(i), body())
  340.     Next
  341.     '// Clear all forces
  342.     For i = 0 To sNUMBEROFBODIES
  343.         Call vectorSet(body(i).force, 0, 0)
  344.         body(i).torque = 0
  345.     Next
  346.  
  347. Sub bodyApplyImpulse (body As tBODY, impulse As tVECTOR2d, contactVector As tVECTOR2d)
  348.     Call vectorAddVectorScalar(body.velocity, impulse, body.invMass)
  349.     body.angularVelocity = body.angularVelocity + body.invInertia * vectorCross(contactVector, impulse)
  350.  
  351. Sub bodySetStatic (body As tBODY)
  352.     body.inertia = 0.0
  353.     body.invInertia = 0.0
  354.     body.mass = 0.0
  355.     body.invMass = 0.0
  356.  
  357. Sub manifoldInit (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  358.     Dim ra As tVECTOR2d
  359.     Dim rb As tVECTOR2d
  360.     Dim rv As tVECTOR2d
  361.     Dim tv1 As tVECTOR2d
  362.     Dim tv2 As tVECTOR2d
  363.     m.e = scalarMin(body(m.A).restitution, body(m.B).restitution)
  364.     m.sf = Sqr(body(m.A).staticFriction * body(m.A).staticFriction)
  365.     m.df = Sqr(body(m.A).dynamicFriction * body(m.A).dynamicFriction)
  366.     Dim i As Integer
  367.     For i = 0 To m.contactCount - 1
  368.         Call vectorSubVectorND(contacts(i), body(m.A).position, ra)
  369.         Call vectorSubVectorND(contacts(i), body(m.B).position, rb)
  370.  
  371.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  372.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  373.         Call vectorAddVector(tv1, body(m.B).velocity)
  374.         Call vectorSubVectorND(tv2, body(m.A).velocity, tv2)
  375.         Call vectorSubVectorND(rv, tv1, tv2)
  376.  
  377.         If vectorLengthSq(rv) < sRESTING Then
  378.             m.e = 0.0
  379.         End If
  380.     Next
  381.  
  382. Sub manifoldApplyImpulse (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  383.     Dim ra As tVECTOR2d
  384.     Dim rb As tVECTOR2d
  385.     Dim rv As tVECTOR2d
  386.     Dim tv1 As tVECTOR2d
  387.     Dim tv2 As tVECTOR2d
  388.     Dim contactVel As _Float
  389.  
  390.     Dim raCrossN As _Float
  391.     Dim rbCrossN As _Float
  392.     Dim invMassSum As _Float
  393.     Dim i As Integer
  394.     Dim j As _Float
  395.     Dim impulse As tVECTOR2d
  396.  
  397.     Dim t As tVECTOR2d
  398.     Dim jt As _Float
  399.     Dim tangentImpulse As tVECTOR2d
  400.  
  401.     If impulseEqual(body(m.A).invMass + body(m.B).invMass, 0.0) Then
  402.         Call manifoldInfiniteMassCorrection(body(m.A), body(m.B))
  403.         Exit Sub
  404.     End If
  405.  
  406.     For i = 0 To m.contactCount - 1
  407.         '// Calculate radii from COM to contact
  408.         '// Vec2 ra = contacts[i] - A->position;
  409.         '// Vec2 rb = contacts[i] - B->position;
  410.         Call vectorSubVectorND(ra, contacts(i), body(m.A).position)
  411.         Call vectorSubVectorND(rb, contacts(i), body(m.B).position)
  412.  
  413.         '// Relative velocity
  414.         '// Vec2 rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  415.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  416.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  417.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  418.         Call vectorSubVector(rv, body(m.A).velocity)
  419.         Call vectorSubVector(rv, tv2)
  420.  
  421.         '// Relative velocity along the normal
  422.         '// real contactVel = Dot( rv, normal );
  423.         contactVel = vectorDot(rv, m.normal)
  424.  
  425.  
  426.         '// Do not resolve if velocities are separating
  427.         If contactVel > 0 Then Exit Sub
  428.  
  429.         '// real raCrossN = Cross( ra, normal );
  430.         '// real rbCrossN = Cross( rb, normal );
  431.         '// real invMassSum = A->im + B->im + Sqr( raCrossN ) * A->iI + Sqr( rbCrossN ) * B->iI;
  432.         raCrossN = vectorCross(ra, m.normal)
  433.         rbCrossN = vectorCross(rb, m.normal)
  434.         invMassSum = body(m.A).invMass + body(m.B).invMass + (raCrossN * raCrossN) * body(m.A).invInertia + (rbCrossN * rbCrossN) * body(m.B).invInertia
  435.  
  436.  
  437.         '// Calculate impulse scalar
  438.         j = -(1.0 + m.e) * contactVel
  439.         j = j / invMassSum
  440.         j = j / m.contactCount
  441.  
  442.         '// Apply impulse
  443.         Call vectorMultiplyScalarND(impulse, m.normal, j)
  444.  
  445.         Call vectorNegND(tv1, impulse)
  446.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  447.         Call bodyApplyImpulse(body(m.B), impulse, rb)
  448.  
  449.         '// Friction impulse
  450.         '// rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  451.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  452.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  453.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  454.         Call vectorSubVector(rv, body(m.A).velocity)
  455.         Call vectorSubVector(rv, tv2)
  456.  
  457.         '// Vec2 t = rv - (normal * Dot( rv, normal ));
  458.         '// t.Normalize( );
  459.         Call vectorMultiplyScalarND(t, m.normal, vectorDot(rv, m.normal))
  460.         Call vectorSubVectorND(t, rv, t)
  461.         Call vectorNormalize(t)
  462.  
  463.         '// j tangent magnitude
  464.         jt = -vectorDot(rv, t)
  465.         jt = jt / invMassSum
  466.         jt = jt / m.contactCount
  467.  
  468.         '// Don't apply tiny friction impulses
  469.         If impulseEqual(jt, 0.0) Then Exit Sub
  470.  
  471.         '// Coulumb's law
  472.         If Abs(jt) < j * m.sf Then
  473.             Call vectorMultiplyScalarND(tangentImpulse, t, jt)
  474.         Else
  475.             Call vectorMultiplyScalarND(tangentImpulse, t, -j * m.df)
  476.         End If
  477.  
  478.         '// Apply friction impulse
  479.         '// A->ApplyImpulse( -tangentImpulse, ra );
  480.         '// B->ApplyImpulse( tangentImpulse, rb );
  481.         Call vectorNegND(tv1, tangentImpulse)
  482.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  483.         Call bodyApplyImpulse(body(m.B), tangentImpulse, rb)
  484.     Next i
  485.  
  486. Sub manifoldPositionalCorrection (m As tMANIFOLD, body() As tBODY)
  487.     Dim correction As _Float
  488.     correction = scalarMax(m.penetration - cPENETRATION_ALLOWANCE, 0.0) / (body(m.A).invMass + body(m.B).invMass) * cPENETRATION_CORRECTION
  489.     Call vectorAddVectorScalar(body(m.A).position, m.normal, -body(m.A).invMass * correction)
  490.     Call vectorAddVectorScalar(body(m.B).position, m.normal, body(m.B).invMass * correction)
  491.  
  492. Sub manifoldInfiniteMassCorrection (A As tBODY, B As tBODY)
  493.     Call vectorSet(A.velocity, 0, 0)
  494.     Call vectorSet(B.velocity, 0, 0)
  495.  
  496. '**********************************************************************************************
  497. '   Collision Stuff Ahead
  498. '**********************************************************************************************
  499.  
  500. Sub collisionCCHandle (m As tMANIFOLD, contacts() As tVECTOR2d, A As tBODY, B As tBODY)
  501.     Dim normal As tVECTOR2d
  502.     Dim dist_sqr As _Float
  503.     Dim radius As _Float
  504.  
  505.     Call vectorSubVectorND(normal, B.position, A.position)
  506.     dist_sqr = vectorLengthSq(normal)
  507.     radius = A.shape.radius + B.shape.radius
  508.  
  509.     If (dist_sqr >= radius * radius) Then
  510.         m.contactCount = 0
  511.     Else
  512.         Dim distance As _Float
  513.         distance = Sqr(dist_sqr)
  514.         m.contactCount = 1
  515.  
  516.         If distance = 0 Then
  517.             m.penetration = A.shape.radius
  518.             Call vectorSet(m.normal, 1.0, 0.0)
  519.             Call vectorSetVector(contacts(0), A.position)
  520.         Else
  521.             m.penetration = radius - distance
  522.             Call vectorDivideScalarND(m.normal, normal, distance)
  523.  
  524.             Call vectorMultiplyScalarND(contacts(0), m.normal, A.shape.radius)
  525.             Call vectorAddVector(contacts(0), A.position)
  526.         End If
  527.     End If
  528.  
  529. Sub collisionPCHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  530.     Call collisionCPHandle(p(), pa(), body(), m, contacts(), B, A)
  531.     If m.contactCount > 0 Then
  532.         Call vectorNeg(m.normal)
  533.     End If
  534.  
  535. Sub collisionCPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  536.     'A is the Circle
  537.     'B is the POLY
  538.     m.contactCount = 0
  539.     Dim center As tVECTOR2d
  540.     Dim tm As tMATRIX2d
  541.     Dim tv As tVECTOR2d
  542.     Dim ARadius As _Float: ARadius = body(A).shape.radius
  543.  
  544.  
  545.     Call vectorSubVectorND(center, body(A).position, body(B).position)
  546.     Call matrixTranspose(body(B).shape.u, tm)
  547.     Call matrixMultiplyVector(tm, center, center)
  548.  
  549.     Dim separation As _Float: separation = -9999999
  550.     Dim faceNormal As Integer: faceNormal = 0
  551.     Dim i As Integer
  552.     Dim s As _Float
  553.     For i = 0 To pa(B).count
  554.         Call vectorSubVectorND(tv, center, p(pa(B).start + i).vert)
  555.         s = vectorDot(p(pa(B).start + i).norm, tv)
  556.         If s > ARadius Then Exit Sub
  557.         If s > separation Then
  558.             separation = s
  559.             faceNormal = i
  560.         End If
  561.     Next
  562.     Dim v1 As tVECTOR2d
  563.     v1 = p(pa(B).start + faceNormal).vert
  564.     Dim i2 As Integer
  565.     i2 = pa(B).start + arrayNextIndex(faceNormal, pa(B).count)
  566.     Dim v2 As tVECTOR2d
  567.     v2 = p(i2).vert
  568.  
  569.     If separation < cEPSILON Then
  570.         m.contactCount = 1
  571.         Call matrixMultiplyVector(body(B).shape.u, p(pa(B).start + faceNormal).norm, m.normal)
  572.         Call vectorNeg(m.normal)
  573.         Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  574.         Call vectorAddVector(contacts(0), body(A).position)
  575.         m.penetration = ARadius
  576.         Exit Sub
  577.     End If
  578.  
  579.     Dim dot1 As _Float
  580.     Dim dot2 As _Float
  581.  
  582.     Dim tv1 As tVECTOR2d
  583.     Dim tv2 As tVECTOR2d
  584.     Dim n As tVECTOR2d
  585.     Call vectorSubVectorND(tv1, center, v1)
  586.     Call vectorSubVectorND(tv2, v2, v1)
  587.     dot1 = vectorDot(tv1, tv2)
  588.     Call vectorSubVectorND(tv1, center, v2)
  589.     Call vectorSubVectorND(tv2, v1, v2)
  590.     dot2 = vectorDot(tv1, tv2)
  591.     m.penetration = ARadius - separation
  592.     If dot1 <= 0.0 Then
  593.         If vectorSqDist(center, v1) > ARadius * ARadius Then Exit Sub
  594.         m.contactCount = 1
  595.         Call vectorSubVectorND(n, v1, center)
  596.         Call matrixMultiplyVector(body(B).shape.u, n, n)
  597.         Call vectorNormalize(n)
  598.         m.normal = n
  599.         Call matrixMultiplyVector(body(B).shape.u, v1, v1)
  600.         Call vectorAddVectorND(v1, v1, body(B).position)
  601.         contacts(0) = v1
  602.     Else
  603.         If dot2 <= 0.0 Then
  604.             If vectorSqDist(center, v2) > ARadius * ARadius Then Exit Sub
  605.             m.contactCount = 1
  606.             Call vectorSubVectorND(n, v2, center)
  607.             Call matrixMultiplyVector(body(B).shape.u, v2, v2)
  608.             Call vectorAddVectorND(v2, v2, body(B).position)
  609.             contacts(0) = v2
  610.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  611.             Call vectorNormalize(n)
  612.             m.normal = n
  613.         Else
  614.             n = p(pa(B).start + faceNormal).norm
  615.             Call vectorSubVectorND(tv1, center, v1)
  616.             If vectorDot(tv1, n) > ARadius Then Exit Sub
  617.             m.contactCount = 1
  618.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  619.             Call vectorNeg(n)
  620.             m.normal = n
  621.             Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  622.             Call vectorAddVector(contacts(0), body(A).position)
  623.  
  624.         End If
  625.     End If
  626.  
  627. Function collisionPPClip (n As tVECTOR2d, c As _Float, face() As tVECTOR2d)
  628.     Dim sp As Integer: sp = 0
  629.     Dim o(cMAXNUMBEROFPOLYGONS) As tVECTOR2d
  630.  
  631.     o(0) = face(0)
  632.     o(1) = face(1)
  633.  
  634.     Dim d1 As _Float: d1 = vectorDot(n, face(0)) - c
  635.     Dim d2 As _Float: d2 = vectorDot(n, face(1)) - c
  636.  
  637.     If d1 <= 0.0 Then
  638.         o(sp) = face(0)
  639.         sp = sp + 1
  640.     End If
  641.  
  642.     If d2 <= 0.0 Then
  643.         o(sp) = face(1)
  644.         sp = sp + 1
  645.     End If
  646.  
  647.     If d1 * d2 < 0.0 Then
  648.         Dim alpha As _Float: alpha = d1 / (d1 - d2)
  649.         Dim tempv As tVECTOR2d
  650.         'out[sp] = face[0] + alpha * (face[1] - face[0]);
  651.         Call vectorSubVectorND(tempv, face(1), face(0))
  652.         Call vectorMultiplyScalar(tempv, alpha)
  653.         Call vectorAddVectorND(o(sp), tempv, face(0))
  654.         sp = sp + 1
  655.     End If
  656.     face(0) = o(0)
  657.     face(1) = o(1)
  658.     collisionPPClip = sp
  659.  
  660. Sub collisionPPFindIncidentFace (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, v() As tVECTOR2d, RefPoly As Integer, IncPoly As Integer, referenceIndex As Integer)
  661.     Dim referenceNormal As tVECTOR2d
  662.     Dim uRef As tMATRIX2d: uRef = b(RefPoly).shape.u
  663.     Dim uInc As tMATRIX2d: uInc = b(IncPoly).shape.u
  664.     Dim uTemp As tMATRIX2d
  665.     Dim i As Integer
  666.     referenceNormal = p(pa(RefPoly).start + referenceIndex).norm
  667.  
  668.     '        // Calculate normal in incident's frame of reference
  669.     '        // referenceNormal = RefPoly->u * referenceNormal; // To world space
  670.     Call matrixMultiplyVector(uRef, referenceNormal, referenceNormal)
  671.     '        // referenceNormal = IncPoly->u.Transpose( ) * referenceNormal; // To incident's model space
  672.     Call matrixTranspose(uInc, uTemp)
  673.     Call matrixMultiplyVector(uTemp, referenceNormal, referenceNormal)
  674.  
  675.     Dim incidentFace As Integer: incidentFace = 0
  676.     Dim minDot As _Float: minDot = 9999999
  677.     Dim dot As _Float
  678.     For i = 0 To pa(IncPoly).count
  679.         dot = vectorDot(referenceNormal, p(pa(IncPoly).start + i).norm)
  680.         If (dot < minDot) Then
  681.             minDot = dot
  682.             incidentFace = i
  683.         End If
  684.     Next
  685.  
  686.     '// Assign face vertices for incidentFace
  687.     '// v[0] = IncPoly->u * IncPoly->m_vertices[incidentFace] + IncPoly->body->position;
  688.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(0))
  689.     Call vectorAddVector(v(0), b(IncPoly).position)
  690.  
  691.     '// incidentFace = incidentFace + 1 >= (int32)IncPoly->m_vertexCount ? 0 : incidentFace + 1;
  692.     incidentFace = arrayNextIndex(incidentFace, pa(IncPoly).count)
  693.  
  694.     '// v[1] = IncPoly->u * IncPoly->m_vertices[incidentFace] +  IncPoly->body->position;
  695.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(1))
  696.     Call vectorAddVector(v(1), b(IncPoly).position)
  697.  
  698. Sub collisionPPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  699.     m.contactCount = 0
  700.  
  701.     Dim faceA(100) As Integer
  702.  
  703.     Dim penetrationA As _Float
  704.     penetrationA = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceA(), A, B)
  705.     If penetrationA >= 0.0 Then Exit Sub
  706.  
  707.     Dim faceB(100) As Integer
  708.  
  709.     Dim penetrationB As _Float
  710.     penetrationB = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceB(), B, A)
  711.     If penetrationB >= 0.0 Then Exit Sub
  712.  
  713.  
  714.     Dim referenceIndex As Integer
  715.     Dim flip As Integer
  716.  
  717.     Dim RefPoly As Integer
  718.     Dim IncPoly As Integer
  719.  
  720.     If impulseGT(penetrationA, penetrationB) Then
  721.         RefPoly = A
  722.         IncPoly = B
  723.         referenceIndex = faceA(0)
  724.         flip = 0
  725.     Else
  726.         RefPoly = B
  727.         IncPoly = A
  728.         referenceIndex = faceB(0)
  729.         flip = 1
  730.     End If
  731.  
  732.     Dim incidentFace(2) As tVECTOR2d
  733.  
  734.     Call collisionPPFindIncidentFace(p(), pa(), body(), incidentFace(), RefPoly, IncPoly, referenceIndex)
  735.     Dim v1 As tVECTOR2d
  736.     Dim v2 As tVECTOR2d
  737.     Dim v1t As tVECTOR2d
  738.     Dim v2t As tVECTOR2d
  739.  
  740.  
  741.     v1 = p(pa(RefPoly).start + referenceIndex).vert
  742.     referenceIndex = arrayNextIndex(referenceIndex, pa(RefPoly).count)
  743.     v2 = p(pa(RefPoly).start + referenceIndex).vert
  744.     '// Transform vertices to world space
  745.     '// v1 = RefPoly->u * v1 + RefPoly->body->position;
  746.     '// v2 = RefPoly->u * v2 + RefPoly->body->position;
  747.     Call matrixMultiplyVector(body(RefPoly).shape.u, v1, v1t)
  748.     Call vectorAddVectorND(v1, v1t, body(RefPoly).position)
  749.     Call matrixMultiplyVector(body(RefPoly).shape.u, v2, v2t)
  750.     Call vectorAddVectorND(v2, v2t, body(RefPoly).position)
  751.     ' Circle (v1.x, v1.y), 5, _RGB32(255, 255, 0)
  752.     'Circle (v2.x, v2.y), 5, _RGB32(255, 255, 0)
  753.  
  754.     '// Calculate reference face side normal in world space
  755.     '// Vec2 sidePlaneNormal = (v2 - v1);
  756.     '// sidePlaneNormal.Normalize( );
  757.     Dim sidePlaneNormal As tVECTOR2d
  758.     Call vectorSubVectorND(sidePlaneNormal, v2, v1)
  759.     Call vectorNormalize(sidePlaneNormal)
  760.  
  761.     '// Orthogonalize
  762.     '// Vec2 refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );
  763.     Dim refFaceNormal As tVECTOR2d
  764.     Call vectorSet(refFaceNormal, sidePlaneNormal.y, -sidePlaneNormal.x)
  765.  
  766.     '// ax + by = c
  767.     '// c is distance from origin
  768.     '// real refC = Dot( refFaceNormal, v1 );
  769.     '// real negSide = -Dot( sidePlaneNormal, v1 );
  770.     '// real posSide = Dot( sidePlaneNormal, v2 );
  771.     Dim refC As _Float: refC = vectorDot(refFaceNormal, v1)
  772.     Dim negSide As _Float: negSide = -vectorDot(sidePlaneNormal, v1)
  773.     Dim posSide As _Float: posSide = vectorDot(sidePlaneNormal, v2)
  774.  
  775.  
  776.     '// Clip incident face to reference face side planes
  777.     '// if(Clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
  778.     Dim negSidePlaneNormal As tVECTOR2d
  779.     Call vectorNegND(negSidePlaneNormal, sidePlaneNormal)
  780.  
  781.  
  782.     If collisionPPClip(negSidePlaneNormal, negSide, incidentFace()) < 2 Then Exit Sub
  783.  
  784.     If collisionPPClip(sidePlaneNormal, posSide, incidentFace()) < 2 Then Exit Sub
  785.  
  786.     Call vectorSet(m.normal, refFaceNormal.x, refFaceNormal.y)
  787.     If flip Then Call vectorNeg(m.normal)
  788.  
  789.     '// Keep points behind reference face
  790.     Dim cp As Integer: cp = 0 '// clipped points behind reference face
  791.     Dim separation As _Float
  792.     separation = vectorDot(refFaceNormal, incidentFace(0)) - refC
  793.     If separation <= 0.0 Then
  794.         contacts(cp) = incidentFace(0)
  795.         m.penetration = -separation
  796.         cp = cp + 1
  797.     Else
  798.         m.penetration = 0
  799.     End If
  800.  
  801.     separation = vectorDot(refFaceNormal, incidentFace(1)) - refC
  802.     If separation <= 0.0 Then
  803.         contacts(cp) = incidentFace(1)
  804.         m.penetration = m.penetration + -separation
  805.         cp = cp + 1
  806.         m.penetration = m.penetration / cp
  807.     End If
  808.     m.contactCount = cp
  809.  
  810. Function collisionPPFindAxisLeastPenetration (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, faceIndex() As Integer, A As Integer, B As Integer)
  811.     Dim bestDistance As _Float: bestDistance = -9999999
  812.     Dim bestIndex As Integer: bestIndex = 0
  813.  
  814.     Dim n As tVECTOR2d
  815.     Dim nw As tVECTOR2d
  816.     Dim buT As tMATRIX2d
  817.     Dim s As tVECTOR2d
  818.     Dim nn As tVECTOR2d
  819.     Dim v As tVECTOR2d
  820.     Dim tv As tVECTOR2d
  821.     Dim d As _Float
  822.     Dim i, k As Integer
  823.  
  824.     For i = 0 To pa(A).count
  825.         k = pa(A).start + i
  826.  
  827.         '// Retrieve a face normal from A
  828.         '// Vec2 n = A->m_normals[i];
  829.         '// Vec2 nw = A->u * n;
  830.         n = p(k).norm
  831.         Call matrixMultiplyVector(body(A).shape.u, n, nw)
  832.  
  833.  
  834.         '// Transform face normal into B's model space
  835.         '// Mat2 buT = B->u.Transpose( );
  836.         '// n = buT * nw;
  837.         Call matrixTranspose(body(B).shape.u, buT)
  838.         Call matrixMultiplyVector(buT, nw, n)
  839.  
  840.         '// Retrieve support point from B along -n
  841.         '// Vec2 s = B->GetSupport( -n );
  842.         Call vectorNegND(nn, n)
  843.         Call vectorGetSupport(p(), pa(), B, nn, s)
  844.  
  845.         '// Retrieve vertex on face from A, transform into
  846.         '// B's model space
  847.         '// Vec2 v = A->m_vertices[i];
  848.         '// v = A->u * v + A->body->position;
  849.         '// v -= B->body->position;
  850.         '// v = buT * v;
  851.  
  852.         v = p(k).vert
  853.         Call matrixMultiplyVector(body(A).shape.u, v, tv)
  854.         Call vectorAddVectorND(v, tv, body(A).position)
  855.  
  856.         Call vectorSubVector(v, body(B).position)
  857.         Call matrixMultiplyVector(buT, v, tv)
  858.  
  859.         Call vectorSubVector(s, tv)
  860.         d = vectorDot(n, s)
  861.  
  862.         If d > bestDistance Then
  863.             bestDistance = d
  864.             bestIndex = i
  865.         End If
  866.  
  867.     Next i
  868.  
  869.     faceIndex(0) = bestIndex
  870.  
  871.     collisionPPFindAxisLeastPenetration = bestDistance
  872.  
  873. '**********************************************************************************************
  874. '   Shape Creation Ahead
  875. '**********************************************************************************************
  876.  
  877. Sub shapeCreate (sh As tSHAPE, ty As Integer, radius As _Float)
  878.     Dim u As tMATRIX2d
  879.     Call matrixSetScalar(u, 1, 0, 0, 1)
  880.     sh.ty = ty
  881.     sh.radius = radius
  882.     sh.u = u
  883.  
  884. Sub bodyCreate (body() As tBODY, index As Integer, shape As tSHAPE)
  885.     Call vectorSet(body(index).position, 0, 0)
  886.     Call vectorSet(body(index).velocity, 0, 0)
  887.     body(index).angularVelocity = 0.0
  888.     body(index).torque = 0.0
  889.     body(index).orient = impulseRandom_float(-cPI, cPI)
  890.  
  891.     Call vectorSet(body(index).force, 0, 0)
  892.     body(index).staticFriction = 0.5
  893.     body(index).dynamicFriction = 0.3
  894.     body(index).restitution = 0.2
  895.     body(index).shape = shape
  896.  
  897. Sub boxCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  898.     Dim vertlength As Integer: vertlength = 3
  899.     Dim verts(vertlength) As tVECTOR2d
  900.  
  901.     Call vectorSet(verts(0), -sizex, -sizey)
  902.     Call vectorSet(verts(1), sizex, -sizey)
  903.     Call vectorSet(verts(2), sizex, sizey)
  904.     Call vectorSet(verts(3), -sizex, sizey)
  905.  
  906.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  907.  
  908. Sub vShapeCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  909.     Dim vertlength As Integer: vertlength = 7
  910.     Dim verts(vertlength) As tVECTOR2d
  911.  
  912.     Call vectorSet(verts(0), -sizex, -sizey)
  913.     Call vectorSet(verts(1), sizex, -sizey)
  914.     Call vectorSet(verts(2), sizex, sizey)
  915.     Call vectorSet(verts(3), -sizex, sizey)
  916.     Call vectorSet(verts(4), -sizex, sizey / 2)
  917.     Call vectorSet(verts(5), sizex / 2, sizey / 2)
  918.     Call vectorSet(verts(6), sizex / 2, -sizey / 2)
  919.     Call vectorSet(verts(7), -sizex, sizey / 2)
  920.  
  921.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  922.  
  923. Sub vertexSet (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, verts() As tVECTOR2d, vertLength As Integer)
  924.     Dim rightMost As Integer: rightMost = 0
  925.     Dim highestXCoord As _Float: highestXCoord = verts(0).x
  926.     Dim i As Integer
  927.     Dim x As _Float
  928.     For i = 1 To vertLength
  929.         x = verts(i).x
  930.         If x > highestXCoord Then
  931.             highestXCoord = x
  932.             rightMost = i
  933.         Else
  934.             If x = highestXCoord Then
  935.                 If verts(i).y < verts(rightMost).y Then
  936.                     rightMost = i
  937.                 End If
  938.             End If
  939.         End If
  940.     Next
  941.     Dim hull(cMAXNUMBEROFPOLYGONS) As Integer
  942.     Dim outCount As Integer: outCount = 0
  943.     Dim indexHull As Integer: indexHull = rightMost
  944.     Dim nextHullIndex As Integer
  945.     Dim e1 As tVECTOR2d
  946.     Dim e2 As tVECTOR2d
  947.     Dim c As _Float
  948.     Do
  949.         hull(outCount) = indexHull
  950.         nextHullIndex = 0
  951.         For i = 1 To vertLength
  952.             If nextHullIndex = indexHull Then
  953.                 nextHullIndex = i
  954.                 _Continue
  955.             End If
  956.             Call vectorSubVectorND(e1, verts(nextHullIndex), verts(hull(outCount)))
  957.             Call vectorSubVectorND(e2, verts(i), verts(hull(outCount)))
  958.             c = vectorCross(e1, e2)
  959.             If c < 0.0 Then nextHullIndex = i
  960.             If c = 0.0 And (vectorLengthSq(e2) > vectorLengthSq(e1)) Then
  961.                 nextHullIndex = i
  962.             End If
  963.         Next
  964.         outCount = outCount + 1
  965.         indexHull = nextHullIndex
  966.         If nextHullIndex = rightMost Then
  967.             pa(index).count = outCount - 1
  968.             Exit Do
  969.         End If
  970.     Loop
  971.  
  972.     If index = 0 Then
  973.         pa(index).start = 0
  974.     Else
  975.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  976.     End If
  977.  
  978.     For i = 0 To vertLength
  979.         p(pa(index).start + i).vert = verts(hull(i))
  980.     Next
  981.     Dim face As tVECTOR2d
  982.     For i = 0 To vertLength
  983.         Call vectorSubVectorND(face, p(pa(index).start + arrayNextIndex(i, pa(index).count)).vert, p(pa(index).start + i).vert)
  984.         Call vectorSet(p(pa(index).start + i).norm, face.y, -face.x)
  985.         Call vectorNormalize(p(pa(index).start + i).norm)
  986.     Next
  987.  
  988. Function arrayNextIndex (i As Integer, count As Integer)
  989.     arrayNextIndex = ((i + 1) Mod (count + 1))
  990.  
  991. Sub renderBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY)
  992.     Dim i As Integer
  993.     For i = 0 To sNUMBEROFBODIES
  994.         'TODO: Put hidden object logic
  995.         If body(i).shape.ty = cSHAPE_CIRCLE Then
  996.             Call renderCircle(body(), i)
  997.         Else If body(i).shape.ty = cSHAPE_POLYGON Then
  998.                 Call renderPoly(p(), pa(), body(), i)
  999.             End If
  1000.         End If
  1001.     Next
  1002.  
  1003. Sub renderPoly (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, index As Integer)
  1004.     Dim a As tVECTOR2d ' dummy vertices
  1005.     Dim b As tVECTOR2d
  1006.  
  1007.     Dim i, element, element_next As Integer
  1008.     For i = 0 To pa(index).count
  1009.         element = pa(index).start + i
  1010.         element_next = pa(index).start + arrayNextIndex(i, pa(index).count) ' wrap around back to first element
  1011.         a = p(element).vert
  1012.         b = p(element_next).vert
  1013.  
  1014.         Call matrixMultiplyVector(b(index).shape.u, a, a)
  1015.         Call matrixMultiplyVector(b(index).shape.u, b, b)
  1016.         Call vectorAddVector(a, b(index).position)
  1017.         Call vectorAddVector(b, b(index).position)
  1018.  
  1019.         Line (a.x, a.y)-(b.x, b.y), b(index).c
  1020.     Next
  1021.     If b(index).position.x < 0 Then b(index).position.x = _Width
  1022.     If b(index).position.x > _Width Then b(index).position.x = 0
  1023.     ' If b(index).position.y < 0 Then b(index).position.y = _Height
  1024.     If b(index).position.y > _Height Then b(index).position.y = 0
  1025.  
  1026. Sub renderCircle (b() As tBODY, index As Integer)
  1027.     Circle (b(index).position.x, b(index).position.y), b(index).shape.radius, b(index).c
  1028.     Line (b(index).position.x, b(index).position.y)-(b(index).position.x + Cos(b(index).orient) * b(index).shape.radius, b(index).position.y + Sin(b(index).orient) * b(index).shape.radius), b(index).c
  1029.     If b(index).position.x < 0 Then b(index).position.x = _Width
  1030.     If b(index).position.x > _Width Then b(index).position.x = 0
  1031.     ' If b(index).position.y < 0 Then b(index).position.y = _Height
  1032.     If b(index).position.y > _Height Then b(index).position.y = 0
  1033.  
  1034. Sub polygonSetOrient (b As tBODY, radians As _Float)
  1035.     Call matrixSetRadians(b.shape.u, radians)
  1036.  
  1037. '**********************************************************************************************
  1038. '   Object initialization Ahead
  1039. '**********************************************************************************************
  1040.  
  1041. Sub circleInitialize (b() As tBODY, index As Integer)
  1042.     Call circleComputeMass(b(), index, 1.0)
  1043.  
  1044. Sub circleComputeMass (b() As tBODY, index As Integer, density As _Float)
  1045.     b(index).mass = cPI * b(index).shape.radius * b(index).shape.radius * density
  1046.     If b(index).mass <> 0 Then
  1047.         b(index).invMass = 1.0 / b(index).mass
  1048.     Else
  1049.         b(index).invMass = 0.0
  1050.     End If
  1051.  
  1052.     b(index).inertia = b(index).mass * b(index).shape.radius * b(index).shape.radius
  1053.  
  1054.     If b(index).inertia <> 0 Then
  1055.         b(index).invInertia = 1.0 / b(index).inertia
  1056.     Else
  1057.         b(index).invInertia = 0.0
  1058.     End If
  1059.  
  1060. Sub polygonInitialize (body() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer)
  1061.     Call polygonComputeMass(body(), p(), pa(), index, 1.0)
  1062.  
  1063. Sub polygonComputeMass (b() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, density As _Float)
  1064.     Dim c As tVECTOR2d ' centroid
  1065.     Dim p1 As tVECTOR2d
  1066.     Dim p2 As tVECTOR2d
  1067.     Dim area As _Float
  1068.     Dim I As _Float
  1069.     Dim k_inv3 As _Float
  1070.     Dim D As _Float
  1071.     Dim triangleArea As _Float
  1072.     Dim weight As _Float
  1073.     Dim intx2 As _Float
  1074.     Dim inty2 As _Float
  1075.     Dim ii As Integer
  1076.  
  1077.     k_inv3 = 1.0 / 3.0
  1078.  
  1079.     For ii = 0 To pa(index).count
  1080.         p1 = p(pa(index).start + ii).vert
  1081.         p2 = p(pa(index).start + arrayNextIndex(ii, pa(index).count)).vert
  1082.         D = vectorCross(p1, p2)
  1083.         triangleArea = .5 * D
  1084.         area = area + triangleArea
  1085.         weight = triangleArea * k_inv3
  1086.         Call vectorAddVectorScalar(c, p1, weight)
  1087.         Call vectorAddVectorScalar(c, p2, weight)
  1088.         intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x
  1089.         inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y
  1090.         I = I + (0.25 * k_inv3 * D) * (intx2 + inty2)
  1091.     Next ii
  1092.  
  1093.     Call vectorMultiplyScalar(c, 1.0 / area)
  1094.  
  1095.     For ii = 0 To pa(index).count
  1096.         Call vectorSubVector(p(pa(index).start + ii).vert, c)
  1097.     Next
  1098.  
  1099.     b(index).mass = density * area
  1100.     If b(index).mass <> 0.0 Then
  1101.         b(index).invMass = 1.0 / b(index).mass
  1102.     Else
  1103.         b(index).invMass = 0.0
  1104.     End If
  1105.  
  1106.     b(index).inertia = I * density
  1107.     If b(index).inertia <> 0 Then
  1108.         b(index).invInertia = 1.0 / b(index).inertia
  1109.     Else
  1110.         b(index).invInertia = 0.0
  1111.     End If
  1112.  
  1113. '**********************************************************************************************
  1114. '   Vector Math Ahead
  1115. '**********************************************************************************************
  1116.  
  1117. Sub vectorSet (v As tVECTOR2d, x As _Float, y As _Float)
  1118.     v.x = x
  1119.     v.y = y
  1120.  
  1121. Sub vectorSetVector (o As tVECTOR2d, v As tVECTOR2d)
  1122.     o.x = v.x
  1123.     o.y = v.y
  1124.  
  1125. Sub vectorNeg (v As tVECTOR2d)
  1126.     v.x = -v.x
  1127.     v.y = -v.y
  1128.  
  1129. Sub vectorNegND (o As tVECTOR2d, v As tVECTOR2d)
  1130.     o.x = -v.x
  1131.     o.y = -v.y
  1132.  
  1133. Sub vectorMultiplyScalar (v As tVECTOR2d, s As _Float)
  1134.     v.x = v.x * s
  1135.     v.y = v.y * s
  1136.  
  1137. Sub vectorMultiplyScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1138.     o.x = v.x * s
  1139.     o.y = v.y * s
  1140.  
  1141. Sub vectorDivideScalar (v As tVECTOR2d, s As _Float)
  1142.     v.x = v.x / s
  1143.     v.y = v.y / s
  1144.  
  1145. Sub vectorDivideScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1146.     o.x = v.x / s
  1147.     o.y = v.y / s
  1148.  
  1149. Sub vectorAddScalar (v As tVECTOR2d, s As _Float)
  1150.     v.x = v.x + s
  1151.     v.y = v.y + s
  1152.  
  1153. Sub vectorAddScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1154.     o.x = v.x + s
  1155.     o.y = v.y + s
  1156.  
  1157. Sub vectorMultiplyVector (v As tVECTOR2d, m As tVECTOR2d)
  1158.     v.x = v.x * m.x
  1159.     v.y = v.y * m.y
  1160.  
  1161. Sub vectorMultiplyVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1162.     o.x = v.x * m.x
  1163.     o.y = v.y * m.y
  1164.  
  1165. Sub vectorDivideVector (v As tVECTOR2d, m As tVECTOR2d)
  1166.     v.x = v.x / m.x
  1167.     v.y = v.y / m.y
  1168.  
  1169. Sub vectorAddVector (v As tVECTOR2d, m As tVECTOR2d)
  1170.     v.x = v.x + m.x
  1171.     v.y = v.y + m.y
  1172.  
  1173. Sub vectorAddVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1174.     o.x = v.x + m.x
  1175.     o.y = v.y + m.y
  1176.  
  1177. Sub vectorAddVectorScalar (v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1178.     v.x = v.x + m.x * s
  1179.     v.y = v.y + m.y * s
  1180.  
  1181. Sub vectorAddVectorScalarND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1182.     o.x = v.x + m.x * s
  1183.     o.y = v.y + m.y * s
  1184.  
  1185. Sub vectorSubVector (v As tVECTOR2d, m As tVECTOR2d)
  1186.     v.x = v.x - m.x
  1187.     v.y = v.y - m.y
  1188.  
  1189. Sub vectorSubVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1190.     o.x = v.x - m.x
  1191.     o.y = v.y - m.y
  1192.  
  1193. Function vectorLengthSq (v As tVECTOR2d)
  1194.     vectorLengthSq = v.x * v.x + v.y * v.y
  1195.  
  1196. Function vectorLength (v As tVECTOR2d)
  1197.     vectorLength = Sqr(vectorLengthSq(v))
  1198.  
  1199. Sub vectorRotate (v As tVECTOR2d, radians As _Float)
  1200.     Dim c, s, xp, yp As _Float
  1201.     c = Cos(radians)
  1202.     s = Sin(radians)
  1203.     xp = v.x * c - v.y * s
  1204.     yp = v.x * s + v.y * c
  1205.     v.x = xp
  1206.     v.y = yp
  1207.  
  1208. Sub vectorNormalize (v As tVECTOR2d)
  1209.     Dim lenSQ, invLen As _Float
  1210.     lenSQ = vectorLengthSq(v)
  1211.     If lenSQ > cEPSILON_SQ Then
  1212.         invLen = 1.0 / Sqr(lenSQ)
  1213.         v.x = v.x * invLen
  1214.         v.y = v.y * invLen
  1215.     End If
  1216.  
  1217. Sub vectorMin (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1218.     o.x = scalarMin(a.x, b.x)
  1219.     o.y = scalarMin(a.y, b.y)
  1220.  
  1221. Sub vectorMax (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1222.     o.x = scalarMax(a.x, b.x)
  1223.     o.y = scalarMax(a.y, b.y)
  1224.  
  1225. Function vectorDot (a As tVECTOR2d, b As tVECTOR2d)
  1226.     vectorDot = a.x * b.x + a.y * b.y
  1227.  
  1228. Function vectorSqDist (a As tVECTOR2d, b As tVECTOR2d)
  1229.     Dim dx, dy As _Float
  1230.     dx = b.x - a.x
  1231.     dy = b.y - a.y
  1232.     vectorSqDist = dx * dx + dy * dy
  1233.  
  1234. Function vectorDistance (a As tVECTOR2d, b As tVECTOR2d)
  1235.     vectorDistance = Sqr(vectorSqDist(a, b))
  1236.  
  1237. Function vectorCross (a As tVECTOR2d, b As tVECTOR2d)
  1238.     vectorCross = a.x * b.y - a.y * b.x
  1239.  
  1240. Sub vectorCrossScalar (o As tVECTOR2d, v As tVECTOR2d, a As _Float)
  1241.     o.x = v.y * -a
  1242.     o.y = v.x * a
  1243.  
  1244. Function vectorArea (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1245.     vectorArea = (((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)))
  1246.  
  1247. Function vectorLeft (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1248.     vectorLeft = vectorArea(a, b, c) > 0
  1249.  
  1250. Function vectorLeftOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1251.     vectorLeftOn = vectorArea(a, b, c) >= 0
  1252.  
  1253. Function vectorRight (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1254.     vectorRight = vectorArea(a, b, c) < 0
  1255.  
  1256. Function vectorRightOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1257.     vectorRightOn = vectorArea(a, b, c) <= 0
  1258.  
  1259. Function vectorCollinear (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d, thresholdAngle As _Float)
  1260.     If (thresholdAngle = 0) Then
  1261.         vectorCollinear = (vectorArea(a, b, c) = 0)
  1262.     Else
  1263.         Dim ab As tVECTOR2d
  1264.         Dim bc As tVECTOR2d
  1265.         Dim dot As _Float
  1266.         Dim magA As _Float
  1267.         Dim magB As _Float
  1268.         Dim angle As _Float
  1269.  
  1270.         ab.x = b.x - a.x
  1271.         ab.y = b.y - a.y
  1272.         bc.x = c.x - b.x
  1273.         bc.y = c.y - b.y
  1274.  
  1275.         dot = ab.x * bc.x + ab.y * bc.y
  1276.         magA = Sqr(ab.x * ab.x + ab.y * ab.y)
  1277.         magB = Sqr(bc.x * bc.x + bc.y * bc.y)
  1278.         angle = _Acos(dot / (magA * magB))
  1279.         vectorCollinear = angle < thresholdAngle
  1280.     End If
  1281.  
  1282. Sub vectorGetSupport (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, dir As tVECTOR2d, bestVertex As tVECTOR2d)
  1283.     Dim bestProjection As _Float
  1284.     Dim v As tVECTOR2d
  1285.     Dim projection As _Float
  1286.     Dim i As Integer
  1287.     bestVertex.x = -9999999
  1288.     bestVertex.y = -9999999
  1289.     bestProjection = -9999999
  1290.  
  1291.     For i = 0 To pa(index).count
  1292.         v = p(i + pa(index).start).vert
  1293.         projection = vectorDot(v, dir)
  1294.         If projection > bestProjection Then
  1295.             bestVertex = v
  1296.             bestProjection = projection
  1297.         End If
  1298.     Next
  1299.  
  1300. '**********************************************************************************************
  1301. '   Matrix Stuff Ahead
  1302. '**********************************************************************************************
  1303.  
  1304. Sub matrixSetRadians (m As tMATRIX2d, radians As _Float)
  1305.     Dim c As _Float
  1306.     Dim s As _Float
  1307.     c = Cos(radians)
  1308.     s = Sin(radians)
  1309.     m.m00 = c
  1310.     m.m01 = -s
  1311.     m.m10 = s
  1312.     m.m11 = c
  1313.  
  1314. Sub matrixSetScalar (m As tMATRIX2d, a As _Float, b As _Float, c As _Float, d As _Float)
  1315.     m.m00 = a
  1316.     m.m01 = b
  1317.     m.m10 = c
  1318.     m.m11 = d
  1319.  
  1320. Sub matrixAbs (m As tMATRIX2d, o As tMATRIX2d)
  1321.     o.m00 = Abs(m.m00)
  1322.     o.m01 = Abs(m.m01)
  1323.     o.m10 = Abs(m.m10)
  1324.     o.m11 = Abs(m.m11)
  1325.  
  1326. Sub matrixGetAxisX (m As tMATRIX2d, o As tVECTOR2d)
  1327.     o.x = m.m00
  1328.     o.y = m.m10
  1329.  
  1330. Sub matrixGetAxisY (m As tMATRIX2d, o As tVECTOR2d)
  1331.     o.x = m.m01
  1332.     o.y = m.m11
  1333.  
  1334. Sub matrixTransposeI (m As tMATRIX2d)
  1335.     Swap m.m01, m.m10
  1336.  
  1337. Sub matrixTranspose (m As tMATRIX2d, o As tMATRIX2d)
  1338.     o.m00 = m.m00
  1339.     o.m01 = m.m10
  1340.     o.m10 = m.m01
  1341.     o.m11 = m.m11
  1342.  
  1343. Sub matrixMultiplyVector (m As tMATRIX2d, v As tVECTOR2d, o As tVECTOR2d)
  1344.     Dim t As tVECTOR2d
  1345.     t.x = m.m00 * v.x + m.m01 * v.y
  1346.     t.y = m.m10 * v.x + m.m11 * v.y
  1347.     o = t
  1348.  
  1349. Sub matrixMultiplyMatrix (m As tMATRIX2d, x As tMATRIX2d, o As tMATRIX2d)
  1350.     o.m00 = m.m00 * x.m00 + m.m01 * x.m10
  1351.     o.m01 = m.m00 * x.m01 + m.m01 * x.m11
  1352.     o.m10 = m.m10 * x.m00 + m.m11 * x.m10
  1353.     o.m11 = m.m10 * x.m01 + m.m11 * x.m11
  1354.  
  1355. '**********************************************************************************************
  1356. '   Mostly Unused Stuff Ahead
  1357. '**********************************************************************************************
  1358.  
  1359. Sub polygonMakeCCW (obj As tTRIANGLE)
  1360.     If vectorLeft(obj.a, obj.b, obj.c) = 0 Then
  1361.         Swap obj.a, obj.c
  1362.     End If
  1363.  
  1364. Function polygonIsReflex (t As tTRIANGLE)
  1365.     polygonIsReflex = vectorRight(t.a, t.b, t.c)
  1366.  
  1367. Function scalarMin (a As _Float, b As _Float)
  1368.     If a < b Then
  1369.         scalarMin = a
  1370.     Else
  1371.         scalarMin = b
  1372.     End If
  1373.  
  1374. Function scalarMax (a As _Float, b As _Float)
  1375.     If a > b Then
  1376.         scalarMax = a
  1377.     Else
  1378.         scalarMax = b
  1379.     End If
  1380.  
  1381. Sub lineIntersection (l1 As tLINE2d, l2 As tLINE2d, o As tVECTOR2d)
  1382.     Dim a1, b1, c1, a2, b2, c2, det As _Float
  1383.     o.x = 0
  1384.     o.y = 0
  1385.     a1 = l1.b.y - l1.a.y
  1386.     b1 = l1.a.x - l1.b.x
  1387.     c1 = a1 * l1.a.x + b1 * l1.a.y
  1388.     a2 = l2.b.y - l2.a.y
  1389.     b2 = l2.a.x - l2.b.x
  1390.     c2 = a2 * l2.a.x + b2 * l2.a.y
  1391.     det = a1 * b2 - a2 * b1
  1392.  
  1393.     If Int(det * cPRECISION) <> 0 Then
  1394.         o.x = (b2 * c1 - b1 * c2) / det
  1395.         o.y = (a1 * c2 - a2 * c1) / det
  1396.     End If
  1397.  
  1398. Function lineSegmentsIntersect (l1 As tLINE2d, l2 As tLINE2d)
  1399.     Dim dx, dy, da, db, s, t As _Float
  1400.     dx = l1.b.x - l1.a.x
  1401.     dy = l1.b.y - l1.a.y
  1402.     da = l2.b.x - l2.a.x
  1403.     db = l2.b.y - l2.a.y
  1404.     If da * dy - db * dx = 0 Then
  1405.         lineSegmentsIntersect = 0
  1406.     Else
  1407.         s = (dx * (l2.a.y - l1.a.y) + dy * (l1.a.x - l2.a.x)) / (da * dy - db * dx)
  1408.         t = (da * (l1.a.y - l2.a.y) + db * (l2.a.x - l1.a.x)) / (db * dx - da * dy)
  1409.         lineSegmentsIntersect = (s >= 0 And s <= 1 And t >= 0 And t <= 1)
  1410.     End If
  1411.  
  1412. '**********************************************************************************************
  1413. '   Impulse Specific Math Ahead
  1414. '**********************************************************************************************
  1415.  
  1416. Function impulseEqual (a As _Float, b As _Float)
  1417.     impulseEqual = Abs(a - b) <= cEPSILON
  1418.  
  1419. Function impulseClamp (min As _Float, max As _Float, a As _Float)
  1420.     If a < min Then
  1421.         impulseClamp = min
  1422.     Else If a > max Then
  1423.             impulseClamp = max
  1424.         Else
  1425.             impulseClamp = a
  1426.         End If
  1427.     End If
  1428.  
  1429. Function impulseRound (a As _Float)
  1430.     impulseRound = Int(a + 0.5)
  1431.  
  1432. Function impulseRandom_float (min As _Float, max As _Float)
  1433.     impulseRandom_float = ((max - min) * Rnd + min)
  1434.  
  1435. Function impulseRandomInteger (min As Integer, max As Integer)
  1436.     impulseRandomInteger = Int((max - min) * Rnd + min)
  1437.  
  1438. Function impulseGT (a As _Float, b As _Float)
  1439.     impulseGT = (a >= b * cBIAS_RELATIVE + a * cBIAS_ABSOLUTE)
  1440.  
  1441. '**********************************************************************************************
  1442. '   Troubleshooting Tools
  1443. '**********************************************************************************************
  1444.  
  1445.  
  1446. Sub printMatrix (u As tMATRIX2d, n As Integer)
  1447.     Print "---------------------------"
  1448.     Print n; " u:"; u.m00; "|"; u.m10
  1449.     Print "       "; u.m10; "|"; u.m11
  1450.  
  1451.  

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D Physics Engine
« Reply #1 on: April 21, 2021, 03:51:25 am »
looking at code
« Last Edit: April 21, 2021, 03:53:29 am by NOVARSEG »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D Physics Engine
« Reply #2 on: April 21, 2021, 01:48:15 pm »
Well it is nice demo!

Welcome @justsomeguy

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D Physics Engine
« Reply #3 on: April 21, 2021, 01:58:08 pm »
Thank you for the feedback!

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: 2D Physics Engine
« Reply #4 on: April 21, 2021, 02:34:48 pm »
That's pretty neat - maybe a new engine for a lottery machine. Seems to be a possible recursive pattern to the moving objects but getting hypnotized trying to see it.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D Physics Engine
« Reply #5 on: April 21, 2021, 04:29:15 pm »
Hello

I have added textures to the objects and and added a FPS counter. I would be interested in what kind of performance that everyone is getting. I will appreciate any feedback good or bad.

Thanks

Code: QB64: [Select]
  1. '/*
  2. '    Copyright (c) 2013 Randy Gaul http://RandyGaul.net
  3.  
  4. '    This software is provided 'as-is', without any express or implied
  5. '    warranty. In no event will the authors be held liable for any damages
  6. '    arising from the use of this software.
  7.  
  8. '    Permission is granted to anyone to use this software for any purpose,
  9. '    including commercial applications, and to alter it and redistribute it
  10. '    freely, subject to the following restrictions:
  11. '      1. The origin of this software must not be misrepresented; you must not
  12. '         claim that you wrote the original software. If you use this software
  13. '         in a product, an acknowledgment in the product documentation would be
  14. '         appreciated but is not required.
  15. '      2. Altered source versions must be plainly marked as such, and must not be
  16. '         misrepresented as being the original software.
  17. '      3. This notice may not be removed or altered from any source distribution.
  18.  
  19. '    Port to Java by Philip Diffenderfer http://magnos.org
  20. '    Port to QB64 by justsomeguy
  21. '*/
  22. '
  23.  
  24. '**********************************************************************************************
  25. '   Setup Types and Variables
  26. '**********************************************************************************************
  27. Type tVECTOR2d
  28.     x As _Float
  29.     y As _Float
  30.  
  31. Type tLINE2d ' Not used
  32.     a As tVECTOR2d
  33.     b As tVECTOR2d
  34.  
  35. Type tFACE2d ' Not used
  36.     f0 As tVECTOR2d
  37.     f1 As tVECTOR2d
  38.  
  39. Type tTRIANGLE ' Not used
  40.     a As tVECTOR2d
  41.     b As tVECTOR2d
  42.     c As tVECTOR2d
  43.  
  44. Type tMATRIX2d
  45.     m00 As _Float
  46.     m01 As _Float
  47.     m10 As _Float
  48.     m11 As _Float
  49.  
  50. Type tSHAPE
  51.     ty As Integer ' cSHAPE_CIRCLE = 1, cSHAPE_POLYGON = 2
  52.     radius As _Float ' Only necessary for circle shapes
  53.     u As tMATRIX2d ' Only neceassary for polygons
  54.     texture As Long
  55.  
  56. Type tPOLY 'list of vertices for all objects in simulation
  57.     vert As tVECTOR2d
  58.     norm As tVECTOR2d
  59.  
  60. Type tPOLYATTRIB 'keep track of polys in monlithic list of vertices
  61.     start As Integer ' starting vertex of the polygon
  62.     count As Integer ' number of vertices in polygon
  63.  
  64. Type tBODY
  65.     position As tVECTOR2d
  66.     velocity As tVECTOR2d
  67.     force As tVECTOR2d
  68.     angularVelocity As _Float
  69.     torque As _Float
  70.     orient As _Float
  71.     mass As _Float
  72.     invMass As _Float
  73.     inertia As _Float
  74.     invInertia As _Float
  75.     staticFriction As _Float
  76.     dynamicFriction As _Float
  77.     restitution As _Float
  78.     shape As tSHAPE
  79.     c As Long ' color
  80.     visible As Integer 'Hide a body ;)
  81.  
  82. Type tMANIFOLD
  83.     A As Integer
  84.     B As Integer
  85.     penetration As _Float
  86.     normal As tVECTOR2d
  87.     contactCount As Integer
  88.     e As _Float
  89.     df As _Float
  90.     sf As _Float
  91.  
  92. Const cSHAPE_CIRCLE = 1
  93. Const cSHAPE_POLYGON = 2
  94. Const cPRECISION = 100
  95. Const cMAXNUMOFTRIANGLES = 100
  96. Const cMAXNUMBEROFOBJECTS = 1000 ' Max number of objects at one time
  97. Const cMAXNUMBEROFPOLYGONS = 10000 ' Max number of total vertices included in all objects
  98. Const cPI = 3.14159
  99. Const cEPSILON = 0.0001
  100. Const cEPSILON_SQ = cEPSILON * cEPSILON
  101. Const cBIAS_RELATIVE = 0.95
  102. Const cBIAS_ABSOLUTE = 0.01
  103. Const cDT = 1.0 / 60.0
  104. Const cITERATIONS = 10
  105. Const cPENETRATION_ALLOWANCE = 0.05
  106. Const cPENETRATION_CORRECTION = 0.4 ' misspelled in original code
  107. Const cPARAMETER_POSITION = 1
  108. Const cPARAMETER_VELOCITY = 2
  109. Const cPARAMETER_FORCE = 3
  110. Const cPARAMETER_ANGULARVELOCITY = 4
  111. Const cPARAMETER_TORQUE = 5
  112. Const cPARAMETER_ORIENT = 6
  113. Const cPARAMETER_STATICFRICTION = 7
  114. Const cPARAMETER_DYNAMICFRICTION = 8
  115. Const cPARAMETER_COLOR = 9
  116. Const cPARAMETER_VISIBLE = 10
  117. Const cPARAMETER_STATIC = 11
  118. Const cPARAMETER_TEXTURE = 12
  119.  
  120. Dim Shared sGRAVITY As tVECTOR2d: Call vectorSet(sGRAVITY, 0.0, 100.0)
  121. Dim Shared sRESTING As _Float: Dim o As tVECTOR2d: Call vectorMultiplyScalarND(o, sGRAVITY, cDT): sRESTING = vectorLengthSq(o) + cEPSILON
  122. Dim Shared sNUMBEROFBODIES As Integer: sNUMBEROFBODIES = 100 ' 0 is included - Don't go under 10 for the current scene
  123.  
  124. Dim poly(cMAXNUMBEROFPOLYGONS) As tPOLY
  125. Dim polyattributes(cMAXNUMBEROFPOLYGONS) As tPOLYATTRIB
  126. Dim body(cMAXNUMBEROFOBJECTS) As tBODY
  127.  
  128. Const cSCENE_USER = 0
  129. Const cSCENE_FLOOR = 1
  130. Const cSCENE_SPINNEROFDEATH = 2
  131. Const cSCENE_RIGHTRAMP = 3
  132. Const cSCENE_LEFTRAMP = 4
  133. Const cSCENE_DOZEROFDOOM = 5
  134. Const cSCENE_ELEVATOROFTERROR = 6
  135. Const cSCENE_RIGHTWALL = 7
  136. Const cSCENE_ELEVATORKICKER = 8
  137.  
  138. '**********************************************************************************************
  139. _Title "phyNGN"
  140. Screen _NewImage(1024, 768, 32)
  141. 'Randomize Timer
  142. Call buildSimpleScene(poly(), polyattributes(), body())
  143. '**********************************************************************************************
  144. Do: fps = Timer(.001)
  145.     Cls , _RGB32(28, 28, 22)
  146.     Call animateScene(body())
  147.     Call impulseStep(poly(), polyattributes(), body(), cDT, cITERATIONS)
  148.     Call renderBodies(poly(), polyattributes(), body())
  149.     Locate 1, 60: Print "FPS:"; Int(1 / (Timer(.001) - fps))
  150.     _Display
  151.     _Limit 240
  152. '**********************************************************************************************
  153. '   End of Main loop
  154. '**********************************************************************************************
  155.  
  156. '**********************************************************************************************
  157. '   Scene Creation and Handling Ahead
  158. '**********************************************************************************************
  159.  
  160. Sub animateScene (body() As tBODY)
  161.     If _MouseInput Then ' Terrible user interaction
  162.         Call vectorSet(body(cSCENE_USER).velocity, _MouseMovementX * 100, _MouseMovementY * 100)
  163.     End If
  164.  
  165.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_SPINNEROFDEATH, body(cSCENE_SPINNEROFDEATH).orient + (cPI / 90), 0)
  166.     If body(cSCENE_SPINNEROFDEATH).orient > 2 * cPI Then body(cSCENE_SPINNEROFDEATH).orient = 0
  167.  
  168.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_DOZEROFDOOM, body(cSCENE_DOZEROFDOOM).position.x - 1, _Height - 75)
  169.     If body(cSCENE_DOZEROFDOOM).position.x < 120 Then body(cSCENE_DOZEROFDOOM).position.x = _Width
  170.  
  171.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATOROFTERROR, 55, body(cSCENE_ELEVATOROFTERROR).position.y - 1)
  172.     If body(cSCENE_ELEVATOROFTERROR).position.y < 50 Then body(cSCENE_ELEVATOROFTERROR).position.y = _Height - 25
  173.  
  174. Sub buildSimpleScene (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY)
  175.     Dim i, ty As Integer
  176.     Dim bm(5) As Long
  177.     Call generateBitmap(bm())
  178.  
  179.     Call createBoxBodies(p(), pa(), body(), cSCENE_USER, 10, 10)
  180.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_USER, (_Width / 2), (_Height / 2))
  181.     Call setBody(body(), cPARAMETER_VELOCITY, cSCENE_USER, 0, 0)
  182.  
  183.     Call createBoxBodies(p(), pa(), body(), cSCENE_FLOOR, 1000, 5)
  184.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_FLOOR, (_Width / 2), (_Height - 20))
  185.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_FLOOR, 0, 0)
  186.  
  187.     Call createBoxBodies(p(), pa(), body(), cSCENE_SPINNEROFDEATH, 75, 5)
  188.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_SPINNEROFDEATH, (_Width / 2), (_Height - 100))
  189.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_SPINNEROFDEATH, 0, 0)
  190.  
  191.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTRAMP, 350, 5)
  192.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_RIGHTRAMP, (_Width * .75) + 35, (_Height / 2) + 105)
  193.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_RIGHTRAMP, (cPI * .75), 0)
  194.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_RIGHTRAMP, 0, 0)
  195.  
  196.     Call createBoxBodies(p(), pa(), body(), cSCENE_LEFTRAMP, 225, 5)
  197.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_LEFTRAMP, (_Width * .25) + 55, (_Height / 2))
  198.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_LEFTRAMP, (cPI * .1), 0)
  199.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_LEFTRAMP, 0, 0)
  200.  
  201.     Call createBoxBodies(p(), pa(), body(), cSCENE_DOZEROFDOOM, 5, 50)
  202.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_DOZEROFDOOM, 0, _Height - 75)
  203.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_DOZEROFDOOM, 0, 0)
  204.  
  205.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATOROFTERROR, 50, 5)
  206.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATOROFTERROR, 55, _Height - 25)
  207.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_ELEVATOROFTERROR, (cPI * .98), 0)
  208.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_ELEVATOROFTERROR, 0, 0)
  209.  
  210.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTWALL, 5, 375)
  211.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_RIGHTWALL, 5, _Height / 2 - 20)
  212.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_RIGHTWALL, 0, 0)
  213.  
  214.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATORKICKER, 100, 5)
  215.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATORKICKER, 55, 120)
  216.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_ELEVATORKICKER, (cPI * .70), 0)
  217.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_ELEVATORKICKER, 0, 0)
  218.  
  219.     For i = cSCENE_ELEVATORKICKER + 1 To sNUMBEROFBODIES
  220.         If Rnd > .25 Then ' Circles are must faster than polygons
  221.             ty = cSHAPE_CIRCLE
  222.         Else
  223.             ty = cSHAPE_POLYGON
  224.         End If
  225.         Call createBodies(p(), pa(), body(), i, ty)
  226.         Call setBody(body(), cPARAMETER_POSITION, i, (Rnd * _Width / 2) + (_Width / 4), ((Rnd * _Height / 2) + (_Height / 4)) - 200)
  227.         Call setBody(body(), cPARAMETER_VELOCITY, i, Rnd * 100 - 50, Rnd * 100 - 50)
  228.     Next
  229.     For i = cSCENE_ELEVATORKICKER + 1 To sNUMBEROFBODIES
  230.         If body(i).shape.ty = cSHAPE_CIRCLE Then
  231.             Call setBody(body(), cPARAMETER_TEXTURE, i, bm(0), 0)
  232.         Else
  233.             Call setBody(body(), cPARAMETER_TEXTURE, i, bm(1), 0)
  234.         End If
  235.     Next
  236.  
  237. Sub setBody (body() As tBODY, Parameter As Integer, Index As Integer, arg1 As _Float, arg2 As _Float)
  238.     Select Case Parameter
  239.         Case cPARAMETER_POSITION:
  240.             Call vectorSet(body(Index).position, arg1, arg2)
  241.         Case cPARAMETER_VELOCITY:
  242.             Call vectorSet(body(Index).velocity, arg1, arg2)
  243.         Case cPARAMETER_FORCE:
  244.             Call vectorSet(body(Index).force, arg1, arg2)
  245.         Case cPARAMETER_ANGULARVELOCITY:
  246.             body(Index).angularVelocity = arg1
  247.         Case cPARAMETER_TORQUE:
  248.             body(Index).torque = arg1
  249.         Case cPARAMETER_ORIENT:
  250.             body(Index).orient = arg1
  251.             Call matrixSetRadians(body(Index).shape.u, body(Index).orient)
  252.         Case cPARAMETER_STATICFRICTION:
  253.             body(Index).staticFriction = arg1
  254.         Case cPARAMETER_DYNAMICFRICTION:
  255.             body(Index).dynamicFriction = arg1
  256.         Case cPARAMETER_COLOR:
  257.             body(Index).c = arg1
  258.         Case cPARAMETER_VISIBLE:
  259.             body(Index).visible = arg1
  260.         Case cPARAMETER_STATIC:
  261.             Call bodySetStatic(body(Index))
  262.         Case cPARAMETER_TEXTURE:
  263.             body(Index).shape.texture = arg1
  264.     End Select
  265.  
  266. Sub createCircleBody (pa() As tPOLYATTRIB, body() As tBODY, index As Integer, radius As _Float)
  267.     Dim shape As tSHAPE
  268.     Call shapeCreate(shape, cSHAPE_CIRCLE, radius)
  269.     Call bodyCreate(body(), index, shape)
  270.     Call circleInitialize(body(), index)
  271.     ' Even though circles do not have Polygons, they still must be included in the Vertices
  272.     If index = 0 Then
  273.         pa(index).start = 0
  274.     Else
  275.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  276.     End If
  277.     pa(index).count = 1
  278.     body(index).c = _RGB32(255, 255, 255)
  279.  
  280. Sub createBoxBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, xs As _Float, ys As _Float)
  281.     Dim shape As tSHAPE
  282.     Call shapeCreate(shape, cSHAPE_POLYGON, 0)
  283.     Call bodyCreate(body(), index, shape)
  284.     Call boxCreate(p(), pa(), index, xs, ys)
  285.     Call polygonInitialize(body(), p(), pa(), index)
  286.     body(index).c = _RGB32(255, 255, 255)
  287.  
  288. Sub createBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, ty As Integer)
  289.     If ty = cSHAPE_CIRCLE Then
  290.         Call createCircleBody(pa(), body(), index, 10 + Rnd * 20)
  291.     Else
  292.         If ty = cSHAPE_POLYGON Then
  293.             Call createBoxBodies(p(), pa(), body(), index, 10 + Rnd * 10, 10 + Rnd * 10)
  294.         End If
  295.     End If
  296.     body(index).c = _RGB32(55 + Rnd * 200, 55 + Rnd * 200, 55 + Rnd * 200)
  297.  
  298. '**********************************************************************************************
  299. '   Physics and Collision Stuff Ahead
  300. '**********************************************************************************************
  301.  
  302. Sub impulseIntegrateForces (b As tBODY, dt As _Float)
  303.     If b.invMass = 0.0 Then Exit Sub
  304.     Dim dts As _Float
  305.     dts = dt * .5
  306.     Call vectorAddVectorScalar(b.velocity, b.force, b.invMass * dts)
  307.     Call vectorAddVectorScalar(b.velocity, sGRAVITY, dts)
  308.     b.angularVelocity = b.angularVelocity + (b.torque * b.invInertia * dts)
  309.  
  310. Sub impulseIntegrateVelocity (body As tBODY, dt As _Float)
  311.     If body.invMass = 0.0 Then Exit Sub
  312.     Call vectorAddVectorScalar(body.position, body.velocity, dt)
  313.     body.orient = body.orient + (body.angularVelocity * dt)
  314.     Call matrixSetRadians(body.shape.u, body.orient)
  315.     Call impulseIntegrateForces(body, dt)
  316.  
  317. Sub impulseStep (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, dt As _Float, iterations As Integer)
  318.     Dim A As tBODY
  319.     Dim B As tBODY
  320.     Dim c(cMAXNUMBEROFOBJECTS) As tVECTOR2d
  321.     Dim m As tMANIFOLD
  322.     Dim manifolds(sNUMBEROFBODIES * sNUMBEROFBODIES) As tMANIFOLD
  323.     Dim collisions(sNUMBEROFBODIES * sNUMBEROFBODIES, cMAXNUMBEROFOBJECTS) As tVECTOR2d
  324.     Dim manifoldCount As Integer: manifoldCount = 0
  325.     '    // Generate new collision info
  326.     Dim i, j, k As Integer
  327.     ' TODO: Implement hidden object logic
  328.     For i = 0 To sNUMBEROFBODIES ' number of bodies
  329.         A = body(i)
  330.         For j = i + 1 To sNUMBEROFBODIES
  331.             B = body(j)
  332.             If A.invMass = 0.0 And B.invMass = 0.0 Then _Continue
  333.             'Mainfold solve - handle collisions
  334.             If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_CIRCLE Then
  335.                 Call collisionCCHandle(m, c(), A, B)
  336.             Else
  337.                 If A.shape.ty = cSHAPE_POLYGON And B.shape.ty = cSHAPE_POLYGON Then
  338.                     Call collisionPPHandle(p(), pa(), body(), m, c(), i, j)
  339.                 Else
  340.                     If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_POLYGON Then
  341.                         Call collisionCPHandle(p(), pa(), body(), m, c(), i, j)
  342.                     Else
  343.                         If B.shape.ty = cSHAPE_CIRCLE And A.shape.ty = cSHAPE_POLYGON Then
  344.                             Call collisionPCHandle(p(), pa(), body(), m, c(), i, j)
  345.                         End If
  346.                     End If
  347.                 End If
  348.             End If
  349.             If m.contactCount > 0 Then
  350.                 m.A = i 'identify the index of objects
  351.                 m.B = j
  352.                 manifolds(manifoldCount) = m
  353.                 For k = 0 To m.contactCount
  354.                     collisions(manifoldCount, k) = c(k)
  355.                 Next
  356.                 manifoldCount = manifoldCount + 1
  357.             End If
  358.         Next
  359.     Next
  360.     '   // Integrate forces
  361.     For i = 0 To sNUMBEROFBODIES
  362.         Call impulseIntegrateForces(body(i), dt)
  363.     Next
  364.     '   // Initialize collision
  365.     For i = 0 To manifoldCount - 1
  366.         ' this is the stupidest thing ever since QB will not let you split arrays
  367.         For k = 0 To manifolds(i).contactCount - 1
  368.             c(k) = collisions(i, k)
  369.         Next
  370.         Call manifoldInit(manifolds(i), body(), c())
  371.     Next
  372.     '// Solve collisions
  373.     For j = 0 To iterations - 1
  374.         For i = 0 To manifoldCount - 1
  375.             For k = 0 To manifolds(i).contactCount - 1
  376.                 c(k) = collisions(i, k)
  377.             Next
  378.             Call manifoldApplyImpulse(manifolds(i), body(), c())
  379.         Next i
  380.     Next j
  381.     '// Integrate velocities
  382.     For i = 0 To sNUMBEROFBODIES
  383.         Call impulseIntegrateVelocity(body(i), dt)
  384.     Next
  385.     '// Correct positions
  386.     For i = 0 To manifoldCount - 1
  387.         Call manifoldPositionalCorrection(manifolds(i), body())
  388.     Next
  389.     '// Clear all forces
  390.     For i = 0 To sNUMBEROFBODIES
  391.         Call vectorSet(body(i).force, 0, 0)
  392.         body(i).torque = 0
  393.     Next
  394.  
  395. Sub bodyApplyImpulse (body As tBODY, impulse As tVECTOR2d, contactVector As tVECTOR2d)
  396.     Call vectorAddVectorScalar(body.velocity, impulse, body.invMass)
  397.     body.angularVelocity = body.angularVelocity + body.invInertia * vectorCross(contactVector, impulse)
  398.  
  399. Sub bodySetStatic (body As tBODY)
  400.     body.inertia = 0.0
  401.     body.invInertia = 0.0
  402.     body.mass = 0.0
  403.     body.invMass = 0.0
  404.  
  405. Sub manifoldInit (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  406.     Dim ra As tVECTOR2d
  407.     Dim rb As tVECTOR2d
  408.     Dim rv As tVECTOR2d
  409.     Dim tv1 As tVECTOR2d
  410.     Dim tv2 As tVECTOR2d
  411.     m.e = scalarMin(body(m.A).restitution, body(m.B).restitution)
  412.     m.sf = Sqr(body(m.A).staticFriction * body(m.A).staticFriction)
  413.     m.df = Sqr(body(m.A).dynamicFriction * body(m.A).dynamicFriction)
  414.     Dim i As Integer
  415.     For i = 0 To m.contactCount - 1
  416.         Call vectorSubVectorND(contacts(i), body(m.A).position, ra)
  417.         Call vectorSubVectorND(contacts(i), body(m.B).position, rb)
  418.  
  419.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  420.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  421.         Call vectorAddVector(tv1, body(m.B).velocity)
  422.         Call vectorSubVectorND(tv2, body(m.A).velocity, tv2)
  423.         Call vectorSubVectorND(rv, tv1, tv2)
  424.  
  425.         If vectorLengthSq(rv) < sRESTING Then
  426.             m.e = 0.0
  427.         End If
  428.     Next
  429.  
  430. Sub manifoldApplyImpulse (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  431.     Dim ra As tVECTOR2d
  432.     Dim rb As tVECTOR2d
  433.     Dim rv As tVECTOR2d
  434.     Dim tv1 As tVECTOR2d
  435.     Dim tv2 As tVECTOR2d
  436.     Dim contactVel As _Float
  437.  
  438.     Dim raCrossN As _Float
  439.     Dim rbCrossN As _Float
  440.     Dim invMassSum As _Float
  441.     Dim i As Integer
  442.     Dim j As _Float
  443.     Dim impulse As tVECTOR2d
  444.  
  445.     Dim t As tVECTOR2d
  446.     Dim jt As _Float
  447.     Dim tangentImpulse As tVECTOR2d
  448.  
  449.     If impulseEqual(body(m.A).invMass + body(m.B).invMass, 0.0) Then
  450.         Call manifoldInfiniteMassCorrection(body(m.A), body(m.B))
  451.         Exit Sub
  452.     End If
  453.  
  454.     For i = 0 To m.contactCount - 1
  455.         '// Calculate radii from COM to contact
  456.         '// Vec2 ra = contacts[i] - A->position;
  457.         '// Vec2 rb = contacts[i] - B->position;
  458.         Call vectorSubVectorND(ra, contacts(i), body(m.A).position)
  459.         Call vectorSubVectorND(rb, contacts(i), body(m.B).position)
  460.  
  461.         '// Relative velocity
  462.         '// Vec2 rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  463.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  464.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  465.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  466.         Call vectorSubVector(rv, body(m.A).velocity)
  467.         Call vectorSubVector(rv, tv2)
  468.  
  469.         '// Relative velocity along the normal
  470.         '// real contactVel = Dot( rv, normal );
  471.         contactVel = vectorDot(rv, m.normal)
  472.  
  473.         '// Do not resolve if velocities are separating
  474.         If contactVel > 0 Then Exit Sub
  475.  
  476.         '// real raCrossN = Cross( ra, normal );
  477.         '// real rbCrossN = Cross( rb, normal );
  478.         '// real invMassSum = A->im + B->im + Sqr( raCrossN ) * A->iI + Sqr( rbCrossN ) * B->iI;
  479.         raCrossN = vectorCross(ra, m.normal)
  480.         rbCrossN = vectorCross(rb, m.normal)
  481.         invMassSum = body(m.A).invMass + body(m.B).invMass + (raCrossN * raCrossN) * body(m.A).invInertia + (rbCrossN * rbCrossN) * body(m.B).invInertia
  482.  
  483.         '// Calculate impulse scalar
  484.         j = -(1.0 + m.e) * contactVel
  485.         j = j / invMassSum
  486.         j = j / m.contactCount
  487.  
  488.         '// Apply impulse
  489.         Call vectorMultiplyScalarND(impulse, m.normal, j)
  490.  
  491.         Call vectorNegND(tv1, impulse)
  492.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  493.         Call bodyApplyImpulse(body(m.B), impulse, rb)
  494.  
  495.         '// Friction impulse
  496.         '// rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  497.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  498.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  499.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  500.         Call vectorSubVector(rv, body(m.A).velocity)
  501.         Call vectorSubVector(rv, tv2)
  502.  
  503.         '// Vec2 t = rv - (normal * Dot( rv, normal ));
  504.         '// t.Normalize( );
  505.         Call vectorMultiplyScalarND(t, m.normal, vectorDot(rv, m.normal))
  506.         Call vectorSubVectorND(t, rv, t)
  507.         Call vectorNormalize(t)
  508.  
  509.         '// j tangent magnitude
  510.         jt = -vectorDot(rv, t)
  511.         jt = jt / invMassSum
  512.         jt = jt / m.contactCount
  513.  
  514.         '// Don't apply tiny friction impulses
  515.         If impulseEqual(jt, 0.0) Then Exit Sub
  516.  
  517.         '// Coulumb's law
  518.         If Abs(jt) < j * m.sf Then
  519.             Call vectorMultiplyScalarND(tangentImpulse, t, jt)
  520.         Else
  521.             Call vectorMultiplyScalarND(tangentImpulse, t, -j * m.df)
  522.         End If
  523.  
  524.         '// Apply friction impulse
  525.         '// A->ApplyImpulse( -tangentImpulse, ra );
  526.         '// B->ApplyImpulse( tangentImpulse, rb );
  527.         Call vectorNegND(tv1, tangentImpulse)
  528.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  529.         Call bodyApplyImpulse(body(m.B), tangentImpulse, rb)
  530.     Next i
  531.  
  532. Sub manifoldPositionalCorrection (m As tMANIFOLD, body() As tBODY)
  533.     Dim correction As _Float
  534.     correction = scalarMax(m.penetration - cPENETRATION_ALLOWANCE, 0.0) / (body(m.A).invMass + body(m.B).invMass) * cPENETRATION_CORRECTION
  535.     Call vectorAddVectorScalar(body(m.A).position, m.normal, -body(m.A).invMass * correction)
  536.     Call vectorAddVectorScalar(body(m.B).position, m.normal, body(m.B).invMass * correction)
  537.  
  538. Sub manifoldInfiniteMassCorrection (A As tBODY, B As tBODY)
  539.     Call vectorSet(A.velocity, 0, 0)
  540.     Call vectorSet(B.velocity, 0, 0)
  541.  
  542. '**********************************************************************************************
  543. '   Collision Stuff Ahead
  544. '**********************************************************************************************
  545.  
  546. Sub collisionCCHandle (m As tMANIFOLD, contacts() As tVECTOR2d, A As tBODY, B As tBODY)
  547.     Dim normal As tVECTOR2d
  548.     Dim dist_sqr As _Float
  549.     Dim radius As _Float
  550.  
  551.     Call vectorSubVectorND(normal, B.position, A.position)
  552.     dist_sqr = vectorLengthSq(normal)
  553.     radius = A.shape.radius + B.shape.radius
  554.  
  555.     If (dist_sqr >= radius * radius) Then
  556.         m.contactCount = 0
  557.     Else
  558.         Dim distance As _Float
  559.         distance = Sqr(dist_sqr)
  560.         m.contactCount = 1
  561.  
  562.         If distance = 0 Then
  563.             m.penetration = A.shape.radius
  564.             Call vectorSet(m.normal, 1.0, 0.0)
  565.             Call vectorSetVector(contacts(0), A.position)
  566.         Else
  567.             m.penetration = radius - distance
  568.             Call vectorDivideScalarND(m.normal, normal, distance)
  569.  
  570.             Call vectorMultiplyScalarND(contacts(0), m.normal, A.shape.radius)
  571.             Call vectorAddVector(contacts(0), A.position)
  572.         End If
  573.     End If
  574.  
  575. Sub collisionPCHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  576.     Call collisionCPHandle(p(), pa(), body(), m, contacts(), B, A)
  577.     If m.contactCount > 0 Then
  578.         Call vectorNeg(m.normal)
  579.     End If
  580.  
  581. Sub collisionCPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  582.     'A is the Circle
  583.     'B is the POLY
  584.     m.contactCount = 0
  585.     Dim center As tVECTOR2d
  586.     Dim tm As tMATRIX2d
  587.     Dim tv As tVECTOR2d
  588.     Dim ARadius As _Float: ARadius = body(A).shape.radius
  589.  
  590.     Call vectorSubVectorND(center, body(A).position, body(B).position)
  591.     Call matrixTranspose(body(B).shape.u, tm)
  592.     Call matrixMultiplyVector(tm, center, center)
  593.  
  594.     Dim separation As _Float: separation = -9999999
  595.     Dim faceNormal As Integer: faceNormal = 0
  596.     Dim i As Integer
  597.     Dim s As _Float
  598.     For i = 0 To pa(B).count
  599.         Call vectorSubVectorND(tv, center, p(pa(B).start + i).vert)
  600.         s = vectorDot(p(pa(B).start + i).norm, tv)
  601.         If s > ARadius Then Exit Sub
  602.         If s > separation Then
  603.             separation = s
  604.             faceNormal = i
  605.         End If
  606.     Next
  607.     Dim v1 As tVECTOR2d
  608.     v1 = p(pa(B).start + faceNormal).vert
  609.     Dim i2 As Integer
  610.     i2 = pa(B).start + arrayNextIndex(faceNormal, pa(B).count)
  611.     Dim v2 As tVECTOR2d
  612.     v2 = p(i2).vert
  613.  
  614.     If separation < cEPSILON Then
  615.         m.contactCount = 1
  616.         Call matrixMultiplyVector(body(B).shape.u, p(pa(B).start + faceNormal).norm, m.normal)
  617.         Call vectorNeg(m.normal)
  618.         Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  619.         Call vectorAddVector(contacts(0), body(A).position)
  620.         m.penetration = ARadius
  621.         Exit Sub
  622.     End If
  623.  
  624.     Dim dot1 As _Float
  625.     Dim dot2 As _Float
  626.  
  627.     Dim tv1 As tVECTOR2d
  628.     Dim tv2 As tVECTOR2d
  629.     Dim n As tVECTOR2d
  630.     Call vectorSubVectorND(tv1, center, v1)
  631.     Call vectorSubVectorND(tv2, v2, v1)
  632.     dot1 = vectorDot(tv1, tv2)
  633.     Call vectorSubVectorND(tv1, center, v2)
  634.     Call vectorSubVectorND(tv2, v1, v2)
  635.     dot2 = vectorDot(tv1, tv2)
  636.     m.penetration = ARadius - separation
  637.     If dot1 <= 0.0 Then
  638.         If vectorSqDist(center, v1) > ARadius * ARadius Then Exit Sub
  639.         m.contactCount = 1
  640.         Call vectorSubVectorND(n, v1, center)
  641.         Call matrixMultiplyVector(body(B).shape.u, n, n)
  642.         Call vectorNormalize(n)
  643.         m.normal = n
  644.         Call matrixMultiplyVector(body(B).shape.u, v1, v1)
  645.         Call vectorAddVectorND(v1, v1, body(B).position)
  646.         contacts(0) = v1
  647.     Else
  648.         If dot2 <= 0.0 Then
  649.             If vectorSqDist(center, v2) > ARadius * ARadius Then Exit Sub
  650.             m.contactCount = 1
  651.             Call vectorSubVectorND(n, v2, center)
  652.             Call matrixMultiplyVector(body(B).shape.u, v2, v2)
  653.             Call vectorAddVectorND(v2, v2, body(B).position)
  654.             contacts(0) = v2
  655.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  656.             Call vectorNormalize(n)
  657.             m.normal = n
  658.         Else
  659.             n = p(pa(B).start + faceNormal).norm
  660.             Call vectorSubVectorND(tv1, center, v1)
  661.             If vectorDot(tv1, n) > ARadius Then Exit Sub
  662.             m.contactCount = 1
  663.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  664.             Call vectorNeg(n)
  665.             m.normal = n
  666.             Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  667.             Call vectorAddVector(contacts(0), body(A).position)
  668.         End If
  669.     End If
  670.  
  671. Function collisionPPClip (n As tVECTOR2d, c As _Float, face() As tVECTOR2d)
  672.     Dim sp As Integer: sp = 0
  673.     Dim o(cMAXNUMBEROFPOLYGONS) As tVECTOR2d
  674.  
  675.     o(0) = face(0)
  676.     o(1) = face(1)
  677.  
  678.     Dim d1 As _Float: d1 = vectorDot(n, face(0)) - c
  679.     Dim d2 As _Float: d2 = vectorDot(n, face(1)) - c
  680.  
  681.     If d1 <= 0.0 Then
  682.         o(sp) = face(0)
  683.         sp = sp + 1
  684.     End If
  685.  
  686.     If d2 <= 0.0 Then
  687.         o(sp) = face(1)
  688.         sp = sp + 1
  689.     End If
  690.  
  691.     If d1 * d2 < 0.0 Then
  692.         Dim alpha As _Float: alpha = d1 / (d1 - d2)
  693.         Dim tempv As tVECTOR2d
  694.         'out[sp] = face[0] + alpha * (face[1] - face[0]);
  695.         Call vectorSubVectorND(tempv, face(1), face(0))
  696.         Call vectorMultiplyScalar(tempv, alpha)
  697.         Call vectorAddVectorND(o(sp), tempv, face(0))
  698.         sp = sp + 1
  699.     End If
  700.     face(0) = o(0)
  701.     face(1) = o(1)
  702.     collisionPPClip = sp
  703.  
  704. Sub collisionPPFindIncidentFace (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, v() As tVECTOR2d, RefPoly As Integer, IncPoly As Integer, referenceIndex As Integer)
  705.     Dim referenceNormal As tVECTOR2d
  706.     Dim uRef As tMATRIX2d: uRef = b(RefPoly).shape.u
  707.     Dim uInc As tMATRIX2d: uInc = b(IncPoly).shape.u
  708.     Dim uTemp As tMATRIX2d
  709.     Dim i As Integer
  710.     referenceNormal = p(pa(RefPoly).start + referenceIndex).norm
  711.  
  712.     '        // Calculate normal in incident's frame of reference
  713.     '        // referenceNormal = RefPoly->u * referenceNormal; // To world space
  714.     Call matrixMultiplyVector(uRef, referenceNormal, referenceNormal)
  715.     '        // referenceNormal = IncPoly->u.Transpose( ) * referenceNormal; // To incident's model space
  716.     Call matrixTranspose(uInc, uTemp)
  717.     Call matrixMultiplyVector(uTemp, referenceNormal, referenceNormal)
  718.  
  719.     Dim incidentFace As Integer: incidentFace = 0
  720.     Dim minDot As _Float: minDot = 9999999
  721.     Dim dot As _Float
  722.     For i = 0 To pa(IncPoly).count
  723.         dot = vectorDot(referenceNormal, p(pa(IncPoly).start + i).norm)
  724.         If (dot < minDot) Then
  725.             minDot = dot
  726.             incidentFace = i
  727.         End If
  728.     Next
  729.  
  730.     '// Assign face vertices for incidentFace
  731.     '// v[0] = IncPoly->u * IncPoly->m_vertices[incidentFace] + IncPoly->body->position;
  732.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(0))
  733.     Call vectorAddVector(v(0), b(IncPoly).position)
  734.  
  735.     '// incidentFace = incidentFace + 1 >= (int32)IncPoly->m_vertexCount ? 0 : incidentFace + 1;
  736.     incidentFace = arrayNextIndex(incidentFace, pa(IncPoly).count)
  737.  
  738.     '// v[1] = IncPoly->u * IncPoly->m_vertices[incidentFace] +  IncPoly->body->position;
  739.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(1))
  740.     Call vectorAddVector(v(1), b(IncPoly).position)
  741.  
  742. Sub collisionPPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  743.     m.contactCount = 0
  744.  
  745.     Dim faceA(100) As Integer
  746.  
  747.     Dim penetrationA As _Float
  748.     penetrationA = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceA(), A, B)
  749.     If penetrationA >= 0.0 Then Exit Sub
  750.  
  751.     Dim faceB(100) As Integer
  752.  
  753.     Dim penetrationB As _Float
  754.     penetrationB = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceB(), B, A)
  755.     If penetrationB >= 0.0 Then Exit Sub
  756.  
  757.  
  758.     Dim referenceIndex As Integer
  759.     Dim flip As Integer
  760.  
  761.     Dim RefPoly As Integer
  762.     Dim IncPoly As Integer
  763.  
  764.     If impulseGT(penetrationA, penetrationB) Then
  765.         RefPoly = A
  766.         IncPoly = B
  767.         referenceIndex = faceA(0)
  768.         flip = 0
  769.     Else
  770.         RefPoly = B
  771.         IncPoly = A
  772.         referenceIndex = faceB(0)
  773.         flip = 1
  774.     End If
  775.  
  776.     Dim incidentFace(2) As tVECTOR2d
  777.  
  778.     Call collisionPPFindIncidentFace(p(), pa(), body(), incidentFace(), RefPoly, IncPoly, referenceIndex)
  779.     Dim v1 As tVECTOR2d
  780.     Dim v2 As tVECTOR2d
  781.     Dim v1t As tVECTOR2d
  782.     Dim v2t As tVECTOR2d
  783.  
  784.     v1 = p(pa(RefPoly).start + referenceIndex).vert
  785.     referenceIndex = arrayNextIndex(referenceIndex, pa(RefPoly).count)
  786.     v2 = p(pa(RefPoly).start + referenceIndex).vert
  787.     '// Transform vertices to world space
  788.     '// v1 = RefPoly->u * v1 + RefPoly->body->position;
  789.     '// v2 = RefPoly->u * v2 + RefPoly->body->position;
  790.     Call matrixMultiplyVector(body(RefPoly).shape.u, v1, v1t)
  791.     Call vectorAddVectorND(v1, v1t, body(RefPoly).position)
  792.     Call matrixMultiplyVector(body(RefPoly).shape.u, v2, v2t)
  793.     Call vectorAddVectorND(v2, v2t, body(RefPoly).position)
  794.  
  795.     '// Calculate reference face side normal in world space
  796.     '// Vec2 sidePlaneNormal = (v2 - v1);
  797.     '// sidePlaneNormal.Normalize( );
  798.     Dim sidePlaneNormal As tVECTOR2d
  799.     Call vectorSubVectorND(sidePlaneNormal, v2, v1)
  800.     Call vectorNormalize(sidePlaneNormal)
  801.  
  802.     '// Orthogonalize
  803.     '// Vec2 refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );
  804.     Dim refFaceNormal As tVECTOR2d
  805.     Call vectorSet(refFaceNormal, sidePlaneNormal.y, -sidePlaneNormal.x)
  806.  
  807.     '// ax + by = c
  808.     '// c is distance from origin
  809.     '// real refC = Dot( refFaceNormal, v1 );
  810.     '// real negSide = -Dot( sidePlaneNormal, v1 );
  811.     '// real posSide = Dot( sidePlaneNormal, v2 );
  812.     Dim refC As _Float: refC = vectorDot(refFaceNormal, v1)
  813.     Dim negSide As _Float: negSide = -vectorDot(sidePlaneNormal, v1)
  814.     Dim posSide As _Float: posSide = vectorDot(sidePlaneNormal, v2)
  815.  
  816.  
  817.     '// Clip incident face to reference face side planes
  818.     '// if(Clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
  819.     Dim negSidePlaneNormal As tVECTOR2d
  820.     Call vectorNegND(negSidePlaneNormal, sidePlaneNormal)
  821.  
  822.     If collisionPPClip(negSidePlaneNormal, negSide, incidentFace()) < 2 Then Exit Sub
  823.     If collisionPPClip(sidePlaneNormal, posSide, incidentFace()) < 2 Then Exit Sub
  824.  
  825.     Call vectorSet(m.normal, refFaceNormal.x, refFaceNormal.y)
  826.     If flip Then Call vectorNeg(m.normal)
  827.  
  828.     '// Keep points behind reference face
  829.     Dim cp As Integer: cp = 0 '// clipped points behind reference face
  830.     Dim separation As _Float
  831.     separation = vectorDot(refFaceNormal, incidentFace(0)) - refC
  832.     If separation <= 0.0 Then
  833.         contacts(cp) = incidentFace(0)
  834.         m.penetration = -separation
  835.         cp = cp + 1
  836.     Else
  837.         m.penetration = 0
  838.     End If
  839.  
  840.     separation = vectorDot(refFaceNormal, incidentFace(1)) - refC
  841.     If separation <= 0.0 Then
  842.         contacts(cp) = incidentFace(1)
  843.         m.penetration = m.penetration + -separation
  844.         cp = cp + 1
  845.         m.penetration = m.penetration / cp
  846.     End If
  847.     m.contactCount = cp
  848.  
  849. Function collisionPPFindAxisLeastPenetration (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, faceIndex() As Integer, A As Integer, B As Integer)
  850.     Dim bestDistance As _Float: bestDistance = -9999999
  851.     Dim bestIndex As Integer: bestIndex = 0
  852.  
  853.     Dim n As tVECTOR2d
  854.     Dim nw As tVECTOR2d
  855.     Dim buT As tMATRIX2d
  856.     Dim s As tVECTOR2d
  857.     Dim nn As tVECTOR2d
  858.     Dim v As tVECTOR2d
  859.     Dim tv As tVECTOR2d
  860.     Dim d As _Float
  861.     Dim i, k As Integer
  862.  
  863.     For i = 0 To pa(A).count
  864.         k = pa(A).start + i
  865.  
  866.         '// Retrieve a face normal from A
  867.         '// Vec2 n = A->m_normals[i];
  868.         '// Vec2 nw = A->u * n;
  869.         n = p(k).norm
  870.         Call matrixMultiplyVector(body(A).shape.u, n, nw)
  871.  
  872.  
  873.         '// Transform face normal into B's model space
  874.         '// Mat2 buT = B->u.Transpose( );
  875.         '// n = buT * nw;
  876.         Call matrixTranspose(body(B).shape.u, buT)
  877.         Call matrixMultiplyVector(buT, nw, n)
  878.  
  879.         '// Retrieve support point from B along -n
  880.         '// Vec2 s = B->GetSupport( -n );
  881.         Call vectorNegND(nn, n)
  882.         Call vectorGetSupport(p(), pa(), B, nn, s)
  883.  
  884.         '// Retrieve vertex on face from A, transform into
  885.         '// B's model space
  886.         '// Vec2 v = A->m_vertices[i];
  887.         '// v = A->u * v + A->body->position;
  888.         '// v -= B->body->position;
  889.         '// v = buT * v;
  890.  
  891.         v = p(k).vert
  892.         Call matrixMultiplyVector(body(A).shape.u, v, tv)
  893.         Call vectorAddVectorND(v, tv, body(A).position)
  894.  
  895.         Call vectorSubVector(v, body(B).position)
  896.         Call matrixMultiplyVector(buT, v, tv)
  897.  
  898.         Call vectorSubVector(s, tv)
  899.         d = vectorDot(n, s)
  900.  
  901.         If d > bestDistance Then
  902.             bestDistance = d
  903.             bestIndex = i
  904.         End If
  905.  
  906.     Next i
  907.  
  908.     faceIndex(0) = bestIndex
  909.  
  910.     collisionPPFindAxisLeastPenetration = bestDistance
  911.  
  912. '**********************************************************************************************
  913. '   Shape Creation Ahead
  914. '**********************************************************************************************
  915.  
  916. Sub shapeCreate (sh As tSHAPE, ty As Integer, radius As _Float)
  917.     Dim u As tMATRIX2d
  918.     Call matrixSetScalar(u, 1, 0, 0, 1)
  919.     sh.ty = ty
  920.     sh.radius = radius
  921.     sh.u = u
  922.  
  923. Sub bodyCreate (body() As tBODY, index As Integer, shape As tSHAPE)
  924.     Call vectorSet(body(index).position, 0, 0)
  925.     Call vectorSet(body(index).velocity, 0, 0)
  926.     body(index).angularVelocity = 0.0
  927.     body(index).torque = 0.0
  928.     body(index).orient = impulseRandom_float(-cPI, cPI)
  929.  
  930.     Call vectorSet(body(index).force, 0, 0)
  931.     body(index).staticFriction = 0.5
  932.     body(index).dynamicFriction = 0.3
  933.     body(index).restitution = 0.2
  934.     body(index).shape = shape
  935.  
  936. Sub boxCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  937.     Dim vertlength As Integer: vertlength = 3
  938.     Dim verts(vertlength) As tVECTOR2d
  939.  
  940.     Call vectorSet(verts(0), -sizex, -sizey)
  941.     Call vectorSet(verts(1), sizex, -sizey)
  942.     Call vectorSet(verts(2), sizex, sizey)
  943.     Call vectorSet(verts(3), -sizex, sizey)
  944.  
  945.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  946.  
  947. Sub vShapeCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  948.     Dim vertlength As Integer: vertlength = 7
  949.     Dim verts(vertlength) As tVECTOR2d
  950.  
  951.     Call vectorSet(verts(0), -sizex, -sizey)
  952.     Call vectorSet(verts(1), sizex, -sizey)
  953.     Call vectorSet(verts(2), sizex, sizey)
  954.     Call vectorSet(verts(3), -sizex, sizey)
  955.     Call vectorSet(verts(4), -sizex, sizey / 2)
  956.     Call vectorSet(verts(5), sizex / 2, sizey / 2)
  957.     Call vectorSet(verts(6), sizex / 2, -sizey / 2)
  958.     Call vectorSet(verts(7), -sizex, sizey / 2)
  959.  
  960.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  961.  
  962. Sub vertexSet (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, verts() As tVECTOR2d, vertLength As Integer)
  963.     Dim rightMost As Integer: rightMost = 0
  964.     Dim highestXCoord As _Float: highestXCoord = verts(0).x
  965.     Dim i As Integer
  966.     Dim x As _Float
  967.     For i = 1 To vertLength
  968.         x = verts(i).x
  969.         If x > highestXCoord Then
  970.             highestXCoord = x
  971.             rightMost = i
  972.         Else
  973.             If x = highestXCoord Then
  974.                 If verts(i).y < verts(rightMost).y Then
  975.                     rightMost = i
  976.                 End If
  977.             End If
  978.         End If
  979.     Next
  980.     Dim hull(cMAXNUMBEROFPOLYGONS) As Integer
  981.     Dim outCount As Integer: outCount = 0
  982.     Dim indexHull As Integer: indexHull = rightMost
  983.     Dim nextHullIndex As Integer
  984.     Dim e1 As tVECTOR2d
  985.     Dim e2 As tVECTOR2d
  986.     Dim c As _Float
  987.     Do
  988.         hull(outCount) = indexHull
  989.         nextHullIndex = 0
  990.         For i = 1 To vertLength
  991.             If nextHullIndex = indexHull Then
  992.                 nextHullIndex = i
  993.                 _Continue
  994.             End If
  995.             Call vectorSubVectorND(e1, verts(nextHullIndex), verts(hull(outCount)))
  996.             Call vectorSubVectorND(e2, verts(i), verts(hull(outCount)))
  997.             c = vectorCross(e1, e2)
  998.             If c < 0.0 Then nextHullIndex = i
  999.             If c = 0.0 And (vectorLengthSq(e2) > vectorLengthSq(e1)) Then
  1000.                 nextHullIndex = i
  1001.             End If
  1002.         Next
  1003.         outCount = outCount + 1
  1004.         indexHull = nextHullIndex
  1005.         If nextHullIndex = rightMost Then
  1006.             pa(index).count = outCount - 1
  1007.             Exit Do
  1008.         End If
  1009.     Loop
  1010.  
  1011.     If index = 0 Then
  1012.         pa(index).start = 0
  1013.     Else
  1014.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  1015.     End If
  1016.  
  1017.     For i = 0 To vertLength
  1018.         p(pa(index).start + i).vert = verts(hull(i))
  1019.     Next
  1020.     Dim face As tVECTOR2d
  1021.     For i = 0 To vertLength
  1022.         Call vectorSubVectorND(face, p(pa(index).start + arrayNextIndex(i, pa(index).count)).vert, p(pa(index).start + i).vert)
  1023.         Call vectorSet(p(pa(index).start + i).norm, face.y, -face.x)
  1024.         Call vectorNormalize(p(pa(index).start + i).norm)
  1025.     Next
  1026.  
  1027. Function arrayNextIndex (i As Integer, count As Integer)
  1028.     arrayNextIndex = ((i + 1) Mod (count + 1))
  1029.  
  1030. Sub renderBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY)
  1031.     Dim i As Integer
  1032.     For i = 0 To sNUMBEROFBODIES
  1033.         'TODO: Put hidden object logic
  1034.         If body(i).shape.ty = cSHAPE_CIRCLE Then
  1035.             If body(i).shape.texture = 0 Then
  1036.                 Call renderWireframeCircle(body(), i)
  1037.             Else
  1038.                 Call renderTexturedCircle(body(), i)
  1039.             End If
  1040.         Else If body(i).shape.ty = cSHAPE_POLYGON Then
  1041.                 If body(i).shape.texture = 0 Then
  1042.                     Call renderWireframePoly(p(), pa(), body(), i)
  1043.                 Else
  1044.                     Call renderTexturedBox(p(), pa(), body(), i)
  1045.                 End If
  1046.             End If
  1047.         End If
  1048.         If body(i).position.x < 0 Then body(i).position.x = _Width
  1049.         If body(i).position.x > _Width Then body(i).position.x = 0
  1050.         ' If b(index).position.y < 0 Then b(index).position.y = _Height
  1051.         If body(i).position.y > _Height Then body(i).position.y = 0
  1052.     Next
  1053.  
  1054. Sub renderWireframePoly (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, index As Integer)
  1055.     Dim a As tVECTOR2d ' dummy vertices
  1056.     Dim b As tVECTOR2d
  1057.  
  1058.     Dim i, element, element_next As Integer
  1059.     For i = 0 To pa(index).count
  1060.         element = pa(index).start + i
  1061.         element_next = pa(index).start + arrayNextIndex(i, pa(index).count) ' wrap around back to first element
  1062.         a = p(element).vert
  1063.         b = p(element_next).vert
  1064.  
  1065.         Call matrixMultiplyVector(b(index).shape.u, a, a)
  1066.         Call matrixMultiplyVector(b(index).shape.u, b, b)
  1067.         Call vectorAddVector(a, b(index).position)
  1068.         Call vectorAddVector(b, b(index).position)
  1069.  
  1070.         Line (a.x, a.y)-(b.x, b.y), b(index).c
  1071.     Next
  1072.  
  1073. Sub renderTexturedBox (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, index As Integer)
  1074.     Dim vert(3) As tVECTOR2d
  1075.     Dim W, H As Integer
  1076.     Dim bm As Long
  1077.     bm = b(index).shape.texture
  1078.     W = _Width(bm): H = _Height(bm)
  1079.     Dim i As Integer
  1080.     For i = 0 To 3
  1081.         vert(i) = p(pa(index).start + i).vert
  1082.         Call matrixMultiplyVector(b(index).shape.u, vert(i), vert(i))
  1083.         Call vectorAddVector(vert(i), b(index).position)
  1084.     Next
  1085.     _MapTriangle (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1086.     _MapTriangle (0, 0)-(0, H - 1)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1087.  
  1088.  
  1089. Sub renderWireframeCircle (b() As tBODY, index As Integer)
  1090.     Circle (b(index).position.x, b(index).position.y), b(index).shape.radius, b(index).c
  1091.     Line (b(index).position.x, b(index).position.y)-(b(index).position.x + Cos(b(index).orient) * b(index).shape.radius, b(index).position.y + Sin(b(index).orient) * b(index).shape.radius), b(index).c
  1092.  
  1093. Sub renderTexturedCircle (b() As tBODY, index As Integer)
  1094.     Dim vert(3) As tVECTOR2d
  1095.     Dim W, H As Integer
  1096.     Dim bm As Long
  1097.     bm = b(index).shape.texture
  1098.     W = _Width(bm): H = _Height(bm)
  1099.     Call vectorSet(vert(0), -b(index).shape.radius, -b(index).shape.radius)
  1100.     Call vectorSet(vert(1), -b(index).shape.radius, b(index).shape.radius)
  1101.     Call vectorSet(vert(2), b(index).shape.radius, b(index).shape.radius)
  1102.     Call vectorSet(vert(3), b(index).shape.radius, -b(index).shape.radius)
  1103.     Dim i As Integer
  1104.     For i = 0 To 3
  1105.         Call matrixMultiplyVector(b(index).shape.u, vert(i), vert(i))
  1106.         Call vectorAddVector(vert(i), b(index).position)
  1107.     Next
  1108.     _MapTriangle (0, 0)-(0, H - 1)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1109.     _MapTriangle (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1110.  
  1111. Sub polygonSetOrient (b As tBODY, radians As _Float)
  1112.     Call matrixSetRadians(b.shape.u, radians)
  1113.  
  1114. '**********************************************************************************************
  1115. '   Object initialization Ahead
  1116. '**********************************************************************************************
  1117.  
  1118. Sub circleInitialize (b() As tBODY, index As Integer)
  1119.     Call circleComputeMass(b(), index, 1.0)
  1120.  
  1121. Sub circleComputeMass (b() As tBODY, index As Integer, density As _Float)
  1122.     b(index).mass = cPI * b(index).shape.radius * b(index).shape.radius * density
  1123.     If b(index).mass <> 0 Then
  1124.         b(index).invMass = 1.0 / b(index).mass
  1125.     Else
  1126.         b(index).invMass = 0.0
  1127.     End If
  1128.  
  1129.     b(index).inertia = b(index).mass * b(index).shape.radius * b(index).shape.radius
  1130.  
  1131.     If b(index).inertia <> 0 Then
  1132.         b(index).invInertia = 1.0 / b(index).inertia
  1133.     Else
  1134.         b(index).invInertia = 0.0
  1135.     End If
  1136.  
  1137. Sub polygonInitialize (body() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer)
  1138.     Call polygonComputeMass(body(), p(), pa(), index, 1.0)
  1139.  
  1140. Sub polygonComputeMass (b() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, density As _Float)
  1141.     Dim c As tVECTOR2d ' centroid
  1142.     Dim p1 As tVECTOR2d
  1143.     Dim p2 As tVECTOR2d
  1144.     Dim area As _Float
  1145.     Dim I As _Float
  1146.     Dim k_inv3 As _Float
  1147.     Dim D As _Float
  1148.     Dim triangleArea As _Float
  1149.     Dim weight As _Float
  1150.     Dim intx2 As _Float
  1151.     Dim inty2 As _Float
  1152.     Dim ii As Integer
  1153.  
  1154.     k_inv3 = 1.0 / 3.0
  1155.  
  1156.     For ii = 0 To pa(index).count
  1157.         p1 = p(pa(index).start + ii).vert
  1158.         p2 = p(pa(index).start + arrayNextIndex(ii, pa(index).count)).vert
  1159.         D = vectorCross(p1, p2)
  1160.         triangleArea = .5 * D
  1161.         area = area + triangleArea
  1162.         weight = triangleArea * k_inv3
  1163.         Call vectorAddVectorScalar(c, p1, weight)
  1164.         Call vectorAddVectorScalar(c, p2, weight)
  1165.         intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x
  1166.         inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y
  1167.         I = I + (0.25 * k_inv3 * D) * (intx2 + inty2)
  1168.     Next ii
  1169.  
  1170.     Call vectorMultiplyScalar(c, 1.0 / area)
  1171.  
  1172.     For ii = 0 To pa(index).count
  1173.         Call vectorSubVector(p(pa(index).start + ii).vert, c)
  1174.     Next
  1175.  
  1176.     b(index).mass = density * area
  1177.     If b(index).mass <> 0.0 Then
  1178.         b(index).invMass = 1.0 / b(index).mass
  1179.     Else
  1180.         b(index).invMass = 0.0
  1181.     End If
  1182.  
  1183.     b(index).inertia = I * density
  1184.     If b(index).inertia <> 0 Then
  1185.         b(index).invInertia = 1.0 / b(index).inertia
  1186.     Else
  1187.         b(index).invInertia = 0.0
  1188.     End If
  1189.  
  1190. '**********************************************************************************************
  1191. '   Vector Math Ahead
  1192. '**********************************************************************************************
  1193.  
  1194. Sub vectorSet (v As tVECTOR2d, x As _Float, y As _Float)
  1195.     v.x = x
  1196.     v.y = y
  1197.  
  1198. Sub vectorSetVector (o As tVECTOR2d, v As tVECTOR2d)
  1199.     o.x = v.x
  1200.     o.y = v.y
  1201.  
  1202. Sub vectorNeg (v As tVECTOR2d)
  1203.     v.x = -v.x
  1204.     v.y = -v.y
  1205.  
  1206. Sub vectorNegND (o As tVECTOR2d, v As tVECTOR2d)
  1207.     o.x = -v.x
  1208.     o.y = -v.y
  1209.  
  1210. Sub vectorMultiplyScalar (v As tVECTOR2d, s As _Float)
  1211.     v.x = v.x * s
  1212.     v.y = v.y * s
  1213.  
  1214. Sub vectorMultiplyScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1215.     o.x = v.x * s
  1216.     o.y = v.y * s
  1217.  
  1218. Sub vectorDivideScalar (v As tVECTOR2d, s As _Float)
  1219.     v.x = v.x / s
  1220.     v.y = v.y / s
  1221.  
  1222. Sub vectorDivideScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1223.     o.x = v.x / s
  1224.     o.y = v.y / s
  1225.  
  1226. Sub vectorAddScalar (v As tVECTOR2d, s As _Float)
  1227.     v.x = v.x + s
  1228.     v.y = v.y + s
  1229.  
  1230. Sub vectorAddScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1231.     o.x = v.x + s
  1232.     o.y = v.y + s
  1233.  
  1234. Sub vectorMultiplyVector (v As tVECTOR2d, m As tVECTOR2d)
  1235.     v.x = v.x * m.x
  1236.     v.y = v.y * m.y
  1237.  
  1238. Sub vectorMultiplyVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1239.     o.x = v.x * m.x
  1240.     o.y = v.y * m.y
  1241.  
  1242. Sub vectorDivideVector (v As tVECTOR2d, m As tVECTOR2d)
  1243.     v.x = v.x / m.x
  1244.     v.y = v.y / m.y
  1245.  
  1246. Sub vectorAddVector (v As tVECTOR2d, m As tVECTOR2d)
  1247.     v.x = v.x + m.x
  1248.     v.y = v.y + m.y
  1249.  
  1250. Sub vectorAddVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1251.     o.x = v.x + m.x
  1252.     o.y = v.y + m.y
  1253.  
  1254. Sub vectorAddVectorScalar (v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1255.     v.x = v.x + m.x * s
  1256.     v.y = v.y + m.y * s
  1257.  
  1258. Sub vectorAddVectorScalarND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1259.     o.x = v.x + m.x * s
  1260.     o.y = v.y + m.y * s
  1261.  
  1262. Sub vectorSubVector (v As tVECTOR2d, m As tVECTOR2d)
  1263.     v.x = v.x - m.x
  1264.     v.y = v.y - m.y
  1265.  
  1266. Sub vectorSubVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1267.     o.x = v.x - m.x
  1268.     o.y = v.y - m.y
  1269.  
  1270. Function vectorLengthSq (v As tVECTOR2d)
  1271.     vectorLengthSq = v.x * v.x + v.y * v.y
  1272.  
  1273. Function vectorLength (v As tVECTOR2d)
  1274.     vectorLength = Sqr(vectorLengthSq(v))
  1275.  
  1276. Sub vectorRotate (v As tVECTOR2d, radians As _Float)
  1277.     Dim c, s, xp, yp As _Float
  1278.     c = Cos(radians)
  1279.     s = Sin(radians)
  1280.     xp = v.x * c - v.y * s
  1281.     yp = v.x * s + v.y * c
  1282.     v.x = xp
  1283.     v.y = yp
  1284.  
  1285. Sub vectorNormalize (v As tVECTOR2d)
  1286.     Dim lenSQ, invLen As _Float
  1287.     lenSQ = vectorLengthSq(v)
  1288.     If lenSQ > cEPSILON_SQ Then
  1289.         invLen = 1.0 / Sqr(lenSQ)
  1290.         v.x = v.x * invLen
  1291.         v.y = v.y * invLen
  1292.     End If
  1293.  
  1294. Sub vectorMin (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1295.     o.x = scalarMin(a.x, b.x)
  1296.     o.y = scalarMin(a.y, b.y)
  1297.  
  1298. Sub vectorMax (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1299.     o.x = scalarMax(a.x, b.x)
  1300.     o.y = scalarMax(a.y, b.y)
  1301.  
  1302. Function vectorDot (a As tVECTOR2d, b As tVECTOR2d)
  1303.     vectorDot = a.x * b.x + a.y * b.y
  1304.  
  1305. Function vectorSqDist (a As tVECTOR2d, b As tVECTOR2d)
  1306.     Dim dx, dy As _Float
  1307.     dx = b.x - a.x
  1308.     dy = b.y - a.y
  1309.     vectorSqDist = dx * dx + dy * dy
  1310.  
  1311. Function vectorDistance (a As tVECTOR2d, b As tVECTOR2d)
  1312.     vectorDistance = Sqr(vectorSqDist(a, b))
  1313.  
  1314. Function vectorCross (a As tVECTOR2d, b As tVECTOR2d)
  1315.     vectorCross = a.x * b.y - a.y * b.x
  1316.  
  1317. Sub vectorCrossScalar (o As tVECTOR2d, v As tVECTOR2d, a As _Float)
  1318.     o.x = v.y * -a
  1319.     o.y = v.x * a
  1320.  
  1321. Function vectorArea (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1322.     vectorArea = (((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)))
  1323.  
  1324. Function vectorLeft (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1325.     vectorLeft = vectorArea(a, b, c) > 0
  1326.  
  1327. Function vectorLeftOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1328.     vectorLeftOn = vectorArea(a, b, c) >= 0
  1329.  
  1330. Function vectorRight (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1331.     vectorRight = vectorArea(a, b, c) < 0
  1332.  
  1333. Function vectorRightOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1334.     vectorRightOn = vectorArea(a, b, c) <= 0
  1335.  
  1336. Function vectorCollinear (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d, thresholdAngle As _Float)
  1337.     If (thresholdAngle = 0) Then
  1338.         vectorCollinear = (vectorArea(a, b, c) = 0)
  1339.     Else
  1340.         Dim ab As tVECTOR2d
  1341.         Dim bc As tVECTOR2d
  1342.         Dim dot As _Float
  1343.         Dim magA As _Float
  1344.         Dim magB As _Float
  1345.         Dim angle As _Float
  1346.  
  1347.         ab.x = b.x - a.x
  1348.         ab.y = b.y - a.y
  1349.         bc.x = c.x - b.x
  1350.         bc.y = c.y - b.y
  1351.  
  1352.         dot = ab.x * bc.x + ab.y * bc.y
  1353.         magA = Sqr(ab.x * ab.x + ab.y * ab.y)
  1354.         magB = Sqr(bc.x * bc.x + bc.y * bc.y)
  1355.         angle = _Acos(dot / (magA * magB))
  1356.         vectorCollinear = angle < thresholdAngle
  1357.     End If
  1358.  
  1359. Sub vectorGetSupport (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, dir As tVECTOR2d, bestVertex As tVECTOR2d)
  1360.     Dim bestProjection As _Float
  1361.     Dim v As tVECTOR2d
  1362.     Dim projection As _Float
  1363.     Dim i As Integer
  1364.     bestVertex.x = -9999999
  1365.     bestVertex.y = -9999999
  1366.     bestProjection = -9999999
  1367.  
  1368.     For i = 0 To pa(index).count
  1369.         v = p(i + pa(index).start).vert
  1370.         projection = vectorDot(v, dir)
  1371.         If projection > bestProjection Then
  1372.             bestVertex = v
  1373.             bestProjection = projection
  1374.         End If
  1375.     Next
  1376.  
  1377. '**********************************************************************************************
  1378. '   Matrix Stuff Ahead
  1379. '**********************************************************************************************
  1380.  
  1381. Sub matrixSetRadians (m As tMATRIX2d, radians As _Float)
  1382.     Dim c As _Float
  1383.     Dim s As _Float
  1384.     c = Cos(radians)
  1385.     s = Sin(radians)
  1386.     m.m00 = c
  1387.     m.m01 = -s
  1388.     m.m10 = s
  1389.     m.m11 = c
  1390.  
  1391. Sub matrixSetScalar (m As tMATRIX2d, a As _Float, b As _Float, c As _Float, d As _Float)
  1392.     m.m00 = a
  1393.     m.m01 = b
  1394.     m.m10 = c
  1395.     m.m11 = d
  1396.  
  1397. Sub matrixAbs (m As tMATRIX2d, o As tMATRIX2d)
  1398.     o.m00 = Abs(m.m00)
  1399.     o.m01 = Abs(m.m01)
  1400.     o.m10 = Abs(m.m10)
  1401.     o.m11 = Abs(m.m11)
  1402.  
  1403. Sub matrixGetAxisX (m As tMATRIX2d, o As tVECTOR2d)
  1404.     o.x = m.m00
  1405.     o.y = m.m10
  1406.  
  1407. Sub matrixGetAxisY (m As tMATRIX2d, o As tVECTOR2d)
  1408.     o.x = m.m01
  1409.     o.y = m.m11
  1410.  
  1411. Sub matrixTransposeI (m As tMATRIX2d)
  1412.     Swap m.m01, m.m10
  1413.  
  1414. Sub matrixTranspose (m As tMATRIX2d, o As tMATRIX2d)
  1415.     o.m00 = m.m00
  1416.     o.m01 = m.m10
  1417.     o.m10 = m.m01
  1418.     o.m11 = m.m11
  1419.  
  1420. Sub matrixMultiplyVector (m As tMATRIX2d, v As tVECTOR2d, o As tVECTOR2d)
  1421.     Dim t As tVECTOR2d
  1422.     t.x = m.m00 * v.x + m.m01 * v.y
  1423.     t.y = m.m10 * v.x + m.m11 * v.y
  1424.     o = t
  1425.  
  1426. Sub matrixMultiplyMatrix (m As tMATRIX2d, x As tMATRIX2d, o As tMATRIX2d)
  1427.     o.m00 = m.m00 * x.m00 + m.m01 * x.m10
  1428.     o.m01 = m.m00 * x.m01 + m.m01 * x.m11
  1429.     o.m10 = m.m10 * x.m00 + m.m11 * x.m10
  1430.     o.m11 = m.m10 * x.m01 + m.m11 * x.m11
  1431.  
  1432. '**********************************************************************************************
  1433. '   Mostly Unused Stuff Ahead
  1434. '**********************************************************************************************
  1435.  
  1436. Sub polygonMakeCCW (obj As tTRIANGLE)
  1437.     If vectorLeft(obj.a, obj.b, obj.c) = 0 Then
  1438.         Swap obj.a, obj.c
  1439.     End If
  1440.  
  1441. Function polygonIsReflex (t As tTRIANGLE)
  1442.     polygonIsReflex = vectorRight(t.a, t.b, t.c)
  1443.  
  1444. Function scalarMin (a As _Float, b As _Float)
  1445.     If a < b Then
  1446.         scalarMin = a
  1447.     Else
  1448.         scalarMin = b
  1449.     End If
  1450.  
  1451. Function scalarMax (a As _Float, b As _Float)
  1452.     If a > b Then
  1453.         scalarMax = a
  1454.     Else
  1455.         scalarMax = b
  1456.     End If
  1457.  
  1458. Sub lineIntersection (l1 As tLINE2d, l2 As tLINE2d, o As tVECTOR2d)
  1459.     Dim a1, b1, c1, a2, b2, c2, det As _Float
  1460.     o.x = 0
  1461.     o.y = 0
  1462.     a1 = l1.b.y - l1.a.y
  1463.     b1 = l1.a.x - l1.b.x
  1464.     c1 = a1 * l1.a.x + b1 * l1.a.y
  1465.     a2 = l2.b.y - l2.a.y
  1466.     b2 = l2.a.x - l2.b.x
  1467.     c2 = a2 * l2.a.x + b2 * l2.a.y
  1468.     det = a1 * b2 - a2 * b1
  1469.  
  1470.     If Int(det * cPRECISION) <> 0 Then
  1471.         o.x = (b2 * c1 - b1 * c2) / det
  1472.         o.y = (a1 * c2 - a2 * c1) / det
  1473.     End If
  1474.  
  1475. Function lineSegmentsIntersect (l1 As tLINE2d, l2 As tLINE2d)
  1476.     Dim dx, dy, da, db, s, t As _Float
  1477.     dx = l1.b.x - l1.a.x
  1478.     dy = l1.b.y - l1.a.y
  1479.     da = l2.b.x - l2.a.x
  1480.     db = l2.b.y - l2.a.y
  1481.     If da * dy - db * dx = 0 Then
  1482.         lineSegmentsIntersect = 0
  1483.     Else
  1484.         s = (dx * (l2.a.y - l1.a.y) + dy * (l1.a.x - l2.a.x)) / (da * dy - db * dx)
  1485.         t = (da * (l1.a.y - l2.a.y) + db * (l2.a.x - l1.a.x)) / (db * dx - da * dy)
  1486.         lineSegmentsIntersect = (s >= 0 And s <= 1 And t >= 0 And t <= 1)
  1487.     End If
  1488.  
  1489. '**********************************************************************************************
  1490. '   Impulse Specific Math Ahead
  1491. '**********************************************************************************************
  1492.  
  1493. Function impulseEqual (a As _Float, b As _Float)
  1494.     impulseEqual = Abs(a - b) <= cEPSILON
  1495.  
  1496. Function impulseClamp (min As _Float, max As _Float, a As _Float)
  1497.     If a < min Then
  1498.         impulseClamp = min
  1499.     Else If a > max Then
  1500.             impulseClamp = max
  1501.         Else
  1502.             impulseClamp = a
  1503.         End If
  1504.     End If
  1505.  
  1506. Function impulseRound (a As _Float)
  1507.     impulseRound = Int(a + 0.5)
  1508.  
  1509. Function impulseRandom_float (min As _Float, max As _Float)
  1510.     impulseRandom_float = ((max - min) * Rnd + min)
  1511.  
  1512. Function impulseRandomInteger (min As Integer, max As Integer)
  1513.     impulseRandomInteger = Int((max - min) * Rnd + min)
  1514.  
  1515. Function impulseGT (a As _Float, b As _Float)
  1516.     impulseGT = (a >= b * cBIAS_RELATIVE + a * cBIAS_ABSOLUTE)
  1517.  
  1518. '**********************************************************************************************
  1519. '   Troubleshooting Tools
  1520. '**********************************************************************************************
  1521.  
  1522. Sub printMatrix (u As tMATRIX2d, n As Integer)
  1523.     Print "---------------------------"
  1524.     Print n; " u:"; u.m00; "|"; u.m10
  1525.     Print "       "; u.m10; "|"; u.m11
  1526.  
  1527.  
  1528. '**********************************************************************************************
  1529. '   Generate Bitmap
  1530. '**********************************************************************************************
  1531.  
  1532. Sub generateBitmap (bm() As Long)
  1533.     Dim cl As _Unsigned Long
  1534.     Dim rleCount, y, x, i As Integer
  1535.  
  1536.     Data 0,30,4289110427,4,0,52,4289110427,15,0,44,4289110427,21,0,38,4289110427,7
  1537.     Data 4281808695,11,4289110427,7,0,34,4289110427,5,4281808695,18,4289110427,5,0,30,4289110427,4
  1538.     Data 4281808695,23,4289110427,4,0,28,4289110427,3,4281808695,27,4289110427,3,0,25,4289110427,3
  1539.     Data 4281808695,31,4289110427,3,0,22,4289110427,3,4281808695,33,4289110427,3,0,20,4289110427,3
  1540.     Data 4281808695,35,4289110427,3,0,18,4289110427,3,4281808695,38,4289110427,2,0,16,4289110427,3
  1541.     Data 4281808695,40,4289110427,2,0,14,4289110427,3,4281808695,42,4289110427,2,0,13,4289110427,2
  1542.     Data 4281808695,43,4289110427,3,0,11,4289110427,2,4281808695,45,4289110427,2,0,10,4289110427,2
  1543.     Data 4281808695,47,4289110427,2,0,8,4289110427,2,4281808695,49,4289110427,2,0,7,4289110427,2
  1544.     Data 4281808695,49,4289110427,2,0,6,4289110427,2,4281808695,51,4289110427,2,0,5,4289110427,2
  1545.     Data 4281808695,51,4289110427,2,0,5,4289110427,1,4281808695,53,4289110427,2,0,3,4289110427,2
  1546.     Data 4281808695,53,4289110427,2,0,3,4289110427,2,4281808695,54,4289110427,1,0,3,4289110427,1
  1547.     Data 4281808695,7,4281084972,0,4279703319,0,4279637526,10,4280492835,0,4281808695,7,4279637526,14,4279834905,0
  1548.     Data 4281742902,0,4281808695,8,4289110427,2,0,1,4289110427,2,4281808695,6,4279637526,15,4281808695,6
  1549.     Data 4279637526,17,4281808695,7,4289110427,2,0,1,4289110427,2,4281808695,5,4279637526,1,4282412608,0
  1550.     Data 4284394590,14,4281808695,5,4279637526,1,4284394590,15,4284394334,0,4281808695,7,4289110427,1,0,1
  1551.     Data 4289110427,1,4281808695,6,4279637526,0,4280034076,0,4284394590,16,4281808695,4,4279637526,1,4284394590,16
  1552.     Data 4284129114,0,4281808695,6,4289110427,1,0,1,4289110427,1,4281808695,6,4279637526,0,4284394590,17
  1553.     Data 4283333454,0,4281808695,3,4279637526,1,4284394590,17,4281808695,6,4289110427,2,0,0,4289110427,1
  1554.     Data 4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1
  1555.     Data 4284394590,2,4281808695,9,4279637526,0,4284394590,3,4281808695,6,4289110427,2,0,0,4289110427,1
  1556.     Data 4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1
  1557.     Data 4284394590,2,4281808695,9,4279637526,0,4284394590,3,4281808695,6,4289110427,5,4281808695,6,4279637526,0
  1558.     Data 4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,2,4281808695,9
  1559.     Data 4279637526,0,4284394590,3,4281808695,6,4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9
  1560.     Data 4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,2,4279637526,10,4284394590,3,4281808695,6
  1561.     Data 4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3
  1562.     Data 4279637526,1,4284394590,17,4281808695,6,4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9
  1563.     Data 4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,16,4281808695,7,4289110427,5,4281808695,6
  1564.     Data 4279637526,0,4284394590,3,4281808695,7,4281084972,0,4279703319,0,4279637526,0,4284394590,3,4281808695,3
  1565.     Data 4279637526,1,4284394590,17,4281808695,6,4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0
  1566.     Data 4284394590,3,4281808695,6,4279637526,3,4284394590,3,4281808695,3,4279637526,1,4284394590,17,4281808695,6
  1567.     Data 4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0,4284394590,3,4281808695,5,4279637526,1
  1568.     Data 4282412608,0,4284394590,5,4281808695,3,4279637526,1,4284394590,2,4281808695,9,4279637526,0,4284394590,3
  1569.     Data 4281808695,6,4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0,4284394590,3,4281808695,5
  1570.     Data 4279637526,0,4280034076,0,4284394590,6,4281808695,3,4279637526,1,4284394590,2,4281808695,9,4279637526,0
  1571.     Data 4284394590,3,4281808695,6,4289110427,1,0,1,4289110427,2,4281808695,5,4279637526,0,4284394590,3
  1572.     Data 4279637526,6,4284394590,7,4281808695,3,4279637526,1,4284394590,2,4279637526,10,4284394590,3,4281808695,6
  1573.     Data 4289110427,1,0,1,4289110427,2,4281808695,5,4279637526,0,4284394590,16,4283532625,0,4281875000,0
  1574.     Data 4281808695,3,4279637526,1,4284394590,17,4281808695,5,4289110427,2,0,2,4289110427,1,4281808695,6
  1575.     Data 4284394590,15,4281808695,6,4279637526,1,4284394590,17,4281808695,5,4289110427,2,0,2,4289110427,1
  1576.     Data 4281808695,7,4284394590,13,4281808695,7,4279637526,1,4284394590,16,4282803270,0,4281808695,5,4289110427,1
  1577.     Data 0,3,4289110427,2,4281808695,7,4284394590,12,4281808695,9,4284394590,15,4282670916,0,4281808695,5
  1578.     Data 4289110427,2,0,4,4289110427,1,4281808695,53,4289110427,2,0,4,4289110427,2,4281808695,52
  1579.     Data 4289110427,1,0,5,4289110427,2,4281808695,51,4289110427,2,0,6,4289110427,2,4281808695,50
  1580.     Data 4289110427,1,0,7,4289110427,2,4281808695,49,4289110427,2,0,8,4289110427,2,4281808695,47
  1581.     Data 4289110427,2,0,10,4289110427,2,4281808695,46,4289110427,1,0,12,4289110427,2,4281808695,44
  1582.     Data 4289110427,2,0,12,4289110427,2,4281808695,43,4289110427,2,0,14,4289110427,2,4281808695,41
  1583.     Data 4289110427,2,0,16,4289110427,2,4281808695,39,4289110427,2,0,18,4289110427,3,4281808695,36
  1584.     Data 4289110427,2,0,20,4289110427,3,4281808695,33,4289110427,3,0,22,4289110427,3,4281808695,31
  1585.     Data 4289110427,3,0,24,4289110427,4,4281808695,28,4289110427,3,0,27,4289110427,4,4281808695,24
  1586.     Data 4289110427,3,0,30,4289110427,5,4281808695,19,4289110427,5,0,33,4289110427,6,4281808695,13
  1587.     Data 4289110427,6,0,37,4289110427,23,0,42,4289110427,17,0,49,4289110427,9,0,26
  1588.  
  1589.  
  1590.     Data 4278190160,1484,4278650685,0,4279637527,0,4279637526,13,4278190160,5,4279637526,3,4278190160,10,4279637526,3
  1591.     Data 4278190160,21,4279637526,16,4278190160,5,4279637526,3,4278190160,10,4279637526,3,4278190160,20,4279637526,1
  1592.     Data 4286280720,0,4290950923,16,4278190160,3,4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3
  1593.     Data 4278190160,19,4279637526,0,4280624149,0,4290950923,17,4278190160,3,4279637526,1,4290950923,2,4278190160,9
  1594.     Data 4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,18,4278190160,3,4279637526,1,4290950923,2
  1595.     Data 4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,18,4279637526,1
  1596.     Data 4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,18
  1597.     Data 4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3
  1598.     Data 4278190160,18,4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0
  1599.     Data 4290950923,3,4279637526,10,4279505947,0,4278190160,6,4279374370,0,4279637526,0,4290950923,2,4279637526,10
  1600.     Data 4290950923,3,4278190160,19,4279637526,0,4290950923,16,4278190160,6,4279637526,0,4290950923,17,4278190160,19
  1601.     Data 4279637526,0,4290950923,17,4278190160,5,4278255950,0,4290950923,17,4278190160,19,4279637526,0,4290950923,17
  1602.     Data 4282070843,0,4278190160,5,4278584910,0,4290950923,16,4278190160,19,4279637526,0,4290950923,17,4290622221,0
  1603.     Data 4278190160,7,4286149157,0,4290950923,14,4278190160,19,4279637526,0,4290950923,3,4278190160,9,4279637526,0
  1604.     Data 4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,9
  1605.     Data 4279637526,0,4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3
  1606.     Data 4279637526,10,4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,17
  1607.     Data 4290687756,0,4278190160,18,4279637526,0,4290950923,3,4278190160,20,4290950923,17,4282597176,0,4278190160,18
  1608.     Data 4279637526,0,4290950923,3,4278190160,21,4290950923,16,4278190160,19,4279637526,0,4290950923,3,4278190160,21
  1609.     Data 4278255952,0,4290950923,14,4278190160,21,4290950923,3,4278190160,1353
  1610.  
  1611.     For i = 0 To 1
  1612.  
  1613.         bm(i) = _NewImage(64, 64, 32)
  1614.         _Dest bm(i)
  1615.  
  1616.         Read cl
  1617.         Read rleCount
  1618.  
  1619.         For y = 0 To 63
  1620.             For x = 0 To 63
  1621.                 PSet (x, y), cl
  1622.                 rleCount = rleCount - 1
  1623.                 If rleCount < 0 Then
  1624.                     Read cl
  1625.                     Read rleCount
  1626.                 End If
  1627.             Next
  1628.         Next
  1629.     Next
  1630.  
  1631.     _Dest 0
  1632.  

Offline Richard Frost

  • Seasoned Forum Regular
  • Posts: 316
  • Needle nardle noo. - Peter Sellers
    • View Profile
Re: 2D Physics Engine
« Reply #6 on: April 21, 2021, 05:00:14 pm »
I get 22 fps average, 35 tops on a 12 year old laptop running Win10. 

That's a great simulation of a terrible elevator system.  I'll take the
stairs.  Oh, the stairs are in there too.  Best to stay out of the building.
« Last Edit: April 21, 2021, 05:03:24 pm by Richard Frost »
It works better if you plug it in.

Offline AndyA

  • Newbie
  • Posts: 73
    • View Profile
Re: 2D Physics Engine
« Reply #7 on: April 22, 2021, 02:51:42 pm »
That's a very well done physics engine!

I got between 90 and 200 FPS, it flickered so quickly, it was hard to read.

What method of indexing the objects did you use, a hash table or linked list? Maybe a blend of the two methods.

I looked at the code, but since I haven't use either, it kind of went over my head.

Oh well, maybe you could give a quick overview of how you keep track of everything.

It seems that this 2D physics engine could handle many different types of applications. Angry Birds, pinball, pachinko, etc. 


Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D Physics Engine
« Reply #8 on: April 22, 2021, 04:05:46 pm »
Thanks for that!

As far as keeping track of objects it is merely an entry into a couple arrays. There is currently three arrays that handle the rigid bodies.
 
Code: QB64: [Select]
  1. Dim poly(cMAXNUMBEROFPOLYGONS) As tPOLY
  2. Dim polyattributes(cMAXNUMBEROFPOLYGONS) As tPOLYATTRIB
  3. Dim body(cMAXNUMBEROFOBJECTS) As tBODY
  • Poly is a monolithic list of vertices for all objects.
  • Polyattributes is for keeping track of which vertices are for each object.
  • Body is for keeping track of each rigid body (i.e. position, velocity etc.)

It's a bit of a mess and I plan on circling back to it and rethink,refactor and rename. I spent weeks of writing code without knowing how it would turn out or even being able to properly test it. So, not everything is optimal :)

Right now my focus is...
  • Getting joints working properly, that will expand its range of uses (simple cars and motorcycles.)
  • Making collision information easier access.
  • Camera tools, so that games like angry birds can exceed the size of the screen
  • Make it more approachable for a beginner to use. Simplify scene building and object management.
  • Perhaps quad-trees to optimize the collision.
  • Multithreading if I really want to punish myself.
  • Whatever the community thinks it needs.

Thanks for the comment!

Offline AndyA

  • Newbie
  • Posts: 73
    • View Profile
Re: 2D Physics Engine
« Reply #9 on: April 22, 2021, 05:00:21 pm »
Games like moto-cross, mario kart would be much easier with joints. For simple games with low poly counts (like billiards or pinball) the arrays seem like more than  enough to keep track of everything.

At any rate I'll be keeping an eye on your progress, as this would be a very nice addition to something like the 'QB64 tool box' (where SUBs/Functions and libraries) are posted for others to utilize. Whatever you do would be great as I haven't seen any physics engine on this board. So many possible applications for physics base simulations and such.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D Physics Engine
« Reply #10 on: April 23, 2021, 04:20:24 pm »
Hello

I have a new update.
  • Joints are now working, more work needs to be done with the management, and constraints.
  • The demo is a bit more interactive, add a hedgehog that you can move around with arrow keys or WASD.
  • Fixed the FPS counter.
  • Set '_LIMIT 120' so that the hegehog wouldn't lag so bad.
  • Renamed Engine to FzxNGN. Because vowels are obsolete.
  • Added some collision query tools.
Please enjoy and share with me your feedback!

Thanks
Code: QB64: [Select]
  1. '**********************************************************************************************
  2. '   FzxNGN written by justsomeguy
  3. '   Physics code ported from RandyGaul's Impulse Engine
  4. '   https://github.com/RandyGaul/ImpulseEngine
  5. '   http://RandyGaul.net
  6. '**********************************************************************************************
  7. '/*
  8. '    Copyright (c) 2013 Randy Gaul http://RandyGaul.net
  9.  
  10. '    This software is provided 'as-is', without any express or implied
  11. '    warranty. In no event will the authors be held liable for any damages
  12. '    arising from the use of this software.
  13.  
  14. '    Permission is granted to anyone to use this software for any purpose,
  15. '    including commercial applications, and to alter it and redistribute it
  16. '    freely, subject to the following restrictions:
  17. '      1. The origin of this software must not be misrepresented; you must not
  18. '         claim that you wrote the original software. If you use this software
  19. '         in a product, an acknowledgment in the product documentation would be
  20. '         appreciated but is not required.
  21. '      2. Altered source versions must be plainly marked as such, and must not be
  22. '         misrepresented as being the original software.
  23. '      3. This notice may not be removed or altered from any source distribution.
  24. '
  25. '    Port to QB64 by justsomeguy
  26. '*/
  27. '
  28.  
  29. '**********************************************************************************************
  30. '   Setup Types and Variables
  31. '**********************************************************************************************
  32. Type tVECTOR2d
  33.     x As _Float
  34.     y As _Float
  35.  
  36. Type tLINE2d ' Not used
  37.     a As tVECTOR2d
  38.     b As tVECTOR2d
  39.  
  40. Type tFACE2d ' Not used
  41.     f0 As tVECTOR2d
  42.     f1 As tVECTOR2d
  43.  
  44. Type tTRIANGLE ' Not used
  45.     a As tVECTOR2d
  46.     b As tVECTOR2d
  47.     c As tVECTOR2d
  48.  
  49. Type tMATRIX2d
  50.     m00 As _Float
  51.     m01 As _Float
  52.     m10 As _Float
  53.     m11 As _Float
  54.  
  55. Type tSHAPE
  56.     ty As Integer ' cSHAPE_CIRCLE = 1, cSHAPE_POLYGON = 2
  57.     radius As _Float ' Only necessary for circle shapes
  58.     u As tMATRIX2d ' Only neceassary for polygons
  59.     texture As Long
  60.     flipTexture As Integer 'flag for flipping texture depending on direction
  61.  
  62. Type tPOLY 'list of vertices for all objects in simulation
  63.     vert As tVECTOR2d
  64.     norm As tVECTOR2d
  65.  
  66. Type tPOLYATTRIB 'keep track of polys in monlithic list of vertices
  67.     start As Integer ' starting vertex of the polygon
  68.     count As Integer ' number of vertices in polygon
  69.  
  70. Type tBODY
  71.     position As tVECTOR2d
  72.     velocity As tVECTOR2d
  73.     force As tVECTOR2d
  74.     angularVelocity As _Float
  75.     torque As _Float
  76.     orient As _Float
  77.     mass As _Float
  78.     invMass As _Float
  79.     inertia As _Float
  80.     invInertia As _Float
  81.     staticFriction As _Float
  82.     dynamicFriction As _Float
  83.     restitution As _Float
  84.     shape As tSHAPE
  85.     c As Long ' color
  86.     visible As Integer 'Hide a body ;)
  87.  
  88. Type tMANIFOLD
  89.     A As Integer
  90.     B As Integer
  91.     penetration As _Float
  92.     normal As tVECTOR2d
  93.     contactCount As Integer
  94.     e As _Float
  95.     df As _Float
  96.     sf As _Float
  97.  
  98. Type tHIT
  99.     A As Integer
  100.     B As Integer
  101.     position As tVECTOR2d
  102.     force As _Float
  103.  
  104. Type tJOINT
  105.     M As tMATRIX2d
  106.     localAnchor1 As tVECTOR2d
  107.     localAnchor2 As tVECTOR2d
  108.     r1 As tVECTOR2d
  109.     r2 As tVECTOR2d
  110.     bias As tVECTOR2d
  111.     P As tVECTOR2d
  112.     body1 As Integer
  113.     body2 As Integer
  114.     biasFactor As _Float
  115.     softness As _Float
  116.  
  117. Const cSHAPE_CIRCLE = 1
  118. Const cSHAPE_POLYGON = 2
  119. Const cPRECISION = 100
  120. Const cMAXNUMOFTRIANGLES = 100
  121. Const cMAXNUMBEROFOBJECTS = 1000 ' Max number of objects at one time
  122. Const cMAXNUMBEROFPOLYGONS = 10000 ' Max number of total vertices included in all objects
  123. Const cMAXNUMBEROFJOINTS = 100
  124. Const cMAXNUMBEROFHITS = 1000
  125. Const cPI = 3.14159
  126. Const cEPSILON = 0.0001
  127. Const cEPSILON_SQ = cEPSILON * cEPSILON
  128. Const cBIAS_RELATIVE = 0.95
  129. Const cBIAS_ABSOLUTE = 0.01
  130. Const cDT = 1.0 / 20.0
  131. Const cITERATIONS = 10
  132. Const cPENETRATION_ALLOWANCE = 0.05
  133. Const cPENETRATION_CORRECTION = 0.4 ' misspelled in original code
  134. Const cPARAMETER_POSITION = 1
  135. Const cPARAMETER_VELOCITY = 2
  136. Const cPARAMETER_FORCE = 3
  137. Const cPARAMETER_ANGULARVELOCITY = 4
  138. Const cPARAMETER_TORQUE = 5
  139. Const cPARAMETER_ORIENT = 6
  140. Const cPARAMETER_STATICFRICTION = 7
  141. Const cPARAMETER_DYNAMICFRICTION = 8
  142. Const cPARAMETER_COLOR = 9
  143. Const cPARAMETER_VISIBLE = 10
  144. Const cPARAMETER_STATIC = 11
  145. Const cPARAMETER_TEXTURE = 12
  146. Const cPARAMETER_FLIPTEXTURE = 13
  147. Const cRENDER_JOINTS = 0
  148. Const cRENDER_COLLISIONS = 0
  149. Const cPLAYER_FORCE = 1000000
  150.  
  151. Dim Shared sGRAVITY As tVECTOR2d: Call vectorSet(sGRAVITY, 0.0, 50.0)
  152. Dim Shared sRESTING As _Float: Dim o As tVECTOR2d: Call vectorMultiplyScalarND(o, sGRAVITY, cDT): sRESTING = vectorLengthSq(o) + cEPSILON
  153. Dim Shared sNUMBEROFBODIES As Integer: sNUMBEROFBODIES = 50 ' 0 is included - Don't go under 25 for the current scene
  154. Dim Shared sNUMBEROFJOINTS As Integer: sNUMBEROFJOINTS = 12 ' if zero then no joints at all
  155.  
  156. Dim poly(cMAXNUMBEROFPOLYGONS) As tPOLY
  157. Dim polyattributes(cMAXNUMBEROFPOLYGONS) As tPOLYATTRIB
  158. Dim body(cMAXNUMBEROFOBJECTS) As tBODY
  159. Dim joints(cMAXNUMBEROFJOINTS) As tJOINT
  160. Dim hits(cMAXNUMBEROFHITS) As tHIT
  161.  
  162. Dim timerOne As Integer
  163.  
  164. Const cSCENE_USER = 0
  165. Const cSCENE_FLOOR = 1
  166. Const cSCENE_SPINNEROFDEATH = 2
  167. Const cSCENE_RIGHTRAMP = 3
  168. Const cSCENE_LEFTRAMP = 4
  169. Const cSCENE_DOZEROFDOOM = 5
  170. Const cSCENE_ELEVATOROFTERROR = 6
  171. Const cSCENE_RIGHTWALL = 7
  172. Const cSCENE_ELEVATORKICKER = 8
  173. Const cSCENE_PIVOT = 9
  174. Const cSCENE_PENDULUM = 10
  175.  
  176. '**********************************************************************************************
  177. _Title "FzxNGN" ' vowels are obsolete
  178. Screen _NewImage(1024, 768, 32)
  179.  
  180. timerOne = _FreeTimer
  181. On Timer(timerOne, 1) renderFps
  182. Timer(timerOne) On
  183.  
  184. Call buildSimpleScene(poly(), polyattributes(), body(), joints())
  185. '**********************************************************************************************
  186. Do: fps = fps + 1
  187.     Cls , _RGB32(28, 28, 22)
  188.     Call impulseStep(poly(), polyattributes(), body(), joints(), cDT, cITERATIONS, hits())
  189.     Call renderBodies(poly(), polyattributes(), body(), joints(), hits())
  190.     Call animateScene(body(), hits())
  191.     Locate 1, 60: Print "FPS:"; (fpsLast)
  192.     _Display
  193.     _Limit 120
  194. '**********************************************************************************************
  195. '   End of Main loop
  196. '**********************************************************************************************
  197.  
  198. '**********************************************************************************************
  199. '   Scene Creation and Handling Ahead
  200. '**********************************************************************************************
  201. Sub animateScene (body() As tBODY, hits() As tHIT)
  202.  
  203.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_USER, 0, 0)
  204.     If _KeyDown(32) Or _KeyDown(87) Or _KeyDown(119) Or _KeyDown(18432) Then
  205.         If isBodyTouching(hits(), cSCENE_USER) Then
  206.             Call vectorSet(body(cSCENE_USER).force, 0, -cPLAYER_FORCE)
  207.         End If
  208.     End If
  209.  
  210.     If _KeyDown(65) Or _KeyDown(97) Or _KeyDown(19200) Then
  211.         Call vectorSet(body(cSCENE_USER).force, -cPLAYER_FORCE / 10, 0)
  212.     End If
  213.  
  214.     If _KeyDown(68) Or _KeyDown(100) Or _KeyDown(19712) Then
  215.         Call vectorSet(body(cSCENE_USER).force, cPLAYER_FORCE / 10, 0)
  216.     End If
  217.  
  218.     body(cSCENE_USER).velocity.x = impulseClamp(-100, 100, body(cSCENE_USER).velocity.x)
  219.     body(cSCENE_USER).velocity.y = impulseClamp(-100, 100, body(cSCENE_USER).velocity.y)
  220.  
  221.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_SPINNEROFDEATH, body(cSCENE_SPINNEROFDEATH).orient + (cPI / 90), 0)
  222.     If body(cSCENE_SPINNEROFDEATH).orient > 2 * cPI Then body(cSCENE_SPINNEROFDEATH).orient = 0
  223.  
  224.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_DOZEROFDOOM, body(cSCENE_DOZEROFDOOM).position.x - 1, _Height - 75)
  225.     If body(cSCENE_DOZEROFDOOM).position.x < 120 Then body(cSCENE_DOZEROFDOOM).position.x = _Width
  226.  
  227.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATOROFTERROR, 55, body(cSCENE_ELEVATOROFTERROR).position.y - 1)
  228.     If body(cSCENE_ELEVATOROFTERROR).position.y < 50 Then body(cSCENE_ELEVATOROFTERROR).position.y = _Height - 25
  229.  
  230.  
  231. Sub buildSimpleScene (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, j() As tJOINT)
  232.     Dim i, ty As Integer
  233.     Dim bm(5) As Long ' array texture bitmaps
  234.     Call generateBitmap(bm()) ' auto generated bitmaps can be substituted for whatever images you have
  235.  
  236.     Call createBoxBodies(p(), pa(), body(), cSCENE_USER, 20, 20)
  237.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_USER, (20), (20))
  238.     Call setBody(body(), cPARAMETER_VELOCITY, cSCENE_USER, 0, 0)
  239.     Call setBody(body(), cPARAMETER_TEXTURE, cSCENE_USER, bm(2), 0)
  240.     Call setBody(body(), cPARAMETER_FLIPTEXTURE, cSCENE_USER, 1, 0)
  241.  
  242.     Call createBoxBodies(p(), pa(), body(), cSCENE_FLOOR, 1000, 5)
  243.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_FLOOR, (_Width / 2), (_Height - 20))
  244.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_FLOOR, 0, 0)
  245.  
  246.     Call createBoxBodies(p(), pa(), body(), cSCENE_SPINNEROFDEATH, 75, 5)
  247.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_SPINNEROFDEATH, (_Width / 2), (_Height - 100))
  248.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_SPINNEROFDEATH, 0, 0)
  249.  
  250.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTRAMP, 350, 5)
  251.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_RIGHTRAMP, (_Width * .75) + 35, (_Height / 2) + 105)
  252.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_RIGHTRAMP, (cPI * .75), 0)
  253.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_RIGHTRAMP, 0, 0)
  254.  
  255.     Call createBoxBodies(p(), pa(), body(), cSCENE_LEFTRAMP, 225, 5)
  256.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_LEFTRAMP, (_Width * .25) + 55, (_Height / 2))
  257.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_LEFTRAMP, (cPI * .1), 0)
  258.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_LEFTRAMP, 0, 0)
  259.  
  260.     Call createBoxBodies(p(), pa(), body(), cSCENE_DOZEROFDOOM, 5, 50)
  261.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_DOZEROFDOOM, 0, _Height - 75)
  262.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_DOZEROFDOOM, 0, 0)
  263.  
  264.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATOROFTERROR, 50, 5)
  265.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATOROFTERROR, 55, _Height - 25)
  266.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_ELEVATOROFTERROR, (cPI * .98), 0)
  267.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_ELEVATOROFTERROR, 0, 0)
  268.  
  269.     Call createBoxBodies(p(), pa(), body(), cSCENE_RIGHTWALL, 5, 375)
  270.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_RIGHTWALL, 5, _Height / 2 - 20)
  271.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_RIGHTWALL, 0, 0)
  272.  
  273.     Call createBoxBodies(p(), pa(), body(), cSCENE_ELEVATORKICKER, 100, 5)
  274.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_ELEVATORKICKER, 55, 120)
  275.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_ELEVATORKICKER, (cPI * .70), 0)
  276.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_ELEVATORKICKER, 0, 0)
  277.     '********************************************************
  278.     '   Make the Chain
  279.     '********************************************************
  280.  
  281.     Call createBoxBodies(p(), pa(), body(), cSCENE_PIVOT, 20, 20)
  282.     Call setBody(body(), cPARAMETER_POSITION, cSCENE_PIVOT, _Width / 2, 50)
  283.     Call setBody(body(), cPARAMETER_ORIENT, cSCENE_PIVOT, (cPI * 1.5), 0)
  284.     Call setBody(body(), cPARAMETER_STATIC, cSCENE_PIVOT, 0, 0)
  285.  
  286.     For i = 0 To sNUMBEROFJOINTS - 1
  287.         Call createBoxBodies(p(), pa(), body(), cSCENE_PENDULUM + i, 12, 8)
  288.         ' Call createCircleBody(pa(), body(), cSCENE_PENDULUM + i, 10)
  289.         Call setBody(body(), cPARAMETER_ORIENT, cSCENE_PENDULUM + i, (cPI * 1.5), 0)
  290.         Call setBody(body(), cPARAMETER_POSITION, cSCENE_PENDULUM + i, _Width / 2, 90 + (i * 30))
  291.         Call jointSet(j(i + 1), body(), cSCENE_PENDULUM + i - 1, cSCENE_PENDULUM + i, body(cSCENE_PENDULUM + i - 1).position.x, body(cSCENE_PENDULUM + i - 1).position.y)
  292.     Next i
  293.  
  294.     '********************************************************
  295.     '   Make Random objects
  296.     '********************************************************
  297.     For i = cSCENE_PENDULUM + sNUMBEROFJOINTS To sNUMBEROFBODIES
  298.         If Rnd > .35 Then ' Circles are much faster than polygons
  299.             ty = cSHAPE_CIRCLE
  300.         Else
  301.             ty = cSHAPE_POLYGON
  302.         End If
  303.         Call createBodies(p(), pa(), body(), i, ty)
  304.         Call setBody(body(), cPARAMETER_POSITION, i, (Rnd * _Width / 2) + (_Width / 4), ((Rnd * _Height / 2) + (_Height / 4)) - 200)
  305.         Call setBody(body(), cPARAMETER_VELOCITY, i, Rnd * 100 - 50, Rnd * 100 - 50)
  306.     Next
  307.  
  308.     '********************************************************
  309.     '  Texture Bodies
  310.     '********************************************************
  311.     For i = cSCENE_ELEVATORKICKER + 1 To sNUMBEROFBODIES
  312.         If body(i).shape.ty = cSHAPE_CIRCLE Then
  313.             Call setBody(body(), cPARAMETER_TEXTURE, i, bm(0), 0)
  314.         Else
  315.             If body(i).shape.ty = cSHAPE_POLYGON Then
  316.                 Call setBody(body(), cPARAMETER_TEXTURE, i, bm(1), 0)
  317.             End If
  318.         End If
  319.     Next
  320.  
  321. '**********************************************************************************************
  322. '   Collision Helper Tools
  323. '**********************************************************************************************
  324. Function isBodyTouchingBody (hits() As tHIT, A As Integer, B As Integer)
  325.     Dim hitcount As Integer: hitcount = 1
  326.     isBodyTouchingBody = 0
  327.     Do While hits(hitcount).A <> hits(hitcount).B
  328.         If hits(hitcount).A = A And hits(hitcount).B = B Then
  329.             isBodyTouchingBody = hitcount
  330.             Exit Function
  331.         End If
  332.         hitcount = hitcount + 1: If hitcount > UBound(hits) Then Exit Function
  333.     Loop
  334.  
  335. Function isBodyTouchingStatic (body() As tBODY, hits() As tHIT, A As Integer)
  336.     Dim hitcount As Integer: hitcount = 1
  337.     isBodyTouchingStatic = 0
  338.     Do While hits(hitcount).A <> hits(hitcount).B
  339.         If hits(hitcount).A = A Then
  340.             If body(hits(hitcount).B).mass = 0 Then
  341.                 isBodyTouchingStatic = hitcount
  342.                 Exit Function
  343.             End If
  344.         End If
  345.         hitcount = hitcount + 1: If hitcount > UBound(hits) Then Exit Function
  346.     Loop
  347.  
  348. Function isBodyTouching (hits() As tHIT, A As Integer)
  349.     Dim hitcount As Integer: hitcount = 1
  350.     isBodyTouching = 0
  351.     Do While hits(hitcount).A <> hits(hitcount).B
  352.         If hits(hitcount).A = A Then
  353.             isBodyTouching = hitcount
  354.             Exit Function
  355.         End If
  356.         hitcount = hitcount + 1: If hitcount > UBound(hits) Then Exit Function
  357.     Loop
  358.  
  359. '**********************************************************************************************
  360. '   Scene Creation Tools ahead
  361. '**********************************************************************************************
  362.  
  363. Sub setBody (body() As tBODY, Parameter As Integer, Index As Integer, arg1 As _Float, arg2 As _Float)
  364.     Select Case Parameter
  365.         Case cPARAMETER_POSITION:
  366.             Call vectorSet(body(Index).position, arg1, arg2)
  367.         Case cPARAMETER_VELOCITY:
  368.             Call vectorSet(body(Index).velocity, arg1, arg2)
  369.         Case cPARAMETER_FORCE:
  370.             Call vectorSet(body(Index).force, arg1, arg2)
  371.         Case cPARAMETER_ANGULARVELOCITY:
  372.             body(Index).angularVelocity = arg1
  373.         Case cPARAMETER_TORQUE:
  374.             body(Index).torque = arg1
  375.         Case cPARAMETER_ORIENT:
  376.             body(Index).orient = arg1
  377.             Call matrixSetRadians(body(Index).shape.u, body(Index).orient)
  378.         Case cPARAMETER_STATICFRICTION:
  379.             body(Index).staticFriction = arg1
  380.         Case cPARAMETER_DYNAMICFRICTION:
  381.             body(Index).dynamicFriction = arg1
  382.         Case cPARAMETER_COLOR:
  383.             body(Index).c = arg1
  384.         Case cPARAMETER_VISIBLE:
  385.             body(Index).visible = arg1
  386.         Case cPARAMETER_STATIC:
  387.             Call bodySetStatic(body(Index))
  388.         Case cPARAMETER_TEXTURE:
  389.             body(Index).shape.texture = arg1
  390.         Case cPARAMETER_FLIPTEXTURE:
  391.             body(Index).shape.flipTexture = arg1
  392.     End Select
  393.  
  394. Sub createCircleBody (pa() As tPOLYATTRIB, body() As tBODY, index As Integer, radius As _Float)
  395.     Dim shape As tSHAPE
  396.     Call shapeCreate(shape, cSHAPE_CIRCLE, radius)
  397.     Call bodyCreate(body(), index, shape)
  398.     Call circleInitialize(body(), index)
  399.     ' Even though circles do not have Polygons, they still must be included in the Vertices
  400.     If index = 0 Then
  401.         pa(index).start = 0
  402.     Else
  403.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  404.     End If
  405.     pa(index).count = 1
  406.     body(index).c = _RGB32(255, 255, 255)
  407.  
  408. Sub createBoxBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, xs As _Float, ys As _Float)
  409.     Dim shape As tSHAPE
  410.     Call shapeCreate(shape, cSHAPE_POLYGON, 0)
  411.     Call bodyCreate(body(), index, shape)
  412.     Call boxCreate(p(), pa(), index, xs, ys)
  413.     Call polygonInitialize(body(), p(), pa(), index)
  414.     body(index).c = _RGB32(255, 255, 255)
  415.  
  416. Sub createBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, index As Integer, ty As Integer)
  417.     If ty = cSHAPE_CIRCLE Then
  418.         Call createCircleBody(pa(), body(), index, 10 + Rnd * 20)
  419.     Else
  420.         If ty = cSHAPE_POLYGON Then
  421.             Call createBoxBodies(p(), pa(), body(), index, 10 + Rnd * 10, 10 + Rnd * 10)
  422.         End If
  423.     End If
  424.     body(index).c = _RGB32(55 + Rnd * 200, 55 + Rnd * 200, 55 + Rnd * 200)
  425.  
  426. '**********************************************************************************************
  427. '   Physics and Collision Stuff Ahead
  428. '**********************************************************************************************
  429.  
  430. Sub impulseIntegrateForces (b As tBODY, dt As _Float)
  431.     If b.invMass = 0.0 Then Exit Sub
  432.     Dim dts As _Float
  433.     dts = dt * .5
  434.     Call vectorAddVectorScalar(b.velocity, b.force, b.invMass * dts)
  435.     Call vectorAddVectorScalar(b.velocity, sGRAVITY, dts)
  436.     b.angularVelocity = b.angularVelocity + (b.torque * b.invInertia * dts)
  437.  
  438. Sub impulseIntegrateVelocity (body As tBODY, dt As _Float)
  439.     If body.invMass = 0.0 Then Exit Sub
  440.     Call vectorAddVectorScalar(body.position, body.velocity, dt)
  441.     body.orient = body.orient + (body.angularVelocity * dt)
  442.     Call matrixSetRadians(body.shape.u, body.orient)
  443.     Call impulseIntegrateForces(body, dt)
  444.  
  445. Sub impulseStep (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, j() As tJOINT, dt As _Float, iterations As Integer, hits() As tHIT)
  446.     Dim A As tBODY
  447.     Dim B As tBODY
  448.     Dim c(cMAXNUMBEROFOBJECTS) As tVECTOR2d
  449.     Dim m As tMANIFOLD
  450.     Dim manifolds(sNUMBEROFBODIES * sNUMBEROFBODIES) As tMANIFOLD
  451.     Dim collisions(sNUMBEROFBODIES * sNUMBEROFBODIES, cMAXNUMBEROFOBJECTS) As tVECTOR2d
  452.     Dim manifoldCount As Integer: manifoldCount = 0
  453.     '    // Generate new collision info
  454.     Dim i, j, k As Integer
  455.     Dim hitCount As Integer: hitCount = 0
  456.     Dim dHit As tHIT
  457.     ' // erase hitlist
  458.     Do
  459.         hits(hitCount) = dHit
  460.         hitCount = hitCount + 1
  461.     Loop Until hits(hitCount).A = hits(hitCount).B
  462.     hitCount = 0
  463.  
  464.     ' TODO: Implement hidden object logic
  465.     For i = 0 To sNUMBEROFBODIES ' number of bodies
  466.         A = body(i)
  467.         For j = i + 1 To sNUMBEROFBODIES
  468.             B = body(j)
  469.             If A.invMass = 0.0 And B.invMass = 0.0 Then _Continue
  470.             'Mainfold solve - handle collisions
  471.             If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_CIRCLE Then
  472.                 Call collisionCCHandle(m, c(), A, B)
  473.             Else
  474.                 If A.shape.ty = cSHAPE_POLYGON And B.shape.ty = cSHAPE_POLYGON Then
  475.                     Call collisionPPHandle(p(), pa(), body(), m, c(), i, j)
  476.                 Else
  477.                     If A.shape.ty = cSHAPE_CIRCLE And B.shape.ty = cSHAPE_POLYGON Then
  478.                         Call collisionCPHandle(p(), pa(), body(), m, c(), i, j)
  479.                     Else
  480.                         If B.shape.ty = cSHAPE_CIRCLE And A.shape.ty = cSHAPE_POLYGON Then
  481.                             Call collisionPCHandle(p(), pa(), body(), m, c(), i, j)
  482.                         End If
  483.                     End If
  484.                 End If
  485.             End If
  486.             If m.contactCount > 0 Then
  487.                 m.A = i 'identify the index of objects
  488.                 m.B = j
  489.                 manifolds(manifoldCount) = m
  490.                 For k = 0 To m.contactCount
  491.                     hits(hitCount).A = i
  492.                     hits(hitCount).B = j
  493.                     hits(hitCount).position = c(k)
  494.                     collisions(manifoldCount, k) = c(k)
  495.                     hitCount = hitCount + 1
  496.                 Next
  497.                 manifoldCount = manifoldCount + 1
  498.             End If
  499.         Next
  500.     Next
  501.  
  502.     '   // Integrate forces
  503.     For i = 0 To sNUMBEROFBODIES
  504.         Call impulseIntegrateForces(body(i), dt)
  505.     Next
  506.     '   // Initialize collision
  507.     For i = 0 To manifoldCount - 1
  508.         ' this is the stupidest thing ever since QB will not let you split arrays
  509.         For k = 0 To manifolds(i).contactCount - 1
  510.             c(k) = collisions(i, k)
  511.         Next
  512.         Call manifoldInit(manifolds(i), body(), c())
  513.     Next
  514.     ' joint pre Steps
  515.  
  516.     For i = 1 To sNUMBEROFJOINTS
  517.         Call jointPrestep(j(i), body(), dt)
  518.     Next
  519.  
  520.     '// Solve collisions
  521.     For j = 0 To iterations - 1
  522.  
  523.         For i = 0 To manifoldCount - 1
  524.             For k = 0 To manifolds(i).contactCount - 1
  525.                 c(k) = collisions(i, k)
  526.             Next
  527.             Call manifoldApplyImpulse(manifolds(i), body(), c())
  528.         Next
  529.         For i = 1 To sNUMBEROFJOINTS
  530.             Call jointApplyImpulse(j(i), body())
  531.         Next
  532.     Next
  533.  
  534.     '// Integrate velocities
  535.     For i = 0 To sNUMBEROFBODIES
  536.         Call impulseIntegrateVelocity(body(i), dt)
  537.     Next
  538.     '// Correct positions
  539.     For i = 0 To manifoldCount - 1
  540.         Call manifoldPositionalCorrection(manifolds(i), body())
  541.     Next
  542.     '// Clear all forces
  543.     For i = 0 To sNUMBEROFBODIES
  544.         Call vectorSet(body(i).force, 0, 0)
  545.         body(i).torque = 0
  546.     Next
  547.  
  548. Sub bodyApplyImpulse (body As tBODY, impulse As tVECTOR2d, contactVector As tVECTOR2d)
  549.     Call vectorAddVectorScalar(body.velocity, impulse, body.invMass)
  550.     body.angularVelocity = body.angularVelocity + body.invInertia * vectorCross(contactVector, impulse)
  551.  
  552. Sub bodySetStatic (body As tBODY)
  553.     body.inertia = 0.0
  554.     body.invInertia = 0.0
  555.     body.mass = 0.0
  556.     body.invMass = 0.0
  557.  
  558. Sub manifoldInit (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  559.     Dim ra As tVECTOR2d
  560.     Dim rb As tVECTOR2d
  561.     Dim rv As tVECTOR2d
  562.     Dim tv1 As tVECTOR2d
  563.     Dim tv2 As tVECTOR2d
  564.     m.e = scalarMin(body(m.A).restitution, body(m.B).restitution)
  565.     m.sf = Sqr(body(m.A).staticFriction * body(m.A).staticFriction)
  566.     m.df = Sqr(body(m.A).dynamicFriction * body(m.A).dynamicFriction)
  567.     Dim i As Integer
  568.     For i = 0 To m.contactCount - 1
  569.         Call vectorSubVectorND(contacts(i), body(m.A).position, ra)
  570.         Call vectorSubVectorND(contacts(i), body(m.B).position, rb)
  571.  
  572.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  573.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  574.         Call vectorAddVector(tv1, body(m.B).velocity)
  575.         Call vectorSubVectorND(tv2, body(m.A).velocity, tv2)
  576.         Call vectorSubVectorND(rv, tv1, tv2)
  577.  
  578.         If vectorLengthSq(rv) < sRESTING Then
  579.             m.e = 0.0
  580.         End If
  581.     Next
  582.  
  583. Sub manifoldApplyImpulse (m As tMANIFOLD, body() As tBODY, contacts() As tVECTOR2d)
  584.     Dim ra As tVECTOR2d
  585.     Dim rb As tVECTOR2d
  586.     Dim rv As tVECTOR2d
  587.     Dim tv1 As tVECTOR2d
  588.     Dim tv2 As tVECTOR2d
  589.     Dim contactVel As _Float
  590.  
  591.     Dim raCrossN As _Float
  592.     Dim rbCrossN As _Float
  593.     Dim invMassSum As _Float
  594.     Dim i As Integer
  595.     Dim j As _Float
  596.     Dim impulse As tVECTOR2d
  597.  
  598.     Dim t As tVECTOR2d
  599.     Dim jt As _Float
  600.     Dim tangentImpulse As tVECTOR2d
  601.  
  602.     If impulseEqual(body(m.A).invMass + body(m.B).invMass, 0.0) Then
  603.         Call manifoldInfiniteMassCorrection(body(m.A), body(m.B))
  604.         Exit Sub
  605.     End If
  606.  
  607.     For i = 0 To m.contactCount - 1
  608.         '// Calculate radii from COM to contact
  609.         '// Vec2 ra = contacts[i] - A->position;
  610.         '// Vec2 rb = contacts[i] - B->position;
  611.         Call vectorSubVectorND(ra, contacts(i), body(m.A).position)
  612.         Call vectorSubVectorND(rb, contacts(i), body(m.B).position)
  613.  
  614.         '// Relative velocity
  615.         '// Vec2 rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  616.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  617.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  618.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  619.         Call vectorSubVector(rv, body(m.A).velocity)
  620.         Call vectorSubVector(rv, tv2)
  621.  
  622.         '// Relative velocity along the normal
  623.         '// real contactVel = Dot( rv, normal );
  624.         contactVel = vectorDot(rv, m.normal)
  625.  
  626.         '// Do not resolve if velocities are separating
  627.         If contactVel > 0 Then Exit Sub
  628.  
  629.         '// real raCrossN = Cross( ra, normal );
  630.         '// real rbCrossN = Cross( rb, normal );
  631.         '// real invMassSum = A->im + B->im + Sqr( raCrossN ) * A->iI + Sqr( rbCrossN ) * B->iI;
  632.         raCrossN = vectorCross(ra, m.normal)
  633.         rbCrossN = vectorCross(rb, m.normal)
  634.         invMassSum = body(m.A).invMass + body(m.B).invMass + (raCrossN * raCrossN) * body(m.A).invInertia + (rbCrossN * rbCrossN) * body(m.B).invInertia
  635.  
  636.         '// Calculate impulse scalar
  637.         j = -(1.0 + m.e) * contactVel
  638.         j = j / invMassSum
  639.         j = j / m.contactCount
  640.  
  641.         '// Apply impulse
  642.         Call vectorMultiplyScalarND(impulse, m.normal, j)
  643.  
  644.         Call vectorNegND(tv1, impulse)
  645.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  646.         Call bodyApplyImpulse(body(m.B), impulse, rb)
  647.  
  648.         '// Friction impulse
  649.         '// rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
  650.         Call vectorCrossScalar(tv1, rb, body(m.B).angularVelocity)
  651.         Call vectorCrossScalar(tv2, ra, body(m.A).angularVelocity)
  652.         Call vectorAddVectorND(rv, tv1, body(m.B).velocity)
  653.         Call vectorSubVector(rv, body(m.A).velocity)
  654.         Call vectorSubVector(rv, tv2)
  655.  
  656.         '// Vec2 t = rv - (normal * Dot( rv, normal ));
  657.         '// t.Normalize( );
  658.         Call vectorMultiplyScalarND(t, m.normal, vectorDot(rv, m.normal))
  659.         Call vectorSubVectorND(t, rv, t)
  660.         Call vectorNormalize(t)
  661.  
  662.         '// j tangent magnitude
  663.         jt = -vectorDot(rv, t)
  664.         jt = jt / invMassSum
  665.         jt = jt / m.contactCount
  666.  
  667.         '// Don't apply tiny friction impulses
  668.         If impulseEqual(jt, 0.0) Then Exit Sub
  669.  
  670.         '// Coulumb's law
  671.         If Abs(jt) < j * m.sf Then
  672.             Call vectorMultiplyScalarND(tangentImpulse, t, jt)
  673.         Else
  674.             Call vectorMultiplyScalarND(tangentImpulse, t, -j * m.df)
  675.         End If
  676.  
  677.         '// Apply friction impulse
  678.         '// A->ApplyImpulse( -tangentImpulse, ra );
  679.         '// B->ApplyImpulse( tangentImpulse, rb );
  680.         Call vectorNegND(tv1, tangentImpulse)
  681.         Call bodyApplyImpulse(body(m.A), tv1, ra)
  682.         Call bodyApplyImpulse(body(m.B), tangentImpulse, rb)
  683.     Next i
  684.  
  685. Sub manifoldPositionalCorrection (m As tMANIFOLD, body() As tBODY)
  686.     Dim correction As _Float
  687.     correction = scalarMax(m.penetration - cPENETRATION_ALLOWANCE, 0.0) / (body(m.A).invMass + body(m.B).invMass) * cPENETRATION_CORRECTION
  688.     Call vectorAddVectorScalar(body(m.A).position, m.normal, -body(m.A).invMass * correction)
  689.     Call vectorAddVectorScalar(body(m.B).position, m.normal, body(m.B).invMass * correction)
  690.  
  691. Sub manifoldInfiniteMassCorrection (A As tBODY, B As tBODY)
  692.     Call vectorSet(A.velocity, 0, 0)
  693.     Call vectorSet(B.velocity, 0, 0)
  694.  
  695. '**********************************************************************************************
  696. '   Collision Stuff Ahead
  697. '**********************************************************************************************
  698.  
  699. Sub collisionCCHandle (m As tMANIFOLD, contacts() As tVECTOR2d, A As tBODY, B As tBODY)
  700.     Dim normal As tVECTOR2d
  701.     Dim dist_sqr As _Float
  702.     Dim radius As _Float
  703.  
  704.     Call vectorSubVectorND(normal, B.position, A.position)
  705.     dist_sqr = vectorLengthSq(normal)
  706.     radius = A.shape.radius + B.shape.radius
  707.  
  708.     If (dist_sqr >= radius * radius) Then
  709.         m.contactCount = 0
  710.     Else
  711.         Dim distance As _Float
  712.         distance = Sqr(dist_sqr)
  713.         m.contactCount = 1
  714.  
  715.         If distance = 0 Then
  716.             m.penetration = A.shape.radius
  717.             Call vectorSet(m.normal, 1.0, 0.0)
  718.             Call vectorSetVector(contacts(0), A.position)
  719.         Else
  720.             m.penetration = radius - distance
  721.             Call vectorDivideScalarND(m.normal, normal, distance)
  722.  
  723.             Call vectorMultiplyScalarND(contacts(0), m.normal, A.shape.radius)
  724.             Call vectorAddVector(contacts(0), A.position)
  725.         End If
  726.     End If
  727.  
  728. Sub collisionPCHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  729.     Call collisionCPHandle(p(), pa(), body(), m, contacts(), B, A)
  730.     If m.contactCount > 0 Then
  731.         Call vectorNeg(m.normal)
  732.     End If
  733.  
  734. Sub collisionCPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  735.     'A is the Circle
  736.     'B is the POLY
  737.     m.contactCount = 0
  738.     Dim center As tVECTOR2d
  739.     Dim tm As tMATRIX2d
  740.     Dim tv As tVECTOR2d
  741.     Dim ARadius As _Float: ARadius = body(A).shape.radius
  742.  
  743.     Call vectorSubVectorND(center, body(A).position, body(B).position)
  744.     Call matrixTranspose(body(B).shape.u, tm)
  745.     Call matrixMultiplyVector(tm, center, center)
  746.  
  747.     Dim separation As _Float: separation = -9999999
  748.     Dim faceNormal As Integer: faceNormal = 0
  749.     Dim i As Integer
  750.     Dim s As _Float
  751.     For i = 0 To pa(B).count
  752.         Call vectorSubVectorND(tv, center, p(pa(B).start + i).vert)
  753.         s = vectorDot(p(pa(B).start + i).norm, tv)
  754.         If s > ARadius Then Exit Sub
  755.         If s > separation Then
  756.             separation = s
  757.             faceNormal = i
  758.         End If
  759.     Next
  760.     Dim v1 As tVECTOR2d
  761.     v1 = p(pa(B).start + faceNormal).vert
  762.     Dim i2 As Integer
  763.     i2 = pa(B).start + arrayNextIndex(faceNormal, pa(B).count)
  764.     Dim v2 As tVECTOR2d
  765.     v2 = p(i2).vert
  766.  
  767.     If separation < cEPSILON Then
  768.         m.contactCount = 1
  769.         Call matrixMultiplyVector(body(B).shape.u, p(pa(B).start + faceNormal).norm, m.normal)
  770.         Call vectorNeg(m.normal)
  771.         Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  772.         Call vectorAddVector(contacts(0), body(A).position)
  773.         m.penetration = ARadius
  774.         Exit Sub
  775.     End If
  776.  
  777.     Dim dot1 As _Float
  778.     Dim dot2 As _Float
  779.  
  780.     Dim tv1 As tVECTOR2d
  781.     Dim tv2 As tVECTOR2d
  782.     Dim n As tVECTOR2d
  783.     Call vectorSubVectorND(tv1, center, v1)
  784.     Call vectorSubVectorND(tv2, v2, v1)
  785.     dot1 = vectorDot(tv1, tv2)
  786.     Call vectorSubVectorND(tv1, center, v2)
  787.     Call vectorSubVectorND(tv2, v1, v2)
  788.     dot2 = vectorDot(tv1, tv2)
  789.     m.penetration = ARadius - separation
  790.     If dot1 <= 0.0 Then
  791.         If vectorSqDist(center, v1) > ARadius * ARadius Then Exit Sub
  792.         m.contactCount = 1
  793.         Call vectorSubVectorND(n, v1, center)
  794.         Call matrixMultiplyVector(body(B).shape.u, n, n)
  795.         Call vectorNormalize(n)
  796.         m.normal = n
  797.         Call matrixMultiplyVector(body(B).shape.u, v1, v1)
  798.         Call vectorAddVectorND(v1, v1, body(B).position)
  799.         contacts(0) = v1
  800.     Else
  801.         If dot2 <= 0.0 Then
  802.             If vectorSqDist(center, v2) > ARadius * ARadius Then Exit Sub
  803.             m.contactCount = 1
  804.             Call vectorSubVectorND(n, v2, center)
  805.             Call matrixMultiplyVector(body(B).shape.u, v2, v2)
  806.             Call vectorAddVectorND(v2, v2, body(B).position)
  807.             contacts(0) = v2
  808.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  809.             Call vectorNormalize(n)
  810.             m.normal = n
  811.         Else
  812.             n = p(pa(B).start + faceNormal).norm
  813.             Call vectorSubVectorND(tv1, center, v1)
  814.             If vectorDot(tv1, n) > ARadius Then Exit Sub
  815.             m.contactCount = 1
  816.             Call matrixMultiplyVector(body(B).shape.u, n, n)
  817.             Call vectorNeg(n)
  818.             m.normal = n
  819.             Call vectorMultiplyScalarND(contacts(0), m.normal, ARadius)
  820.             Call vectorAddVector(contacts(0), body(A).position)
  821.         End If
  822.     End If
  823.  
  824. Function collisionPPClip (n As tVECTOR2d, c As _Float, face() As tVECTOR2d)
  825.     Dim sp As Integer: sp = 0
  826.     Dim o(cMAXNUMBEROFPOLYGONS) As tVECTOR2d
  827.  
  828.     o(0) = face(0)
  829.     o(1) = face(1)
  830.  
  831.     Dim d1 As _Float: d1 = vectorDot(n, face(0)) - c
  832.     Dim d2 As _Float: d2 = vectorDot(n, face(1)) - c
  833.  
  834.     If d1 <= 0.0 Then
  835.         o(sp) = face(0)
  836.         sp = sp + 1
  837.     End If
  838.  
  839.     If d2 <= 0.0 Then
  840.         o(sp) = face(1)
  841.         sp = sp + 1
  842.     End If
  843.  
  844.     If d1 * d2 < 0.0 Then
  845.         Dim alpha As _Float: alpha = d1 / (d1 - d2)
  846.         Dim tempv As tVECTOR2d
  847.         'out[sp] = face[0] + alpha * (face[1] - face[0]);
  848.         Call vectorSubVectorND(tempv, face(1), face(0))
  849.         Call vectorMultiplyScalar(tempv, alpha)
  850.         Call vectorAddVectorND(o(sp), tempv, face(0))
  851.         sp = sp + 1
  852.     End If
  853.     face(0) = o(0)
  854.     face(1) = o(1)
  855.     collisionPPClip = sp
  856.  
  857. Sub collisionPPFindIncidentFace (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, v() As tVECTOR2d, RefPoly As Integer, IncPoly As Integer, referenceIndex As Integer)
  858.     Dim referenceNormal As tVECTOR2d
  859.     Dim uRef As tMATRIX2d: uRef = b(RefPoly).shape.u
  860.     Dim uInc As tMATRIX2d: uInc = b(IncPoly).shape.u
  861.     Dim uTemp As tMATRIX2d
  862.     Dim i As Integer
  863.     referenceNormal = p(pa(RefPoly).start + referenceIndex).norm
  864.  
  865.     '        // Calculate normal in incident's frame of reference
  866.     '        // referenceNormal = RefPoly->u * referenceNormal; // To world space
  867.     Call matrixMultiplyVector(uRef, referenceNormal, referenceNormal)
  868.     '        // referenceNormal = IncPoly->u.Transpose( ) * referenceNormal; // To incident's model space
  869.     Call matrixTranspose(uInc, uTemp)
  870.     Call matrixMultiplyVector(uTemp, referenceNormal, referenceNormal)
  871.  
  872.     Dim incidentFace As Integer: incidentFace = 0
  873.     Dim minDot As _Float: minDot = 9999999
  874.     Dim dot As _Float
  875.     For i = 0 To pa(IncPoly).count
  876.         dot = vectorDot(referenceNormal, p(pa(IncPoly).start + i).norm)
  877.         If (dot < minDot) Then
  878.             minDot = dot
  879.             incidentFace = i
  880.         End If
  881.     Next
  882.  
  883.     '// Assign face vertices for incidentFace
  884.     '// v[0] = IncPoly->u * IncPoly->m_vertices[incidentFace] + IncPoly->body->position;
  885.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(0))
  886.     Call vectorAddVector(v(0), b(IncPoly).position)
  887.  
  888.     '// incidentFace = incidentFace + 1 >= (int32)IncPoly->m_vertexCount ? 0 : incidentFace + 1;
  889.     incidentFace = arrayNextIndex(incidentFace, pa(IncPoly).count)
  890.  
  891.     '// v[1] = IncPoly->u * IncPoly->m_vertices[incidentFace] +  IncPoly->body->position;
  892.     Call matrixMultiplyVector(uInc, p(pa(IncPoly).start + incidentFace).vert, v(1))
  893.     Call vectorAddVector(v(1), b(IncPoly).position)
  894.  
  895. Sub collisionPPHandle (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, m As tMANIFOLD, contacts() As tVECTOR2d, A As Integer, B As Integer)
  896.     m.contactCount = 0
  897.  
  898.     Dim faceA(100) As Integer
  899.  
  900.     Dim penetrationA As _Float
  901.     penetrationA = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceA(), A, B)
  902.     If penetrationA >= 0.0 Then Exit Sub
  903.  
  904.     Dim faceB(100) As Integer
  905.  
  906.     Dim penetrationB As _Float
  907.     penetrationB = collisionPPFindAxisLeastPenetration(p(), pa(), body(), faceB(), B, A)
  908.     If penetrationB >= 0.0 Then Exit Sub
  909.  
  910.  
  911.     Dim referenceIndex As Integer
  912.     Dim flip As Integer
  913.  
  914.     Dim RefPoly As Integer
  915.     Dim IncPoly As Integer
  916.  
  917.     If impulseGT(penetrationA, penetrationB) Then
  918.         RefPoly = A
  919.         IncPoly = B
  920.         referenceIndex = faceA(0)
  921.         flip = 0
  922.     Else
  923.         RefPoly = B
  924.         IncPoly = A
  925.         referenceIndex = faceB(0)
  926.         flip = 1
  927.     End If
  928.  
  929.     Dim incidentFace(2) As tVECTOR2d
  930.  
  931.     Call collisionPPFindIncidentFace(p(), pa(), body(), incidentFace(), RefPoly, IncPoly, referenceIndex)
  932.     Dim v1 As tVECTOR2d
  933.     Dim v2 As tVECTOR2d
  934.     Dim v1t As tVECTOR2d
  935.     Dim v2t As tVECTOR2d
  936.  
  937.     v1 = p(pa(RefPoly).start + referenceIndex).vert
  938.     referenceIndex = arrayNextIndex(referenceIndex, pa(RefPoly).count)
  939.     v2 = p(pa(RefPoly).start + referenceIndex).vert
  940.     '// Transform vertices to world space
  941.     '// v1 = RefPoly->u * v1 + RefPoly->body->position;
  942.     '// v2 = RefPoly->u * v2 + RefPoly->body->position;
  943.     Call matrixMultiplyVector(body(RefPoly).shape.u, v1, v1t)
  944.     Call vectorAddVectorND(v1, v1t, body(RefPoly).position)
  945.     Call matrixMultiplyVector(body(RefPoly).shape.u, v2, v2t)
  946.     Call vectorAddVectorND(v2, v2t, body(RefPoly).position)
  947.  
  948.     '// Calculate reference face side normal in world space
  949.     '// Vec2 sidePlaneNormal = (v2 - v1);
  950.     '// sidePlaneNormal.Normalize( );
  951.     Dim sidePlaneNormal As tVECTOR2d
  952.     Call vectorSubVectorND(sidePlaneNormal, v2, v1)
  953.     Call vectorNormalize(sidePlaneNormal)
  954.  
  955.     '// Orthogonalize
  956.     '// Vec2 refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );
  957.     Dim refFaceNormal As tVECTOR2d
  958.     Call vectorSet(refFaceNormal, sidePlaneNormal.y, -sidePlaneNormal.x)
  959.  
  960.     '// ax + by = c
  961.     '// c is distance from origin
  962.     '// real refC = Dot( refFaceNormal, v1 );
  963.     '// real negSide = -Dot( sidePlaneNormal, v1 );
  964.     '// real posSide = Dot( sidePlaneNormal, v2 );
  965.     Dim refC As _Float: refC = vectorDot(refFaceNormal, v1)
  966.     Dim negSide As _Float: negSide = -vectorDot(sidePlaneNormal, v1)
  967.     Dim posSide As _Float: posSide = vectorDot(sidePlaneNormal, v2)
  968.  
  969.  
  970.     '// Clip incident face to reference face side planes
  971.     '// if(Clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
  972.     Dim negSidePlaneNormal As tVECTOR2d
  973.     Call vectorNegND(negSidePlaneNormal, sidePlaneNormal)
  974.  
  975.     If collisionPPClip(negSidePlaneNormal, negSide, incidentFace()) < 2 Then Exit Sub
  976.     If collisionPPClip(sidePlaneNormal, posSide, incidentFace()) < 2 Then Exit Sub
  977.  
  978.     Call vectorSet(m.normal, refFaceNormal.x, refFaceNormal.y)
  979.     If flip Then Call vectorNeg(m.normal)
  980.  
  981.     '// Keep points behind reference face
  982.     Dim cp As Integer: cp = 0 '// clipped points behind reference face
  983.     Dim separation As _Float
  984.     separation = vectorDot(refFaceNormal, incidentFace(0)) - refC
  985.     If separation <= 0.0 Then
  986.         contacts(cp) = incidentFace(0)
  987.         m.penetration = -separation
  988.         cp = cp + 1
  989.     Else
  990.         m.penetration = 0
  991.     End If
  992.  
  993.     separation = vectorDot(refFaceNormal, incidentFace(1)) - refC
  994.     If separation <= 0.0 Then
  995.         contacts(cp) = incidentFace(1)
  996.         m.penetration = m.penetration + -separation
  997.         cp = cp + 1
  998.         m.penetration = m.penetration / cp
  999.     End If
  1000.     m.contactCount = cp
  1001.  
  1002. Function collisionPPFindAxisLeastPenetration (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, faceIndex() As Integer, A As Integer, B As Integer)
  1003.     Dim bestDistance As _Float: bestDistance = -9999999
  1004.     Dim bestIndex As Integer: bestIndex = 0
  1005.  
  1006.     Dim n As tVECTOR2d
  1007.     Dim nw As tVECTOR2d
  1008.     Dim buT As tMATRIX2d
  1009.     Dim s As tVECTOR2d
  1010.     Dim nn As tVECTOR2d
  1011.     Dim v As tVECTOR2d
  1012.     Dim tv As tVECTOR2d
  1013.     Dim d As _Float
  1014.     Dim i, k As Integer
  1015.  
  1016.     For i = 0 To pa(A).count
  1017.         k = pa(A).start + i
  1018.  
  1019.         '// Retrieve a face normal from A
  1020.         '// Vec2 n = A->m_normals[i];
  1021.         '// Vec2 nw = A->u * n;
  1022.         n = p(k).norm
  1023.         Call matrixMultiplyVector(body(A).shape.u, n, nw)
  1024.  
  1025.  
  1026.         '// Transform face normal into B's model space
  1027.         '// Mat2 buT = B->u.Transpose( );
  1028.         '// n = buT * nw;
  1029.         Call matrixTranspose(body(B).shape.u, buT)
  1030.         Call matrixMultiplyVector(buT, nw, n)
  1031.  
  1032.         '// Retrieve support point from B along -n
  1033.         '// Vec2 s = B->GetSupport( -n );
  1034.         Call vectorNegND(nn, n)
  1035.         Call vectorGetSupport(p(), pa(), B, nn, s)
  1036.  
  1037.         '// Retrieve vertex on face from A, transform into
  1038.         '// B's model space
  1039.         '// Vec2 v = A->m_vertices[i];
  1040.         '// v = A->u * v + A->body->position;
  1041.         '// v -= B->body->position;
  1042.         '// v = buT * v;
  1043.  
  1044.         v = p(k).vert
  1045.         Call matrixMultiplyVector(body(A).shape.u, v, tv)
  1046.         Call vectorAddVectorND(v, tv, body(A).position)
  1047.  
  1048.         Call vectorSubVector(v, body(B).position)
  1049.         Call matrixMultiplyVector(buT, v, tv)
  1050.  
  1051.         Call vectorSubVector(s, tv)
  1052.         d = vectorDot(n, s)
  1053.  
  1054.         If d > bestDistance Then
  1055.             bestDistance = d
  1056.             bestIndex = i
  1057.         End If
  1058.  
  1059.     Next i
  1060.  
  1061.     faceIndex(0) = bestIndex
  1062.  
  1063.     collisionPPFindAxisLeastPenetration = bestDistance
  1064.  
  1065. '**********************************************************************************************
  1066. '   Shape Creation Ahead
  1067. '**********************************************************************************************
  1068.  
  1069. Sub shapeCreate (sh As tSHAPE, ty As Integer, radius As _Float)
  1070.     Dim u As tMATRIX2d
  1071.     Call matrixSetScalar(u, 1, 0, 0, 1)
  1072.     sh.ty = ty
  1073.     sh.radius = radius
  1074.     sh.u = u
  1075.  
  1076. Sub bodyCreate (body() As tBODY, index As Integer, shape As tSHAPE)
  1077.     Call vectorSet(body(index).position, 0, 0)
  1078.     Call vectorSet(body(index).velocity, 0, 0)
  1079.     body(index).angularVelocity = 0.0
  1080.     body(index).torque = 0.0
  1081.     body(index).orient = 0.0 ' impulseRandom_float(-cPI, cPI)
  1082.  
  1083.     Call vectorSet(body(index).force, 0, 0)
  1084.     body(index).staticFriction = 0.5
  1085.     body(index).dynamicFriction = 0.3
  1086.     body(index).restitution = 0.2
  1087.     body(index).shape = shape
  1088.  
  1089. Sub boxCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  1090.     Dim vertlength As Integer: vertlength = 3
  1091.     Dim verts(vertlength) As tVECTOR2d
  1092.  
  1093.     Call vectorSet(verts(0), -sizex, -sizey)
  1094.     Call vectorSet(verts(1), sizex, -sizey)
  1095.     Call vectorSet(verts(2), sizex, sizey)
  1096.     Call vectorSet(verts(3), -sizex, sizey)
  1097.  
  1098.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  1099.  
  1100. Sub vShapeCreate (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, sizex As _Float, sizey As _Float)
  1101.     Dim vertlength As Integer: vertlength = 7
  1102.     Dim verts(vertlength) As tVECTOR2d
  1103.  
  1104.     Call vectorSet(verts(0), -sizex, -sizey)
  1105.     Call vectorSet(verts(1), sizex, -sizey)
  1106.     Call vectorSet(verts(2), sizex, sizey)
  1107.     Call vectorSet(verts(3), -sizex, sizey)
  1108.     Call vectorSet(verts(4), -sizex, sizey / 2)
  1109.     Call vectorSet(verts(5), sizex / 2, sizey / 2)
  1110.     Call vectorSet(verts(6), sizex / 2, -sizey / 2)
  1111.     Call vectorSet(verts(7), -sizex, sizey / 2)
  1112.  
  1113.     Call vertexSet(p(), pa(), index, verts(), vertlength)
  1114.  
  1115. Sub vertexSet (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, verts() As tVECTOR2d, vertLength As Integer)
  1116.     Dim rightMost As Integer: rightMost = 0
  1117.     Dim highestXCoord As _Float: highestXCoord = verts(0).x
  1118.     Dim i As Integer
  1119.     Dim x As _Float
  1120.     For i = 1 To vertLength
  1121.         x = verts(i).x
  1122.         If x > highestXCoord Then
  1123.             highestXCoord = x
  1124.             rightMost = i
  1125.         Else
  1126.             If x = highestXCoord Then
  1127.                 If verts(i).y < verts(rightMost).y Then
  1128.                     rightMost = i
  1129.                 End If
  1130.             End If
  1131.         End If
  1132.     Next
  1133.     Dim hull(cMAXNUMBEROFPOLYGONS) As Integer
  1134.     Dim outCount As Integer: outCount = 0
  1135.     Dim indexHull As Integer: indexHull = rightMost
  1136.     Dim nextHullIndex As Integer
  1137.     Dim e1 As tVECTOR2d
  1138.     Dim e2 As tVECTOR2d
  1139.     Dim c As _Float
  1140.     Do
  1141.         hull(outCount) = indexHull
  1142.         nextHullIndex = 0
  1143.         For i = 1 To vertLength
  1144.             If nextHullIndex = indexHull Then
  1145.                 nextHullIndex = i
  1146.                 _Continue
  1147.             End If
  1148.             Call vectorSubVectorND(e1, verts(nextHullIndex), verts(hull(outCount)))
  1149.             Call vectorSubVectorND(e2, verts(i), verts(hull(outCount)))
  1150.             c = vectorCross(e1, e2)
  1151.             If c < 0.0 Then nextHullIndex = i
  1152.             If c = 0.0 And (vectorLengthSq(e2) > vectorLengthSq(e1)) Then
  1153.                 nextHullIndex = i
  1154.             End If
  1155.         Next
  1156.         outCount = outCount + 1
  1157.         indexHull = nextHullIndex
  1158.         If nextHullIndex = rightMost Then
  1159.             pa(index).count = outCount - 1
  1160.             Exit Do
  1161.         End If
  1162.     Loop
  1163.  
  1164.     If index = 0 Then
  1165.         pa(index).start = 0
  1166.     Else
  1167.         pa(index).start = pa(index - 1).start + pa(index - 1).count + 1
  1168.     End If
  1169.  
  1170.     For i = 0 To vertLength
  1171.         p(pa(index).start + i).vert = verts(hull(i))
  1172.     Next
  1173.     Dim face As tVECTOR2d
  1174.     For i = 0 To vertLength
  1175.         Call vectorSubVectorND(face, p(pa(index).start + arrayNextIndex(i, pa(index).count)).vert, p(pa(index).start + i).vert)
  1176.         Call vectorSet(p(pa(index).start + i).norm, face.y, -face.x)
  1177.         Call vectorNormalize(p(pa(index).start + i).norm)
  1178.     Next
  1179.  
  1180. Function arrayNextIndex (i As Integer, count As Integer)
  1181.     arrayNextIndex = ((i + 1) Mod (count + 1))
  1182.  
  1183. '**********************************************************************************************
  1184. '   Rendering Stuff Ahead
  1185. '**********************************************************************************************
  1186.  
  1187. Sub renderFps
  1188.     fpsLast = fps
  1189.     fps = 0
  1190.  
  1191. Sub renderBodies (p() As tPOLY, pa() As tPOLYATTRIB, body() As tBODY, j() As tJOINT, hits() As tHIT)
  1192.     Dim i As Integer
  1193.     Dim hitcount As Integer
  1194.  
  1195.     For i = 0 To sNUMBEROFBODIES
  1196.         'TODO: Put hidden object logic
  1197.         If body(i).shape.ty = cSHAPE_CIRCLE Then
  1198.             If body(i).shape.texture = 0 Then
  1199.                 Call renderWireframeCircle(body(), i)
  1200.             Else
  1201.                 Call renderTexturedCircle(body(), i)
  1202.             End If
  1203.         Else If body(i).shape.ty = cSHAPE_POLYGON Then
  1204.                 If body(i).shape.texture = 0 Then
  1205.                     Call renderWireframePoly(p(), pa(), body(), i)
  1206.                 Else
  1207.                     Call renderTexturedBox(p(), pa(), body(), i)
  1208.                 End If
  1209.             End If
  1210.         End If
  1211.         If body(i).position.x < 0 Then body(i).position.x = _Width
  1212.         If body(i).position.x > _Width Then body(i).position.x = 0
  1213.         ' If b(index).position.y < 0 Then b(index).position.y = _Height
  1214.         If body(i).position.y > _Height Then body(i).position.y = 0
  1215.     Next
  1216.     If cRENDER_JOINTS Then
  1217.         For i = 1 To sNUMBEROFJOINTS
  1218.             Call renderJoints(j(i), body())
  1219.         Next
  1220.     End If
  1221.     If cRENDER_COLLISIONS Then
  1222.         hitcount = 0
  1223.         Do While hits(hitcount).A <> hits(hitcount).B
  1224.             Circle (hits(hitcount).position.x, hits(hitcount).position.y), 5, _RGB(255, 6, 11)
  1225.             hitcount = hitcount + 1
  1226.         Loop
  1227.     End If
  1228.  
  1229.  
  1230. Sub renderJoints (j As tJOINT, b() As tBODY)
  1231.     Dim b1 As tBODY: b1 = b(j.body1)
  1232.     Dim b2 As tBODY: b2 = b(j.body2)
  1233.     Dim R1 As tMATRIX2d: R1 = b1.shape.u 'Call matrixSetRadians(R1, b1.orient)
  1234.     Dim R2 As tMATRIX2d: R2 = b2.shape.u ' Call matrixSetRadians(R2, b2.orient)
  1235.  
  1236.     Dim x1 As tVECTOR2d: x1 = b1.position
  1237.     Dim p1 As tVECTOR2d: Call matrixMultiplyVector(R1, j.localAnchor1, p1)
  1238.  
  1239.     Call vectorAddVectorND(p1, p1, x1)
  1240.  
  1241.     Dim x2 As tVECTOR2d: x2 = b2.position
  1242.     Dim p2 As tVECTOR2d: Call matrixMultiplyVector(R2, j.localAnchor2, p2)
  1243.  
  1244.     Call vectorAddVectorND(p2, p2, x2)
  1245.  
  1246.     'Line (x1.x, x1.y)-(p1.x, p1.y), _RGB(127, 127, 244) 'blue
  1247.     Line (p1.x, p1.y)-(x2.x, x2.y), _RGB(255, 255, 127) 'yellow
  1248.     ' Line (x2.x, x2.y)-(p2.x, p2.y), _RGB32(127, 255, 127) 'green
  1249.     ' Line (p2.x, p2.y)-(x1.x, x1.y), _RGB(127, 6, 127) 'purple
  1250.  
  1251. Sub renderWireframePoly (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, index As Integer)
  1252.     Dim a As tVECTOR2d ' dummy vertices
  1253.     Dim b As tVECTOR2d
  1254.  
  1255.     Dim i, element, element_next As Integer
  1256.     For i = 0 To pa(index).count
  1257.         element = pa(index).start + i
  1258.         element_next = pa(index).start + arrayNextIndex(i, pa(index).count) ' wrap around back to first element
  1259.         a = p(element).vert
  1260.         b = p(element_next).vert
  1261.  
  1262.         Call matrixMultiplyVector(b(index).shape.u, a, a)
  1263.         Call matrixMultiplyVector(b(index).shape.u, b, b)
  1264.         Call vectorAddVector(a, b(index).position)
  1265.         Call vectorAddVector(b, b(index).position)
  1266.  
  1267.         Line (a.x, a.y)-(b.x, b.y), b(index).c
  1268.     Next
  1269.  
  1270. Sub renderTexturedBox (p() As tPOLY, pa() As tPOLYATTRIB, b() As tBODY, index As Integer)
  1271.     Dim vert(3) As tVECTOR2d
  1272.     Dim W, H As Integer
  1273.     Dim bm As Long
  1274.     bm = b(index).shape.texture
  1275.     W = _Width(bm): H = _Height(bm)
  1276.     Dim i As Integer
  1277.     For i = 0 To 3
  1278.         vert(i) = p(pa(index).start + i).vert
  1279.         Call matrixMultiplyVector(b(index).shape.u, vert(i), vert(i))
  1280.         Call vectorAddVector(vert(i), b(index).position)
  1281.     Next
  1282.     If b(index).velocity.x > 1 Or b(index).shape.flipTexture = 0 Then
  1283.         _MapTriangle (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm To(vert(3).x, vert(3).y)-(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)
  1284.         _MapTriangle (0, 0)-(0, H - 1)-(W - 1, H - 1), bm To(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)-(vert(1).x, vert(1).y)
  1285.     Else
  1286.         _MapTriangle (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1287.         _MapTriangle (0, 0)-(0, H - 1)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1288.     End If
  1289.  
  1290.  
  1291. Sub renderWireframeCircle (b() As tBODY, index As Integer)
  1292.     Circle (b(index).position.x, b(index).position.y), b(index).shape.radius, b(index).c
  1293.     Line (b(index).position.x, b(index).position.y)-(b(index).position.x + Cos(b(index).orient) * b(index).shape.radius, b(index).position.y + Sin(b(index).orient) * b(index).shape.radius), b(index).c
  1294.  
  1295. Sub renderTexturedCircle (b() As tBODY, index As Integer)
  1296.     Dim vert(3) As tVECTOR2d
  1297.     Dim W, H As Integer
  1298.     Dim bm As Long
  1299.     bm = b(index).shape.texture
  1300.     W = _Width(bm): H = _Height(bm)
  1301.     Call vectorSet(vert(0), -b(index).shape.radius, -b(index).shape.radius)
  1302.     Call vectorSet(vert(1), -b(index).shape.radius, b(index).shape.radius)
  1303.     Call vectorSet(vert(2), b(index).shape.radius, b(index).shape.radius)
  1304.     Call vectorSet(vert(3), b(index).shape.radius, -b(index).shape.radius)
  1305.     Dim i As Integer
  1306.     For i = 0 To 3
  1307.         Call matrixMultiplyVector(b(index).shape.u, vert(i), vert(i))
  1308.         Call vectorAddVector(vert(i), b(index).position)
  1309.     Next
  1310.     _MapTriangle (0, 0)-(0, H - 1)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(1).x, vert(1).y)-(vert(2).x, vert(2).y)
  1311.     _MapTriangle (0, 0)-(W - 1, 0)-(W - 1, H - 1), bm To(vert(0).x, vert(0).y)-(vert(3).x, vert(3).y)-(vert(2).x, vert(2).y)
  1312.  
  1313. Sub polygonSetOrient (b As tBODY, radians As _Float)
  1314.     Call matrixSetRadians(b.shape.u, radians)
  1315.  
  1316. '**********************************************************************************************
  1317. '   Object initialization Ahead
  1318. '**********************************************************************************************
  1319.  
  1320. Sub circleInitialize (b() As tBODY, index As Integer)
  1321.     Call circleComputeMass(b(), index, 1.0)
  1322.  
  1323. Sub circleComputeMass (b() As tBODY, index As Integer, density As _Float)
  1324.     b(index).mass = cPI * b(index).shape.radius * b(index).shape.radius * density
  1325.     If b(index).mass <> 0 Then
  1326.         b(index).invMass = 1.0 / b(index).mass
  1327.     Else
  1328.         b(index).invMass = 0.0
  1329.     End If
  1330.  
  1331.     b(index).inertia = b(index).mass * b(index).shape.radius * b(index).shape.radius
  1332.  
  1333.     If b(index).inertia <> 0 Then
  1334.         b(index).invInertia = 1.0 / b(index).inertia
  1335.     Else
  1336.         b(index).invInertia = 0.0
  1337.     End If
  1338.  
  1339. Sub polygonInitialize (body() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer)
  1340.     Call polygonComputeMass(body(), p(), pa(), index, 1.0)
  1341.  
  1342. Sub polygonComputeMass (b() As tBODY, p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, density As _Float)
  1343.     Dim c As tVECTOR2d ' centroid
  1344.     Dim p1 As tVECTOR2d
  1345.     Dim p2 As tVECTOR2d
  1346.     Dim area As _Float
  1347.     Dim I As _Float
  1348.     Dim k_inv3 As _Float
  1349.     Dim D As _Float
  1350.     Dim triangleArea As _Float
  1351.     Dim weight As _Float
  1352.     Dim intx2 As _Float
  1353.     Dim inty2 As _Float
  1354.     Dim ii As Integer
  1355.  
  1356.     k_inv3 = 1.0 / 3.0
  1357.  
  1358.     For ii = 0 To pa(index).count
  1359.         p1 = p(pa(index).start + ii).vert
  1360.         p2 = p(pa(index).start + arrayNextIndex(ii, pa(index).count)).vert
  1361.         D = vectorCross(p1, p2)
  1362.         triangleArea = .5 * D
  1363.         area = area + triangleArea
  1364.         weight = triangleArea * k_inv3
  1365.         Call vectorAddVectorScalar(c, p1, weight)
  1366.         Call vectorAddVectorScalar(c, p2, weight)
  1367.         intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x
  1368.         inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y
  1369.         I = I + (0.25 * k_inv3 * D) * (intx2 + inty2)
  1370.     Next ii
  1371.  
  1372.     Call vectorMultiplyScalar(c, 1.0 / area)
  1373.  
  1374.     For ii = 0 To pa(index).count
  1375.         Call vectorSubVector(p(pa(index).start + ii).vert, c)
  1376.     Next
  1377.  
  1378.     b(index).mass = density * area
  1379.     If b(index).mass <> 0.0 Then
  1380.         b(index).invMass = 1.0 / b(index).mass
  1381.     Else
  1382.         b(index).invMass = 0.0
  1383.     End If
  1384.  
  1385.     b(index).inertia = I * density
  1386.     If b(index).inertia <> 0 Then
  1387.         b(index).invInertia = 1.0 / b(index).inertia
  1388.     Else
  1389.         b(index).invInertia = 0.0
  1390.     End If
  1391. '**********************************************************************************************
  1392. '   Joint Stuff Ahead
  1393. '**********************************************************************************************
  1394. Sub jointSet (j As tJOINT, body() As tBODY, b1 As Integer, b2 As Integer, x As _Float, y As _Float)
  1395.     Dim anchor As tVECTOR2d
  1396.     Call vectorSet(anchor, x, y)
  1397.     Dim Rot1 As tMATRIX2d: Rot1 = body(b1).shape.u 'Call matrixSetRadians(Rot1, body(b1).orient)
  1398.     Dim Rot2 As tMATRIX2d: Rot2 = body(b2).shape.u 'Call matrixSetRadians(Rot2, body(b2).orient)
  1399.     Dim Rot1T As tMATRIX2d: Call matrixTranspose(Rot1, Rot1T)
  1400.     Dim Rot2T As tMATRIX2d: Call matrixTranspose(Rot2, Rot2T)
  1401.     Dim tv As tVECTOR2d
  1402.  
  1403.     j.body1 = b1
  1404.     j.body2 = b2
  1405.  
  1406.     Call vectorSubVectorND(tv, anchor, body(b1).position)
  1407.     Call matrixMultiplyVector(Rot1T, tv, j.localAnchor1)
  1408.  
  1409.     Call vectorSubVectorND(tv, anchor, body(b2).position)
  1410.     Call matrixMultiplyVector(Rot2T, tv, j.localAnchor2)
  1411.  
  1412.     Call vectorSet(j.P, 0, 0)
  1413.  
  1414.     j.softness = 0
  1415.     j.biasFactor = 100
  1416.  
  1417.  
  1418. Sub jointPrestep (j As tJOINT, body() As tBODY, inv_dt As _Float)
  1419.     Dim Rot1 As tMATRIX2d: Rot1 = body(j.body1).shape.u 'Call matrixSetRadians(Rot1, body(j.body1).orient)
  1420.     Dim Rot2 As tMATRIX2d: Rot2 = body(j.body2).shape.u 'Call matrixSetRadians(Rot2, body(j.body2).orient)
  1421.     Dim b1invMass As _Float
  1422.     Dim b2invMass As _Float
  1423.  
  1424.     Dim b1invInertia As _Float
  1425.     Dim b2invInertia As _Float
  1426.  
  1427.     Call matrixMultiplyVector(Rot1, j.localAnchor1, j.r1)
  1428.     Call matrixMultiplyVector(Rot2, j.localAnchor2, j.r2)
  1429.  
  1430.     b1invMass = body(j.body1).invMass
  1431.     b2invMass = body(j.body2).invMass
  1432.  
  1433.     b1invInertia = body(j.body1).invInertia
  1434.     b2invInertia = body(j.body2).invInertia
  1435.  
  1436.     Dim K1 As tMATRIX2d
  1437.     Call matrixSetScalar(K1, b1invMass + b2invMass, 0,_
  1438.                                                 0, b1invMass + b2invMass)
  1439.     Dim K2 As tMATRIX2d
  1440.     Call matrixSetScalar(K2, b1invInertia * j.r1.y * j.r1.y, -b1invInertia * j.r1.x * j.r1.y,_
  1441.                             -b1invInertia * j.r1.x * j.r1.y,  b1invInertia * j.r1.x * j.r1.x)
  1442.  
  1443.     Dim K3 As tMATRIX2d
  1444.     Call matrixSetScalar(K3,  b2invInertia * j.r2.y * j.r2.y, - b2invInertia * j.r2.x * j.r2.y,_
  1445.                              -b2invInertia * j.r2.x * j.r2.y,   b2invInertia * j.r2.x * j.r2.x)
  1446.  
  1447.     Dim K As tMATRIX2d
  1448.     Call matrixAddMatrix(K1, K2, K)
  1449.     Call matrixAddMatrix(K3, K, K)
  1450.     K.m00 = K.m00 + j.softness
  1451.     K.m11 = K.m11 + j.softness
  1452.     Call matrixInvert(K, j.M)
  1453.  
  1454.     Dim p1 As tVECTOR2d: Call vectorAddVectorND(p1, body(j.body1).position, j.r1)
  1455.     Dim p2 As tVECTOR2d: Call vectorAddVectorND(p2, body(j.body2).position, j.r2)
  1456.     Dim dp As tVECTOR2d: Call vectorSubVectorND(dp, p2, p1)
  1457.  
  1458.     Call vectorMultiplyScalarND(j.bias, dp, -j.biasFactor * inv_dt)
  1459.     'Call vectorSet(j.bias, 0, 0)
  1460.     Call vectorSet(j.P, 0, 0)
  1461.  
  1462. Sub jointApplyImpulse (j As tJOINT, body() As tBODY)
  1463.     Dim dv As tVECTOR2d
  1464.     Dim impulse As tVECTOR2d
  1465.     Dim cross1 As tVECTOR2d
  1466.     Dim cross2 As tVECTOR2d
  1467.     Dim tv As tVECTOR2d
  1468.  
  1469.  
  1470.     'Vec2 dv = body2->velocity + Cross(body2->angularVelocity, r2) - body1->velocity - Cross(body1->angularVelocity, r1);
  1471.     Call vectorCrossScalar(cross2, j.r2, body(j.body2).angularVelocity)
  1472.     Call vectorCrossScalar(cross1, j.r1, body(j.body1).angularVelocity)
  1473.     Call vectorAddVectorND(dv, body(j.body2).velocity, cross2)
  1474.     Call vectorSubVectorND(dv, dv, body(j.body1).velocity)
  1475.     Call vectorSubVectorND(dv, dv, cross1)
  1476.  
  1477.     ' impulse = M * (bias - dv - softness * P);
  1478.     Call vectorMultiplyScalarND(tv, j.P, j.softness)
  1479.     Call vectorSubVectorND(impulse, j.bias, dv)
  1480.     Call vectorSubVectorND(impulse, impulse, tv)
  1481.     Call matrixMultiplyVector(j.M, impulse, impulse)
  1482.  
  1483.     ' body1->velocity -= body1->invMass * impulse;
  1484.  
  1485.     Call vectorMultiplyScalarND(tv, impulse, body(j.body1).invMass)
  1486.     Call vectorSubVectorND(body(j.body1).velocity, body(j.body1).velocity, tv)
  1487.  
  1488.     ' body1->angularVelocity -= body1->invI * Cross(r1, impulse);
  1489.     Dim crossScalar As _Float
  1490.     crossScalar = vectorCross(j.r1, impulse)
  1491.     body(j.body1).angularVelocity = body(j.body1).angularVelocity - body(j.body1).invInertia * crossScalar
  1492.  
  1493.     Call vectorMultiplyScalarND(tv, impulse, body(j.body2).invMass)
  1494.     Call vectorAddVectorND(body(j.body2).velocity, body(j.body2).velocity, tv)
  1495.  
  1496.     crossScalar = vectorCross(j.r2, impulse)
  1497.     body(j.body2).angularVelocity = body(j.body2).angularVelocity + body(j.body2).invInertia * crossScalar
  1498.  
  1499.     Call vectorAddVectorND(j.P, j.P, impulse)
  1500.  
  1501.  
  1502. '**********************************************************************************************
  1503. '   Vector Math Ahead
  1504. '**********************************************************************************************
  1505.  
  1506. Sub vectorSet (v As tVECTOR2d, x As _Float, y As _Float)
  1507.     v.x = x
  1508.     v.y = y
  1509.  
  1510. Sub vectorSetVector (o As tVECTOR2d, v As tVECTOR2d)
  1511.     o.x = v.x
  1512.     o.y = v.y
  1513.  
  1514. Sub vectorNeg (v As tVECTOR2d)
  1515.     v.x = -v.x
  1516.     v.y = -v.y
  1517.  
  1518. Sub vectorNegND (o As tVECTOR2d, v As tVECTOR2d)
  1519.     o.x = -v.x
  1520.     o.y = -v.y
  1521.  
  1522. Sub vectorMultiplyScalar (v As tVECTOR2d, s As _Float)
  1523.     v.x = v.x * s
  1524.     v.y = v.y * s
  1525.  
  1526. Sub vectorMultiplyScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1527.     o.x = v.x * s
  1528.     o.y = v.y * s
  1529.  
  1530. Sub vectorDivideScalar (v As tVECTOR2d, s As _Float)
  1531.     v.x = v.x / s
  1532.     v.y = v.y / s
  1533.  
  1534. Sub vectorDivideScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1535.     o.x = v.x / s
  1536.     o.y = v.y / s
  1537.  
  1538. Sub vectorAddScalar (v As tVECTOR2d, s As _Float)
  1539.     v.x = v.x + s
  1540.     v.y = v.y + s
  1541.  
  1542. Sub vectorAddScalarND (o As tVECTOR2d, v As tVECTOR2d, s As _Float)
  1543.     o.x = v.x + s
  1544.     o.y = v.y + s
  1545.  
  1546. Sub vectorMultiplyVector (v As tVECTOR2d, m As tVECTOR2d)
  1547.     v.x = v.x * m.x
  1548.     v.y = v.y * m.y
  1549.  
  1550. Sub vectorMultiplyVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1551.     o.x = v.x * m.x
  1552.     o.y = v.y * m.y
  1553.  
  1554. Sub vectorDivideVector (v As tVECTOR2d, m As tVECTOR2d)
  1555.     v.x = v.x / m.x
  1556.     v.y = v.y / m.y
  1557.  
  1558. Sub vectorAddVector (v As tVECTOR2d, m As tVECTOR2d)
  1559.     v.x = v.x + m.x
  1560.     v.y = v.y + m.y
  1561.  
  1562. Sub vectorAddVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1563.     o.x = v.x + m.x
  1564.     o.y = v.y + m.y
  1565.  
  1566. Sub vectorAddVectorScalar (v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1567.     v.x = v.x + m.x * s
  1568.     v.y = v.y + m.y * s
  1569.  
  1570. Sub vectorAddVectorScalarND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d, s As _Float)
  1571.     o.x = v.x + m.x * s
  1572.     o.y = v.y + m.y * s
  1573.  
  1574. Sub vectorSubVector (v As tVECTOR2d, m As tVECTOR2d)
  1575.     v.x = v.x - m.x
  1576.     v.y = v.y - m.y
  1577.  
  1578. Sub vectorSubVectorND (o As tVECTOR2d, v As tVECTOR2d, m As tVECTOR2d)
  1579.     o.x = v.x - m.x
  1580.     o.y = v.y - m.y
  1581.  
  1582. Function vectorLengthSq (v As tVECTOR2d)
  1583.     vectorLengthSq = v.x * v.x + v.y * v.y
  1584.  
  1585. Function vectorLength (v As tVECTOR2d)
  1586.     vectorLength = Sqr(vectorLengthSq(v))
  1587.  
  1588. Sub vectorRotate (v As tVECTOR2d, radians As _Float)
  1589.     Dim c, s, xp, yp As _Float
  1590.     c = Cos(radians)
  1591.     s = Sin(radians)
  1592.     xp = v.x * c - v.y * s
  1593.     yp = v.x * s + v.y * c
  1594.     v.x = xp
  1595.     v.y = yp
  1596.  
  1597. Sub vectorNormalize (v As tVECTOR2d)
  1598.     Dim lenSQ, invLen As _Float
  1599.     lenSQ = vectorLengthSq(v)
  1600.     If lenSQ > cEPSILON_SQ Then
  1601.         invLen = 1.0 / Sqr(lenSQ)
  1602.         v.x = v.x * invLen
  1603.         v.y = v.y * invLen
  1604.     End If
  1605.  
  1606. Sub vectorMin (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1607.     o.x = scalarMin(a.x, b.x)
  1608.     o.y = scalarMin(a.y, b.y)
  1609.  
  1610. Sub vectorMax (a As tVECTOR2d, b As tVECTOR2d, o As tVECTOR2d)
  1611.     o.x = scalarMax(a.x, b.x)
  1612.     o.y = scalarMax(a.y, b.y)
  1613.  
  1614. Function vectorDot (a As tVECTOR2d, b As tVECTOR2d)
  1615.     vectorDot = a.x * b.x + a.y * b.y
  1616.  
  1617. Function vectorSqDist (a As tVECTOR2d, b As tVECTOR2d)
  1618.     Dim dx, dy As _Float
  1619.     dx = b.x - a.x
  1620.     dy = b.y - a.y
  1621.     vectorSqDist = dx * dx + dy * dy
  1622.  
  1623. Function vectorDistance (a As tVECTOR2d, b As tVECTOR2d)
  1624.     vectorDistance = Sqr(vectorSqDist(a, b))
  1625.  
  1626. Function vectorCross (a As tVECTOR2d, b As tVECTOR2d)
  1627.     vectorCross = a.x * b.y - a.y * b.x
  1628.  
  1629. Sub vectorCrossScalar (o As tVECTOR2d, v As tVECTOR2d, a As _Float)
  1630.     o.x = v.y * -a
  1631.     o.y = v.x * a
  1632.  
  1633. Function vectorArea (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1634.     vectorArea = (((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)))
  1635.  
  1636. Function vectorLeft (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1637.     vectorLeft = vectorArea(a, b, c) > 0
  1638.  
  1639. Function vectorLeftOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1640.     vectorLeftOn = vectorArea(a, b, c) >= 0
  1641.  
  1642. Function vectorRight (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1643.     vectorRight = vectorArea(a, b, c) < 0
  1644.  
  1645. Function vectorRightOn (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d)
  1646.     vectorRightOn = vectorArea(a, b, c) <= 0
  1647.  
  1648. Function vectorCollinear (a As tVECTOR2d, b As tVECTOR2d, c As tVECTOR2d, thresholdAngle As _Float)
  1649.     If (thresholdAngle = 0) Then
  1650.         vectorCollinear = (vectorArea(a, b, c) = 0)
  1651.     Else
  1652.         Dim ab As tVECTOR2d
  1653.         Dim bc As tVECTOR2d
  1654.         Dim dot As _Float
  1655.         Dim magA As _Float
  1656.         Dim magB As _Float
  1657.         Dim angle As _Float
  1658.  
  1659.         ab.x = b.x - a.x
  1660.         ab.y = b.y - a.y
  1661.         bc.x = c.x - b.x
  1662.         bc.y = c.y - b.y
  1663.  
  1664.         dot = ab.x * bc.x + ab.y * bc.y
  1665.         magA = Sqr(ab.x * ab.x + ab.y * ab.y)
  1666.         magB = Sqr(bc.x * bc.x + bc.y * bc.y)
  1667.         angle = _Acos(dot / (magA * magB))
  1668.         vectorCollinear = angle < thresholdAngle
  1669.     End If
  1670.  
  1671. Sub vectorGetSupport (p() As tPOLY, pa() As tPOLYATTRIB, index As Integer, dir As tVECTOR2d, bestVertex As tVECTOR2d)
  1672.     Dim bestProjection As _Float
  1673.     Dim v As tVECTOR2d
  1674.     Dim projection As _Float
  1675.     Dim i As Integer
  1676.     bestVertex.x = -9999999
  1677.     bestVertex.y = -9999999
  1678.     bestProjection = -9999999
  1679.  
  1680.     For i = 0 To pa(index).count
  1681.         v = p(i + pa(index).start).vert
  1682.         projection = vectorDot(v, dir)
  1683.         If projection > bestProjection Then
  1684.             bestVertex = v
  1685.             bestProjection = projection
  1686.         End If
  1687.     Next
  1688.  
  1689. '**********************************************************************************************
  1690. '   Matrix Stuff Ahead
  1691. '**********************************************************************************************
  1692.  
  1693. Sub matrixSetRadians (m As tMATRIX2d, radians As _Float)
  1694.     Dim c As _Float
  1695.     Dim s As _Float
  1696.     c = Cos(radians)
  1697.     s = Sin(radians)
  1698.     m.m00 = c
  1699.     m.m01 = -s
  1700.     m.m10 = s
  1701.     m.m11 = c
  1702.  
  1703. Sub matrixSetScalar (m As tMATRIX2d, a As _Float, b As _Float, c As _Float, d As _Float)
  1704.     m.m00 = a
  1705.     m.m01 = b
  1706.     m.m10 = c
  1707.     m.m11 = d
  1708.  
  1709. Sub matrixAbs (m As tMATRIX2d, o As tMATRIX2d)
  1710.     o.m00 = Abs(m.m00)
  1711.     o.m01 = Abs(m.m01)
  1712.     o.m10 = Abs(m.m10)
  1713.     o.m11 = Abs(m.m11)
  1714.  
  1715. Sub matrixGetAxisX (m As tMATRIX2d, o As tVECTOR2d)
  1716.     o.x = m.m00
  1717.     o.y = m.m10
  1718.  
  1719. Sub matrixGetAxisY (m As tMATRIX2d, o As tVECTOR2d)
  1720.     o.x = m.m01
  1721.     o.y = m.m11
  1722.  
  1723. Sub matrixTransposeI (m As tMATRIX2d)
  1724.     Swap m.m01, m.m10
  1725.  
  1726. Sub matrixTranspose (m As tMATRIX2d, o As tMATRIX2d)
  1727.     Dim tm As tMATRIX2d
  1728.     tm.m00 = m.m00
  1729.     tm.m01 = m.m10
  1730.     tm.m10 = m.m01
  1731.     tm.m11 = m.m11
  1732.     o = tm
  1733.  
  1734. Sub matrixInvert (m As tMATRIX2d, o As tMATRIX2d)
  1735.     Dim a, b, c, d, det As _Float
  1736.     Dim tm As tMATRIX2d
  1737.  
  1738.     a = m.m00: b = m.m01: c = m.m10: d = m.m11
  1739.     det = a * d - b * c
  1740.     If det = 0 Then Exit Sub
  1741.  
  1742.     det = 1 / det
  1743.     tm.m00 = det * d: tm.m01 = -det * b
  1744.     tm.m10 = -det * c: tm.m11 = det * a
  1745.     o = tm
  1746.  
  1747. Sub matrixMultiplyVector (m As tMATRIX2d, v As tVECTOR2d, o As tVECTOR2d)
  1748.     Dim t As tVECTOR2d
  1749.     t.x = m.m00 * v.x + m.m01 * v.y
  1750.     t.y = m.m10 * v.x + m.m11 * v.y
  1751.     o = t
  1752.  
  1753. Sub matrixAddMatrix (m As tMATRIX2d, x As tMATRIX2d, o As tMATRIX2d)
  1754.     o.m00 = m.m00 + x.m00
  1755.     o.m01 = m.m01 + x.m01
  1756.     o.m10 = m.m10 + x.m10
  1757.     o.m11 = m.m11 + x.m11
  1758.  
  1759. Sub matrixMultiplyMatrix (m As tMATRIX2d, x As tMATRIX2d, o As tMATRIX2d)
  1760.     o.m00 = m.m00 * x.m00 + m.m01 * x.m10
  1761.     o.m01 = m.m00 * x.m01 + m.m01 * x.m11
  1762.     o.m10 = m.m10 * x.m00 + m.m11 * x.m10
  1763.     o.m11 = m.m10 * x.m01 + m.m11 * x.m11
  1764.  
  1765. '**********************************************************************************************
  1766. '   Mostly Unused Stuff Ahead
  1767. '**********************************************************************************************
  1768.  
  1769. Sub polygonMakeCCW (obj As tTRIANGLE)
  1770.     If vectorLeft(obj.a, obj.b, obj.c) = 0 Then
  1771.         Swap obj.a, obj.c
  1772.     End If
  1773.  
  1774. Function polygonIsReflex (t As tTRIANGLE)
  1775.     polygonIsReflex = vectorRight(t.a, t.b, t.c)
  1776.  
  1777. Function scalarMin (a As _Float, b As _Float)
  1778.     If a < b Then
  1779.         scalarMin = a
  1780.     Else
  1781.         scalarMin = b
  1782.     End If
  1783.  
  1784. Function scalarMax (a As _Float, b As _Float)
  1785.     If a > b Then
  1786.         scalarMax = a
  1787.     Else
  1788.         scalarMax = b
  1789.     End If
  1790.  
  1791. Sub lineIntersection (l1 As tLINE2d, l2 As tLINE2d, o As tVECTOR2d)
  1792.     Dim a1, b1, c1, a2, b2, c2, det As _Float
  1793.     o.x = 0
  1794.     o.y = 0
  1795.     a1 = l1.b.y - l1.a.y
  1796.     b1 = l1.a.x - l1.b.x
  1797.     c1 = a1 * l1.a.x + b1 * l1.a.y
  1798.     a2 = l2.b.y - l2.a.y
  1799.     b2 = l2.a.x - l2.b.x
  1800.     c2 = a2 * l2.a.x + b2 * l2.a.y
  1801.     det = a1 * b2 - a2 * b1
  1802.  
  1803.     If Int(det * cPRECISION) <> 0 Then
  1804.         o.x = (b2 * c1 - b1 * c2) / det
  1805.         o.y = (a1 * c2 - a2 * c1) / det
  1806.     End If
  1807.  
  1808. Function lineSegmentsIntersect (l1 As tLINE2d, l2 As tLINE2d)
  1809.     Dim dx, dy, da, db, s, t As _Float
  1810.     dx = l1.b.x - l1.a.x
  1811.     dy = l1.b.y - l1.a.y
  1812.     da = l2.b.x - l2.a.x
  1813.     db = l2.b.y - l2.a.y
  1814.     If da * dy - db * dx = 0 Then
  1815.         lineSegmentsIntersect = 0
  1816.     Else
  1817.         s = (dx * (l2.a.y - l1.a.y) + dy * (l1.a.x - l2.a.x)) / (da * dy - db * dx)
  1818.         t = (da * (l1.a.y - l2.a.y) + db * (l2.a.x - l1.a.x)) / (db * dx - da * dy)
  1819.         lineSegmentsIntersect = (s >= 0 And s <= 1 And t >= 0 And t <= 1)
  1820.     End If
  1821.  
  1822. '**********************************************************************************************
  1823. '   Impulse Specific Math Ahead
  1824. '**********************************************************************************************
  1825.  
  1826. Function impulseEqual (a As _Float, b As _Float)
  1827.     impulseEqual = Abs(a - b) <= cEPSILON
  1828.  
  1829. Function impulseClamp (min As _Float, max As _Float, a As _Float)
  1830.     If a < min Then
  1831.         impulseClamp = min
  1832.     Else If a > max Then
  1833.             impulseClamp = max
  1834.         Else
  1835.             impulseClamp = a
  1836.         End If
  1837.     End If
  1838.  
  1839. Function impulseRound (a As _Float)
  1840.     impulseRound = Int(a + 0.5)
  1841.  
  1842. Function impulseRandom_float (min As _Float, max As _Float)
  1843.     impulseRandom_float = ((max - min) * Rnd + min)
  1844.  
  1845. Function impulseRandomInteger (min As Integer, max As Integer)
  1846.     impulseRandomInteger = Int((max - min) * Rnd + min)
  1847.  
  1848. Function impulseGT (a As _Float, b As _Float)
  1849.     impulseGT = (a >= b * cBIAS_RELATIVE + a * cBIAS_ABSOLUTE)
  1850.  
  1851. '**********************************************************************************************
  1852. '   Troubleshooting Tools
  1853. '**********************************************************************************************
  1854.  
  1855. Sub printMatrix (u As tMATRIX2d, n As Integer)
  1856.     Print "---------------------------"
  1857.     Print n; " u:"; u.m00; "|"; u.m10
  1858.     Print "       "; u.m10; "|"; u.m11
  1859.     Do
  1860.     Loop Until _KeyHit = 27
  1861.  
  1862.  
  1863. '**********************************************************************************************
  1864. '   Generate Bitmap
  1865. '**********************************************************************************************
  1866.  
  1867. Sub generateBitmap (bm() As Long)
  1868.     Dim cl As _Unsigned Long
  1869.     Dim rleCount, y, x, i As Integer
  1870.  
  1871.     'CIRCLE QB
  1872.     Data 0,30,4289110427,4,0,52,4289110427,15,0,44,4289110427,21,0,38,4289110427,7
  1873.     Data 4281808695,11,4289110427,7,0,34,4289110427,5,4281808695,18,4289110427,5,0,30,4289110427,4
  1874.     Data 4281808695,23,4289110427,4,0,28,4289110427,3,4281808695,27,4289110427,3,0,25,4289110427,3
  1875.     Data 4281808695,31,4289110427,3,0,22,4289110427,3,4281808695,33,4289110427,3,0,20,4289110427,3
  1876.     Data 4281808695,35,4289110427,3,0,18,4289110427,3,4281808695,38,4289110427,2,0,16,4289110427,3
  1877.     Data 4281808695,40,4289110427,2,0,14,4289110427,3,4281808695,42,4289110427,2,0,13,4289110427,2
  1878.     Data 4281808695,43,4289110427,3,0,11,4289110427,2,4281808695,45,4289110427,2,0,10,4289110427,2
  1879.     Data 4281808695,47,4289110427,2,0,8,4289110427,2,4281808695,49,4289110427,2,0,7,4289110427,2
  1880.     Data 4281808695,49,4289110427,2,0,6,4289110427,2,4281808695,51,4289110427,2,0,5,4289110427,2
  1881.     Data 4281808695,51,4289110427,2,0,5,4289110427,1,4281808695,53,4289110427,2,0,3,4289110427,2
  1882.     Data 4281808695,53,4289110427,2,0,3,4289110427,2,4281808695,54,4289110427,1,0,3,4289110427,1
  1883.     Data 4281808695,7,4281084972,0,4279703319,0,4279637526,10,4280492835,0,4281808695,7,4279637526,14,4279834905,0
  1884.     Data 4281742902,0,4281808695,8,4289110427,2,0,1,4289110427,2,4281808695,6,4279637526,15,4281808695,6
  1885.     Data 4279637526,17,4281808695,7,4289110427,2,0,1,4289110427,2,4281808695,5,4279637526,1,4282412608,0
  1886.     Data 4284394590,14,4281808695,5,4279637526,1,4284394590,15,4284394334,0,4281808695,7,4289110427,1,0,1
  1887.     Data 4289110427,1,4281808695,6,4279637526,0,4280034076,0,4284394590,16,4281808695,4,4279637526,1,4284394590,16
  1888.     Data 4284129114,0,4281808695,6,4289110427,1,0,1,4289110427,1,4281808695,6,4279637526,0,4284394590,17
  1889.     Data 4283333454,0,4281808695,3,4279637526,1,4284394590,17,4281808695,6,4289110427,2,0,0,4289110427,1
  1890.     Data 4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1
  1891.     Data 4284394590,2,4281808695,9,4279637526,0,4284394590,3,4281808695,6,4289110427,2,0,0,4289110427,1
  1892.     Data 4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1
  1893.     Data 4284394590,2,4281808695,9,4279637526,0,4284394590,3,4281808695,6,4289110427,5,4281808695,6,4279637526,0
  1894.     Data 4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,2,4281808695,9
  1895.     Data 4279637526,0,4284394590,3,4281808695,6,4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9
  1896.     Data 4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,2,4279637526,10,4284394590,3,4281808695,6
  1897.     Data 4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9,4279637526,0,4284394590,3,4281808695,3
  1898.     Data 4279637526,1,4284394590,17,4281808695,6,4289110427,5,4281808695,6,4279637526,0,4284394590,3,4281808695,9
  1899.     Data 4279637526,0,4284394590,3,4281808695,3,4279637526,1,4284394590,16,4281808695,7,4289110427,5,4281808695,6
  1900.     Data 4279637526,0,4284394590,3,4281808695,7,4281084972,0,4279703319,0,4279637526,0,4284394590,3,4281808695,3
  1901.     Data 4279637526,1,4284394590,17,4281808695,6,4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0
  1902.     Data 4284394590,3,4281808695,6,4279637526,3,4284394590,3,4281808695,3,4279637526,1,4284394590,17,4281808695,6
  1903.     Data 4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0,4284394590,3,4281808695,5,4279637526,1
  1904.     Data 4282412608,0,4284394590,5,4281808695,3,4279637526,1,4284394590,2,4281808695,9,4279637526,0,4284394590,3
  1905.     Data 4281808695,6,4289110427,2,0,0,4289110427,1,4281808695,6,4279637526,0,4284394590,3,4281808695,5
  1906.     Data 4279637526,0,4280034076,0,4284394590,6,4281808695,3,4279637526,1,4284394590,2,4281808695,9,4279637526,0
  1907.     Data 4284394590,3,4281808695,6,4289110427,1,0,1,4289110427,2,4281808695,5,4279637526,0,4284394590,3
  1908.     Data 4279637526,6,4284394590,7,4281808695,3,4279637526,1,4284394590,2,4279637526,10,4284394590,3,4281808695,6
  1909.     Data 4289110427,1,0,1,4289110427,2,4281808695,5,4279637526,0,4284394590,16,4283532625,0,4281875000,0
  1910.     Data 4281808695,3,4279637526,1,4284394590,17,4281808695,5,4289110427,2,0,2,4289110427,1,4281808695,6
  1911.     Data 4284394590,15,4281808695,6,4279637526,1,4284394590,17,4281808695,5,4289110427,2,0,2,4289110427,1
  1912.     Data 4281808695,7,4284394590,13,4281808695,7,4279637526,1,4284394590,16,4282803270,0,4281808695,5,4289110427,1
  1913.     Data 0,3,4289110427,2,4281808695,7,4284394590,12,4281808695,9,4284394590,15,4282670916,0,4281808695,5
  1914.     Data 4289110427,2,0,4,4289110427,1,4281808695,53,4289110427,2,0,4,4289110427,2,4281808695,52
  1915.     Data 4289110427,1,0,5,4289110427,2,4281808695,51,4289110427,2,0,6,4289110427,2,4281808695,50
  1916.     Data 4289110427,1,0,7,4289110427,2,4281808695,49,4289110427,2,0,8,4289110427,2,4281808695,47
  1917.     Data 4289110427,2,0,10,4289110427,2,4281808695,46,4289110427,1,0,12,4289110427,2,4281808695,44
  1918.     Data 4289110427,2,0,12,4289110427,2,4281808695,43,4289110427,2,0,14,4289110427,2,4281808695,41
  1919.     Data 4289110427,2,0,16,4289110427,2,4281808695,39,4289110427,2,0,18,4289110427,3,4281808695,36
  1920.     Data 4289110427,2,0,20,4289110427,3,4281808695,33,4289110427,3,0,22,4289110427,3,4281808695,31
  1921.     Data 4289110427,3,0,24,4289110427,4,4281808695,28,4289110427,3,0,27,4289110427,4,4281808695,24
  1922.     Data 4289110427,3,0,30,4289110427,5,4281808695,19,4289110427,5,0,33,4289110427,6,4281808695,13
  1923.     Data 4289110427,6,0,37,4289110427,23,0,42,4289110427,17,0,49,4289110427,9,0,26
  1924.  
  1925.     'BOX 64
  1926.     Data 4278190160,1484,4278650685,0,4279637527,0,4279637526,13,4278190160,5,4279637526,3,4278190160,10,4279637526,3
  1927.     Data 4278190160,21,4279637526,16,4278190160,5,4279637526,3,4278190160,10,4279637526,3,4278190160,20,4279637526,1
  1928.     Data 4286280720,0,4290950923,16,4278190160,3,4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3
  1929.     Data 4278190160,19,4279637526,0,4280624149,0,4290950923,17,4278190160,3,4279637526,1,4290950923,2,4278190160,9
  1930.     Data 4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,18,4278190160,3,4279637526,1,4290950923,2
  1931.     Data 4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,18,4279637526,1
  1932.     Data 4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,18
  1933.     Data 4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3
  1934.     Data 4278190160,18,4279637526,1,4290950923,2,4278190160,9,4279637526,0,4290950923,3,4278190160,19,4279637526,0
  1935.     Data 4290950923,3,4279637526,10,4279505947,0,4278190160,6,4279374370,0,4279637526,0,4290950923,2,4279637526,10
  1936.     Data 4290950923,3,4278190160,19,4279637526,0,4290950923,16,4278190160,6,4279637526,0,4290950923,17,4278190160,19
  1937.     Data 4279637526,0,4290950923,17,4278190160,5,4278255950,0,4290950923,17,4278190160,19,4279637526,0,4290950923,17
  1938.     Data 4282070843,0,4278190160,5,4278584910,0,4290950923,16,4278190160,19,4279637526,0,4290950923,17,4290622221,0
  1939.     Data 4278190160,7,4286149157,0,4290950923,14,4278190160,19,4279637526,0,4290950923,3,4278190160,9,4279637526,0
  1940.     Data 4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3,4278190160,9
  1941.     Data 4279637526,0,4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,3
  1942.     Data 4279637526,10,4290950923,3,4278190160,18,4279637526,0,4290950923,3,4278190160,19,4279637526,0,4290950923,17
  1943.     Data 4290687756,0,4278190160,18,4279637526,0,4290950923,3,4278190160,20,4290950923,17,4282597176,0,4278190160,18
  1944.     Data 4279637526,0,4290950923,3,4278190160,21,4290950923,16,4278190160,19,4279637526,0,4290950923,3,4278190160,21
  1945.     Data 4278255952,0,4290950923,14,4278190160,21,4290950923,3,4278190160,1353
  1946.  
  1947.     'Hedgehog CC0 - from Frogatto & Friends team - www.frogatto.com
  1948.     Data 0,148,4282268723,0,4280889648,0,4282145364,0,4282541920,1,0,58,4282663478,0,4282143050,0
  1949.     Data 4284656026,0,4285714365,0,4285914054,0,4290832070,0,4293848006,0,4293780935,0,4293650117,0,4293454274,0
  1950.     Data 4293651396,0,4292602567,0,4288996553,0,4285375122,0,4282467390,0,4281546805,0,4281548088,0,0,48
  1951.     Data 4281546546,0,4281822563,0,4283351467,0,4285519038,0,4286698443,0,4286229455,0,4285246658,0,4283876010,0
  1952.     Data 4285518780,0,4288209609,0,4292471495,0,4294240706,0,4294042300,0,4289452478,0,4285250988,0,4281946187,0
  1953.     Data 4282137394,0,0,48,4281225803,0,4284394139,0,4285847238,0,4284333241,0,4283611312,0,4283154601,0
  1954.     Data 4288272314,0,4292733639,0,4294241734,0,4294963142,1,4293389510,0,4291224001,0,4286362786,0,4283662710,0
  1955.     Data 4282072630,0,4283716160,0,0,46,4280360993,0,4284061579,0,4285716932,0,4283745711,0,4283614638,0
  1956.     Data 4284534199,0,4290371775,0,4294963142,4,4294307270,0,4291291336,0,4288988330,0,4286939503,0,4284965458,0
  1957.     Data 4283518782,0,4284768842,0,0,44,4280101432,0,4283931540,0,4285782725,0,4284205748,0,4285255865,0
  1958.     Data 4287881930,0,4291881415,0,4294963142,8,4288190851,0,4281812801,0,4283520581,0,0,37,4280953127,0
  1959.     Data 4280360993,4,4279842904,0,4279195808,0,4283608250,0,4286308299,1,4289521097,0,4294963142,10,4290635720,0
  1960.     Data 4285515701,0,4282211158,0,4281940016,0,0,33,4281611053,1,0,0,4284391314,0,4287607966,0
  1961.     Data 4291279772,3,4288330924,0,4284530112,0,4284138423,0,4283942833,1,4288075449,0,4294963142,8,4292930503,0
  1962.     Data 4288865225,0,4290635720,0,4291219890,0,4284243796,0,4281940016,0,0,33,4283596917,0,4286285172,0
  1963.     Data 4288389000,0,4290239421,0,4292471495,0,4294963142,3,4293061575,0,4290635720,0,4286896319,0,4284007093,0
  1964.     Data 4281833640,0,4284131499,0,4289058743,0,4292338879,0,4294963142,1,4293323974,0,4290897864,0,4290635720,2
  1965.     Data 4288733123,0,4285059258,0,4289847232,0,4293452220,0,4286947983,0,4283198818,0,0,32,4286225290,0
  1966.     Data 4288865225,0,4292930503,0,4294963142,4,4294373062,0,4293192647,0,4293717190,0,4294963142,0,4290897864,0
  1967.     Data 4287025091,0,4282355120,0,4281371305,0,4282164902,0,4288138425,0,4292799431,1,4290307784,0,4286701514,0
  1968.     Data 4286308299,0,4286176969,0,4285716932,0,4284927421,0,4283807923,0,4288270525,0,4292209351,0,4289783240,0
  1969.     Data 4285900437,0,4281350198,0,0,27,4281089595,0,4282880899,0,4287156403,0,4293060802,0,4294635206,0
  1970.     Data 4294963142,6,4292799431,0,4287881930,0,4289848776,0,4294373062,1,4292798661,0,4287551427,0,4283474867,0
  1971.     Data 4279325850,0,4283145392,0,4286174401,0,4286239939,0,4286240966,0,4286242506,0,4286176713,0,4285716933,0
  1972.     Data 4283942322,0,4284002999,0,4285373646,0,4285906126,0,4287357386,0,4292209351,0,4290358930,0,4282136885,0
  1973.     Data 0,25,4281545260,0,4283200618,0,4286103725,0,4290369721,0,4291224771,0,4288471752,0,4291422408,0
  1974.     Data 4294963142,6,4292799431,0,4287881930,0,4286963914,0,4287881930,1,4286890933,0,4283457136,0,4281939800,0
  1975.     Data 4281149267,0,4282924625,0,4284698708,0,4285616229,0,4286088592,0,4286240453,0,4284862651,0,4283875762,0
  1976.     Data 4284721858,0,4285243083,0,4285504208,0,4285906126,0,4287357386,0,4292209351,0,4290358930,0,4282136885,0
  1977.     Data 0,25,4283598459,0,4287412644,0,4291028936,0,4290373320,0,4288865225,0,4286767050,0,4290635720,0
  1978.     Data 4294963142,4,4294635206,0,4291881415,0,4288667588,0,4285518523,0,4285121729,0,4285839566,0,4284451740,0
  1979.     Data 4284375669,0,4286853466,0,4287570514,1,4288161614,0,4288555854,0,4288359511,0,4287449708,0,4286216070,0
  1980.     Data 4284707957,0,4283855987,0,4285042614,0,4284387016,0,4282152887,0,4283212214,0,4285190329,0,4288339651,0
  1981.     Data 4287211418,0,4282136134,0,4281806644,0,4282597686,1,4283321404,0,0,20,4284319354,0,4288535490,0
  1982.     Data 4290635720,0,4291487943,0,4288144073,0,4287094986,0,4288668617,0,4291946951,0,4294963142,4,4294373062,0
  1983.     Data 4289521097,0,4285519810,0,4283745711,0,4283604646,0,4284314276,0,4284235628,0,4285735502,0,4290717275,0
  1984.     Data 4292290655,1,4292424285,0,4292295515,0,4291443803,0,4290002521,0,4288430166,0,4287313226,0,4286264909,0
  1985.     Data 4284834187,0,4282934425,0,4280174724,0,4281891996,0,4284201400,0,4285518276,0,4285047966,0,4282792271,0
  1986.     Data 4282196791,0,4282000433,0,4281147437,0,4281478192,0,4283452991,0,0,17,4280955955,0,4284259728,0
  1987.     Data 4287876020,0,4292662449,0,4289318579,0,4285515701,1,4287876537,0,4292008383,0,4293190337,0,4293454274,0
  1988.     Data 4293716933,0,4294110662,0,4294766278,0,4294438598,0,4293389510,0,4289127625,0,4285585603,0,4284008626,0
  1989.     Data 4282809213,0,4281867840,0,4286981714,0,4290390622,0,4291635294,0,4291503964,0,4290783063,0,4291315025,0
  1990.     Data 4291714381,1,4292238672,0,4293090900,0,4292689754,0,4291044955,0,4285014091,0,4282654021,1,4283847810,0
  1991.     Data 4285108670,0,4285510587,0,4285110159,0,4284170819,0,4284952389,0,4285738325,0,4283310922,0,4281671227,0
  1992.     Data 4280557349,0,4282597686,0,0,16,4280427043,0,4280889648,0,4281415734,0,4282071349,0,4281612341,0
  1993.     Data 4281087798,1,4281091928,0,4281099414,0,4282882216,0,4284794798,0,4286503105,0,4289258697,0,4293979590,0
  1994.     Data 4291619015,0,4287291594,0,4286701514,0,4286176969,0,4285979591,0,4284911494,0,4283641140,0,4287638350,0
  1995.     Data 4290062942,0,4290259550,0,4287113034,0,4281803048,0,4281869607,0,4281935911,1,4285868859,0,4291768154,0
  1996.     Data 4292291422,0,4291831902,0,4289800283,0,4288948570,1,4285540951,0,4282068051,0,4282134867,0,4282725193,0
  1997.     Data 4283707447,0,4284952389,0,4285935190,0,4285607253,0,4284164423,0,4280951079,0,4282597686,0,0,22
  1998.     Data 4281347882,0,4279972168,0,4279325072,0,4279921569,0,4280713124,0,4281435820,0,4283276465,0,4286826675,0
  1999.     Data 4286107572,0,4284139445,0,4282753970,0,4282220961,0,4283067520,0,4285287524,0,4287769677,0,4289145687,0
  2000.     Data 4288883030,0,4285080895,0,4284428600,0,4285154873,0,4286473550,0,4287719510,0,4288489531,0,4287304755,0
  2001.     Data 4285079863,0,4289013070,0,4292159582,0,4291635294,0,4291438942,1,4289865305,0,4288095060,0,4287308883,0
  2002.     Data 4285473868,0,4282983231,0,4283574590,0,4284952386,0,4287311186,0,4287376213,0,4284426565,0,4283183419,0
  2003.     Data 4282466100,0,4284242501,0,0,21,4281426545,0,4281560742,0,4280378537,0,4279261856,0,4280185506,0
  2004.     Data 4282356647,0,4286695345,0,4287549875,0,4287417779,0,4286032304,0,4284183443,0,4282200406,0,4285276750,0
  2005.     Data 4290063198,0,4287768655,0,4285802818,0,4283839540,0,4285551169,0,4289429342,0,4290682227,0,4292121461,0
  2006.     Data 4293340491,0,4290709299,0,4285927978,0,4286717242,0,4288226634,0,4290521176,0,4291438942,1,4291831902,0
  2007.     Data 4291962974,0,4290718302,0,4288358488,0,4285407822,0,4285474631,0,4286459204,0,4288818005,0,4289407836,0
  2008.     Data 4287835482,0,4284556870,0,4281082671,0,4283715395,0,0,19,4281224777,0,4282817418,0,4283937975,0
  2009.     Data 4284257476,0,4282417079,0,4280776360,0,4283805100,0,4287746740,0,4293716675,0,4294241734,0,4293323719,0
  2010.     Data 4293190599,0,4290293414,0,4283777115,0,4285276750,0,4290063198,0,4283966519,0,4281937450,0,4287192905,0
  2011.     Data 4290088298,0,4292329616,1,4292582527,0,4293472334,0,4293918777,1,4287439157,0,4283185463,0,4286786890,0
  2012.     Data 4288751958,0,4289931869,0,4290914654,0,4291635294,0,4290587230,0,4290063198,1,4290259550,0,4290456158,3
  2013.     Data 4287440212,0,4283638343,0,4284171847,0,0,17,4280953126,0,4281822562,0,4283349669,0,4285124796,0
  2014.     Data 4284069053,0,4280380583,0,4282685877,0,4286371784,0,4291552709,0,4294372548,0,4294831813,0,4291356872,0
  2015.     Data 4286766027,0,4286363598,0,4284972716,0,4282397788,0,4285276750,0,4290063198,0,4283966519,0,4282399538,0
  2016.     Data 4289369454,0,4292724376,0,4294699969,1,4294557608,0,4294064731,0,4293918777,1,4292239711,0,4289764210,0
  2017.     Data 4283450171,0,4283769654,0,4289473114,0,4290128478,1,4290062942,0,4290063198,1,4291045726,0,4292094046,3
  2018.     Data 4291897182,0,4290914137,0,4284753209,0,4280624164,3,4283650368,0,0,12,4282222713,0,4283740575,0
  2019.     Data 4284795582,0,4282621107,0,4282225330,0,4283739835,0,4288139202,0,4292405959,0,4294110662,0,4293455046,0
  2020.     Data 4289717705,0,4286830783,0,4284074163,0,4283940020,0,4283272856,0,4282005591,0,4285276750,0,4290063198,0
  2021.     Data 4283966519,0,4282465331,0,4289567089,0,4292920220,0,4294957510,0,4289303942,0,4284830284,0,4284622899,0
  2022.     Data 4288089390,0,4293329207,0,4293881980,0,4292194975,0,4283713861,0,4283376436,0,4289407578,0,4290063198,4
  2023.     Data 4290390622,0,4290849118,0,4291766366,0,4292424285,0,4292889944,0,4293022295,0,4292760150,0,4290662731,0
  2024.     Data 4289286212,1,4288953671,0,4288554315,0,4284428602,0,4282597686,0,4284769098,0,0,10,4287809964,0
  2025.     Data 4286567352,0,4284203704,0,4280974760,0,4282549420,0,4288730307,0,4292471495,0,4294700742,0,4292340423,0
  2026.     Data 4290897864,0,4290176712,0,4287617985,0,4284468152,0,4284267193,0,4283533980,0,4282070872,0,4285276750,0
  2027.     Data 4290063198,0,4286588744,0,4284953409,0,4286342483,0,4287592556,0,4289101964,0,4286532958,0,4284556347,1
  2028.     Data 4286061372,0,4288286012,0,4288499297,0,4287788661,0,4284235081,0,4285081412,0,4289604187,0,4290063198,5
  2029.     Data 4290194014,0,4291569758,0,4292556636,0,4293221973,0,4293355092,4,4293088854,0,4292756314,0,4287639623,0
  2030.     Data 4284296251,0,4283450427,0,4284045123,0,0,8,4285365355,0,4294042813,0,4288531126,0,4281966759,0
  2031.     Data 4281305001,0,4285308595,0,4293189826,0,4294963142,0,4294504134,0,4290438856,0,4290045640,0,4294110662,0
  2032.     Data 4291160264,0,4286241483,0,4285838286,0,4284578477,0,4282331996,0,4284358731,0,4287899479,0,4289210715,0
  2033.     Data 4288686421,0,4284097592,0,4283113785,0,4283703113,0,4287571284,0,4290456158,0,4290194014,0,4287637078,0
  2034.     Data 4284096586,0,4283179323,0,4283311157,0,4285934414,0,4287966554,0,4289866589,0,4290063198,0,4290128478,0
  2035.     Data 4290390622,1,4290128478,0,4290063198,0,4290194014,0,4291569758,0,4292490076,0,4292956247,0,4293156181,0
  2036.     Data 4293355092,3,4293222229,0,4293088854,0,4289869910,0,4286522700,0,4281737515,0,4281742638,0,4284176709,0
  2037.     Data 4284900427,0,0,5,4282423431,0,4286828210,0,4294175940,0,4287079347,0,4280051877,0,4284069053,0
  2038.     Data 4288996553,0,4293848518,0,4294963142,4,4291160264,0,4286241483,0,4285838286,0,4284578477,0,4282331996,0
  2039.     Data 4281605186,1,4286850387,0,4290063198,3,4291307870,0,4291831902,0,4290587230,0,4290063198,7,4290325086,0
  2040.     Data 4291569758,1,4290325086,0,4290063198,0,4290194014,0,4291569758,0,4292290655,1,4292756314,0,4293355092,3
  2041.     Data 4292889176,0,4292290655,0,4285603916,0,4281343035,0,4280557351,0,4280360993,1,4283190076,0,0,5
  2042.     Data 4289049239,0,4290108346,0,4286434737,0,4283071608,0,0,0,4288786333,0,4293455046,0,4294635206,0
  2043.     Data 4294963142,2,4293323974,0,4289258697,0,4287488458,0,4286043332,0,4283988107,0,4283119456,0,4284427605,0
  2044.     Data 4285805134,0,4286987595,0,4286985043,0,4287573337,0,4289473373,0,4290063198,1,4291307870,0,4292159582,0
  2045.     Data 4291831902,0,4291045726,0,4290128478,0,4285933124,0,4282786352,1,4285015358,0,4289604187,0,4290063198,0
  2046.     Data 4290392157,0,4292102233,0,4292626521,0,4291898973,0,4290849118,0,4290062942,0,4290390622,0,4290980190,0
  2047.     Data 4292028510,0,4292756314,0,4293355092,3,4292103000,0,4290587230,0,4284948812,0,4282985029,0,4287650386,0
  2048.     Data 4288174414,0,4286660162,0,4283840566,0,4281611053,0,0,4,4291279772,0,4290824103,0,4282546027,0
  2049.     Data 0,1,4290358930,0,4293586118,0,4291160264,0,4290635720,0,4290897864,0,4293586118,0,4292799431,0
  2050.     Data 4287357386,0,4285585603,0,4284531890,0,4282736495,0,4283049030,0,4286264911,0,4287971664,0,4288825678,0
  2051.     Data 4287905872,0,4287117138,0,4286130256,0,4287374933,0,4290063198,0,4291307870,0,4292028510,0,4291438942,0
  2052.     Data 4288882770,0,4285539649,0,4286007112,0,4285880905,0,4281937964,0,4283703860,0,4290455642,0,4290587230,0
  2053.     Data 4290458204,0,4292301655,0,4293155925,0,4292889176,0,4289017420,0,4285211967,1,4286654023,0,4290390362,0
  2054.     Data 4292100953,0,4293355092,1,4293221973,0,4292889688,0,4290722649,0,4288032090,0,4283966027,0,4283642443,0
  2055.     Data 4290477417,0,4291461222,0,4289748818,0,4284824127,0,4280950321,0,0,4,4287859318,0,4287727987,0
  2056.     Data 0,2,4290358930,0,4292209351,0,4287357386,0,4286308299,0,4286635978,0,4290373320,0,4290174149,0
  2057.     Data 4285053886,0,4283083699,0,4281833375,0,4281683303,0,4283245382,0,4286920016,0,4288430416,0,4288825678,1
  2058.     Data 4287841100,0,4284755783,0,4285016391,0,4286981962,0,4289538134,0,4290717786,0,4288161615,0,4286002245,0
  2059.     Data 4284499774,0,4289231969,0,4291991925,0,4286472269,0,4285611586,0,4288292682,0,4289603412,0,4291047773,0
  2060.     Data 4292498263,0,4293355092,1,4286988863,0,4280951084,0,4283186231,0,4285087811,0,4286982738,0,4289542742,0
  2061.     Data 4292367191,0,4292760151,0,4292626521,0,4291833949,0,4288489560,0,4284623184,0,4284955474,0,4286666844,0
  2062.     Data 4291597173,0,4292450418,0,4291462242,0,4285876560,0,4281605186,0,0,9,4290358930,0,4292077252,0
  2063.     Data 4287026881,0,4285911745,0,4285977282,1,4284330936,0,4280116383,0,4279325850,0,4279455892,0,4280693091,0
  2064.     Data 4282784840,0,4285537368,0,4287442519,0,4288693839,0,4288759630,0,4288825678,1,4285807166,0,4280885795,0
  2065.     Data 4285736515,0,4287899729,0,4283049009,0,4285420357,0,4292320375,0,4293109117,2,4289297761,0,4281740585,0
  2066.     Data 4286260800,0,4291767900,0,4292759383,0,4293026132,0,4292698451,0,4287447628,0,4282985545,0,4289690731,0
  2067.     Data 4290348143,0,4283642956,0,4285474383,0,4289669468,0,4291111005,0,4291438686,0,4289997148,0,4286326353,0
  2068.     Data 4282196548,0,4289033319,0,4293109117,3,4286601051,0,4281605186,0,0,9,4290358930,0,4290689436,0
  2069.     Data 4283128145,0,4281942606,0,4282466395,1,4282137177,0,4281281108,0,4281149267,0,4281149010,0,4281409608,0
  2070.     Data 4281801795,0,4282391622,0,4284555088,0,4287177819,0,4288166739,0,4288825678,1,4288169546,0,4287185221,0
  2071.     Data 4284951878,0,4283901254,0,4286199877,0,4289422938,0,4292714105,0,4293109117,2,4291727725,0,4289030479,0
  2072.     Data 4286136905,0,4284686410,0,4289670230,0,4289736789,0,4284885832,0,4287322715,0,4291071090,0,4292385913,0
  2073.     Data 4292517498,0,4291202675,0,4287519583,0,4283637576,0,4287702613,0,4288620376,0,4284555339,0,4286136658,0
  2074.     Data 4290014816,0,4291924082,0,4293109117,3,4286601051,0,4281605186,0,0,6,4279844709,0,0,1
  2075.     Data 4284702290,0,4285233770,0,4282481260,0,4281031538,0,4280302451,0,4281149010,0,4282785349,0,4285735759,0
  2076.     Data 4286325841,4,4285276240,0,4283898958,0,4286261332,0,4287902805,0,4288562255,0,4288825678,1,4286987595,0
  2077.     Data 4286200137,0,4287972428,0,4290143323,0,4292185198,0,4292779127,0,4292977275,0,4292580980,0,4291922024,0
  2078.     Data 4290803543,0,4288701009,0,4286662733,0,4286394190,0,4286064460,0,4285739593,0,4289359202,0,4293109117,3
  2079.     Data 4290609772,0,4287386198,0,4286921804,0,4286789450,0,4287253844,0,4289423454,0,4291986024,0,4292646773,0
  2080.     Data 4293109117,1,4292844920,0,4292448625,0,4286336854,0,4281605186,0,0,5,4280504174,0,4281697706,0
  2081.     Data 4282218888,0,4282410591,0,4281753431,0,4281429869,0,4281635227,0,4280643211,0,4279910510,0,4283116631,0
  2082.     Data 4286260048,0,4290127964,0,4290849118,4,4288161366,0,4284752204,0,4284816977,0,4285473108,0,4287443540,0
  2083.     Data 4288298577,0,4288825678,3,4289813589,0,4291328608,0,4292119660,0,4292514930,0,4291789926,0,4291263325,0
  2084.     Data 4290737750,1,4290014804,0,4286795597,0,4286400075,0,4288960080,0,4291264359,0,4293109117,3,4292580723,0
  2085.     Data 4291657317,0,4288831827,0,4288306769,0,4291330920,0,4292382575,1,4292844664,0,4293109117,1,4292449906,0
  2086.     Data 4291460449,0,4285679432,0,4281081141,0,0,4,4280965251,0,4282617263,0,4285584580,0,4285914054,0
  2087.     Data 4285454018,0,4283613105,0,4281570464,0,4279721363,0,4280432988,0,4282065195,0,4287571789,0,4290783582,0
  2088.     Data 4292028510,0,4292291422,0,4292423517,3,4291505501,0,4290325086,0,4284817740,0,4282457158,0,4285275222,0
  2089.     Data 4287048279,0,4288562000,1,4288562255,0,4288759630,0,4289419088,0,4290342228,0,4291197278,0,4291657571,0
  2090.     Data 4291000409,0,4290737750,5,4290869079,0,4291923049,0,4292976762,0,4293043068,1,4292977018,0,4292514674,0
  2091.     Data 4291986024,1,4292250221,0,4292845177,0,4293109117,2,4293043324,0,4292977018,0,4291595370,0,4289424207,0
  2092.     Data 4284630584,0,4280953127,0,0,4,4289834393,0,4290041019,0,4284138926,0,4283548589,0,4283021483,0
  2093.     Data 4280844964,0,4279849342,0,4280102462,0,4284491843,0,4289014102,0,4290979932,0,4292028510,0,4292225118,0
  2094.     Data 4292689754,0,4293221973,3,4292691545,0,4291962974,0,4285472844,0,4281670722,0,4282129476,0,4284030542,0
  2095.     Data 4287045980,1,4287375706,0,4288364369,0,4288891726,0,4289023566,0,4290078036,0,4290869079,0,4290737750,5
  2096.     Data 4291066202,0,4291657571,0,4291920744,0,4292184171,0,4292778871,0,4292844920,0,4292184684,0,4292052073,0
  2097.     Data 4291986024,1,4292250221,0,4292845177,0,4293109117,2,4292910969,0,4292250734,0,4288177233,0,4281608743,0
  2098.     Data 4283451963,0,4284834635,0,0,1,4282466100,0,4286281058,0,4289440144,0,4289516728,0,4287551935,0
  2099.     Data 4283548331,0,4282224254,0,4281161555,0,4280369489,0,4281151820,0,4283378244,0,4287637840,0,4291373406,0
  2100.     Data 4291045726,0,4291242334,0,4292094046,0,4292756314,0,4293355092,3,4293155669,0,4292955736,0,4289999701,0
  2101.     Data 4286980688,0,4282850629,0,4281998398,0,4282785336,1,4283638078,0,4286327120,0,4287573080,1,4288628308,0
  2102.     Data 4289682769,0,4290408020,0,4290737750,4,4291066203,0,4291788901,0,4291986024,1,4292250477,1,4291986280,0
  2103.     Data 4291986024,2,4292052329,0,4292316270,0,4292712565,0,4293109117,1,4292515702,0,4290538334,0,4286334531,0
  2104.     Data 4280360993,0,4283190076,0,0,2,4280624164,0,4288517245,0,4293848518,0,4288472009,0,4284923821,0
  2105.     Data 4282155123,0,4283256660,0,4284359995,0,4283377209,0,4283771202,0,4285541715,0,4289145691,0,4292094046,0
  2106.     Data 4290718302,0,4290325086,0,4291111262,0,4291966812,0,4292889688,0,4293155669,0,4293355092,3,4292689754,0
  2107.     Data 4290258265,0,4283571783,0,4281081141,0,4280360993,0,4281409318,0,4282588719,0,4283571517,0,4283964739,1
  2108.     Data 4284623678,0,4285217338,0,4285876797,0,4287057735,0,4288826968,0,4289419350,0,4290012499,0,4290802522,0
  2109.     Data 4291460449,0,4291854694,0,4291986024,9,4292250221,0,4292580723,0,4292252270,0,4290740066,0,4286400323,0
  2110.     Data 4284169016,0,4282597686,0,4284176965,0,0,2,4280624164,0,4287599230,0,4292273603,0,4287219634,0
  2111.     Data 4284189571,0,4282329908,0,4286064197,0,4289604445,0,4287769946,0,4286393941,0,4285738833,0,4288949079,0
  2112.     Data 4292094046,0,4290718302,0,4290063198,1,4290849118,0,4291898206,0,4292364377,0,4292827733,0,4293024342,0
  2113.     Data 4293155414,1,4292557403,0,4290389594,0,4284227914,0,4282064700,0,4281540396,0,4283637047,0,4284620347,0
  2114.     Data 4282721326,0,4282000426,0,4282064941,0,4281802540,0,4281540396,1,4282785336,0,4285340752,0,4286394188,0
  2115.     Data 4287514440,0,4288962900,0,4289752922,1,4290540382,0,4291393379,0,4291591524,0,4291788901,0,4291920231,0
  2116.     Data 4291986024,2,4291920231,0,4291788901,0,4291591267,0,4291393379,0,4289752148,0,4287388485,0,4282660912,0
  2117.     Data 4282860856,0,0,4,4280624164,0,4283993216,0,4286040502,0,4282210387,0,4283444026,0,4288883287,0
  2118.     Data 4289735260,0,4289997661,0,4289866589,0,4287966288,0,4284755770,0,4288162634,0,4292094046,0,4290718302,0
  2119.     Data 4290063198,1,4290062942,0,4290194270,0,4290194781,0,4290784605,0,4291963998,0,4292356958,1,4292290910,0
  2120.     Data 4291045725,0,4286917719,0,4285933399,0,4286389339,0,4286585947,0,4286981209,0,4287839055,0,4288559186,0
  2121.     Data 4288947293,0,4287569500,0,4286389339,1,4284620106,0,4281081640,0,4280820004,0,4280886052,0,4281017636,0
  2122.     Data 4281083429,1,4285020474,0,4289089360,0,4290210132,0,4291066203,0,4291788901,0,4291986024,2,4291657571,0
  2123.     Data 4291000410,0,4290012243,0,4289023055,0,4283970612,0,4281741612,0,4284373830,0,4285229391,0,0,4
  2124.     Data 4280624164,0,4281352510,0,4282799444,0,4285805645,0,4287965264,0,4289866332,0,4290783582,0,4291569758,1
  2125.     Data 4289211217,0,4284886840,0,4287311176,0,4290783582,0,4291307870,0,4291176798,0,4290194014,0,4290063198,2
  2126.     Data 4290194014,0,4290587230,0,4291373406,0,4292290655,0,4291307870,0,4289275994,0,4284425036,0,4283899718,0
  2127.     Data 4285471812,0,4286983500,0,4288562003,0,4289616979,0,4289681235,0,4289085268,0,4288559955,0,4288231253,0
  2128.     Data 4288554590,0,4288094299,0,4286783823,0,4286652750,2,4284686400,0,4281147430,0,4281673256,0,4282985775,0
  2129.     Data 4283381296,0,4283644722,0,4283907638,0,4283973175,2,4283841845,0,4283644722,0,4283315248,0,4282985519,0
  2130.     Data 4283516218,0,4284176708,0,4285163598,0,0,3,4282926649,0,4283775547,0,4285015871,0,4285211967,0
  2131.     Data 4285802051,0,4288817494,0,4290063198,1,4291443035,0,4292756314,0,4292424029,0,4289997398,0,4285869640,0
  2132.     Data 4286000710,0,4287245641,0,4289866327,0,4291176798,1,4290652766,0,4290063198,1,4290325086,0,4290914654,0
  2133.     Data 4291176798,1,4287833429,0,4285801296,1,4284817480,0,4283243581,0,4286330182,0,4289155407,0,4290210388,0
  2134.     Data 4290342228,0,4289814610,0,4289748818,0,4289945685,0,4290929256,0,4291322736,2,4291190893,0,4290532194,0
  2135.     Data 4288495188,0,4285605445,0,4282982201,0,4281212466,0,4283835195,0,4283966519,0,4280950820,0,4281148196,0
  2136.     Data 4282132522,1,4281673255,0,4280688930,0,4281479211,0,4282926649,0,0,6,4280360993,0,4283966519,0
  2137.     Data 4289407578,0,4290325086,0,4290587230,3,4291971161,0,4293222485,0,4292757081,0,4290786395,0,4287704921,0
  2138.     Data 4286197323,0,4285607487,0,4287179087,0,4288882520,0,4291242077,0,4291242334,0,4290587230,1,4290849118,0
  2139.     Data 4291504478,0,4290062427,0,4287899479,0,4285276750,0,4284883276,0,4288817242,0,4287244117,0,4282654791,0
  2140.     Data 4285083977,0,4287382603,0,4288698189,0,4289421138,0,4289683287,0,4290012501,0,4290473815,0,4291593577,0
  2141.     Data 4292186481,0,4292450419,0,4292516211,0,4292186991,0,4290935133,0,4290008152,0,4289669985,0,4285603666,0
  2142.     Data 4282064195,0,4286719828,0,4286851405,0,4281409832,0,4281935656,0,4283969331,0,4284231480,0,4283640886,0
  2143.     Data 4281804329,0,4282530611,0,0,7,4280360993,0,4283966519,0,4289407578,0,4291307870,0,4292290655,3
  2144.     Data 4292822873,0,4293355092,1,4292301655,0,4290458204,0,4290063198,0,4289473116,0,4284227658,0,4283703113,0
  2145.     Data 4288948570,0,4290980190,0,4292290655,1,4291700830,0,4290456158,0,4286325841,0,4281605186,0,4286850387,0
  2146.     Data 4290063198,1,4288490587,0,4286000983,0,4283507787,0,4282195008,0,4284030010,0,4285602886,0,4286717020,0
  2147.     Data 4287903061,0,4288825678,1,4289419088,0,4290605909,0,4290737750,0,4290474069,0,4289419088,0,4289018964,0
  2148.     Data 4289472096,0,4285603666,0,4281867587,0,4284623184,0,4284558153,0,4281016615,0,4281935656,0,4284034869,0
  2149.     Data 4285214537,0,4285476686,0,4284297017,0,4282329387,0,4280360993,0,0,6,4280360993,0,4282852661,0
  2150.     Data 4286590293,0,4285474128,0,4284489799,0,4285606464,0,4286000702,1,4286133564,0,4286463294,0,4287315021,0
  2151.     Data 4289283414,0,4291908438,0,4291248474,0,4289473114,0,4283572793,0,4281147179,0,4282458671,0,4282982960,0
  2152.     Data 4283375922,0,4283899967,0,4284096329,0,4283768649,0,4284228684,0,4284885585,0,4288096857,0,4290063198,1
  2153.     Data 4288490587,0,4286000983,0,4283507787,0,4282720835,0,4286264646,0,4288498254,0,4289617239,0,4290012501,0
  2154.     Data 4290341717,0,4290933086,0,4291592039,0,4292384370,0,4292516211,0,4292450163,0,4292186481,0,4291526505,0
  2155.     Data 4290602588,0,4288696146,0,4286724938,0,4284033352,0,4282064957,0,4280623141,0,4282590772,0,4285673294,0
  2156.     Data 4287704406,0,4288883802,0,4288621652,0,4284950077,0,4280360993,0,0,6,4280360993,0,4281540912,0
  2157.     Data 4283311176,0,4285080654,0,4286457168,0,4287114060,0,4287769675,0,4288555851,0,4288621387,0,4288162890,0
  2158.     Data 4286000709,0,4286199368,0,4288627795,0,4286854472,0,4284359739,0,4282130230,0,4280884782,0,4280426275,0
  2159.     Data 4280622889,0,4281081140,0,4281343036,0,4281933375,0,4282656059,0,4283771453,0,4284821314,0,4288097363,0
  2160.     Data 4290063198,1,4289342300,0,4288228442,0,4284490572,0,4282917956,0,4286987595,0,4289485137,0,4290605909,0
  2161.     Data 4291131739,0,4291526498,0,4292317038,0,4292713078,0,4293043324,0,4293109117,2,4292779639,0,4292186990,0
  2162.     Data 4290935135,0,4289355089,0,4284166471,0,4281277753,0,4280491813,0,4282787384,0,4286197335,0,4288490587,0
  2163.     Data 4288817242,0,4286194769,0,4283441218,0,4281015603,0,4282004533,0,4283124027,0,4284900427,0,0,3
  2164.     Data 4280360993,0,4281802800,0,4283965512,0,4287506005,0,4290063198,1,4290718302,0,4292094046,0,4292424029,0
  2165.     Data 4291574103,0,4287050567,0,4285017925,0,4285804371,0,4285152330,0,4284368958,0,4284629064,0,4283708482,0
  2166.     Data 4280886054,0,4280884783,0,4281605186,1,4282196286,0,4283509813,0,4283641141,0,4283181367,0,4286655310,0
  2167.     Data 4289014876,0,4289801053,0,4290063198,1,4285276750,0,4282917956,0,4286987595,0,4289485137,0,4290605909,0
  2168.     Data 4291394656,0,4292118122,0,4292779127,0,4293109117,6,4292054892,0,4290474842,0,4284561738,0,4281277753,0
  2169.     Data 4280491813,0,4282787384,0,4286066262,0,4286851413,0,4286325586,0,4283964748,0,4284034639,0,4285154900,0
  2170.     Data 4284432706,0,4283908665,0,4283846719,0,4284439879,0,0,2,4280360993,0,4283966519,0,4289407578,0
  2171.     Data 4290063198,0,4290062942,0,4290128478,0,4290849118,0,4292159582,0,4292822873,0,4292370260,0,4288168020,0
  2172.     Data 4286001237,0,4286198358,0,4289752684,0,4293109632,0,4292382576,0,4289096535,0,4281805866,0,4281015856,0
  2173.     Data 4281867587,1,4282458431,0,4283837496,0,4283377467,0,4282129475,0,4284360526,0,4286459223,0,4289014620,0
  2174.     Data 4289866589,0,4290063198,0,4285276750,0,4282917700,0,4287052619,0,4289484625,0,4290474069,0,4291328863,0
  2175.     Data 4292118122,0,4292779127,0,4293109117,4,4293043324,0,4293043067,0,4292448881,0,4291263076,0,4284824397,0
  2176.     Data 4281343290,0,4280688678,0,4282787384,0,4285475924,0,4283376713,0,4283309897,0,4286128473,0,4289748333,0
  2177.     Data 4293175681,0,4292448882,0,4289818971,0,4282528302,0,4282268723,0,0,2,4280360993,0,4283966519,0
  2178.     Data 4289407578,0,4290063198,0,4290259550,0,4291373406,0,4291962974,0,4292225118,0,4292822873,0,4292173648,0
  2179.     Data 4287119425,0,4286005570,0,4289159506,0,4292191113,0,4294498744,0,4293374606,0,4289756771,0,4281872172,0
  2180.     Data 4282459446,0,4285147987,1,4286065491,0,4288162645,0,4288686679,0,4288424024,0,4284686924,0,4283179593,0
  2181.     Data 4285934420,0,4287966554,0,4290063198,0,4285276750,0,4283047495,0,4287440215,0,4289345625,0,4289154640,0
  2182.     Data 4290735197,0,4292118122,0,4292779127,0,4293109117,4,4292779127,0,4292250734,0,4291526754,0,4290343511,0
  2183.     Data 4284495945,0,4281999421,0,4283050545,0,4282853177,0,4282720582,0,4285543497,0,4288104782,0,4289486421,0
  2184.     Data 4291991939,0,4294565308,0,4293506706,0,4290545256,0,4282660657,0,4282268723,0,0,2,4280360993,0
  2185.     Data 4283966519,0,4289407578,0,4290783582,0,4291636573,0,4292560217,0,4292955736,1,4292822873,0,4291575125,0
  2186.     Data 4286657599,0,4286072125,0,4289883473,0,4292254328,0,4293639062,0,4292254069,0,4288965976,0,4282395195,0
  2187.     Data 4283245892,0,4286197335,0,4288425050,0,4290259548,0,4291111262,0,4290783582,0,4290063198,0,4288030551,0
  2188.     Data 4286194769,0,4283900747,0,4284819279,0,4287507801,0,4284162892,0,4282260037,0,4284225102,0,4285537867,0
  2189.     Data 4286262081,0,4287707475,0,4288956258,0,4290011488,0,4290802785,0,4291528036,0,4291594084,2,4291461986,0
  2190.     Data 4291198045,0,4290341462,0,4289025616,0,4284034886,0,4283113540,0,4286982732,0,4284687935,0,4281278767,0
  2191.     Data 4285806913,0,4289287248,0,4290342228,0,4292056691,0,4293771162,0,4292385912,0,4289492055,0,4282463021,0
  2192.     Data 4282268723,0,0,2,4280360993,0,4283114545,0,4287310923,0,4288555854,0,4289343311,0,4290201427,0
  2193.     Data 4290335056,0,4289876296,0,4289478219,0,4288490315,0,4285541947,0,4285480254,0,4288304465,0,4289686114,0
  2194.     Data 4290213483,0,4287914064,0,4285942084,0,4283640136,0,4285081424,0,4287769945,0,4290456156,0,4292357469,0
  2195.     Data 4292557403,0,4292034650,0,4291248474,0,4291048028,0,4289144665,0,4283309639,0,4283245385,0,4286000983,0
  2196.     Data 4283507787,0,4282195267,0,4284031304,0,4285409094,0,4286262336,0,4287707475,0,4288956258,0,4290011488,0
  2197.     Data 4290802785,0,4291528036,0,4291594084,4,4288831319,0,4286002761,0,4285147211,0,4286063440,0,4289341787,0
  2198.     Data 4285802051,0,4280820004,0,4285148993,0,4288364373,0,4289023576,0,4289880420,0,4290736497,0,4288307283,0
  2199.     Data 4285944127,0,4283121973,0,4283452990,0,0,2,4280953127,0,4281936682,0,4283313456,0,4284232756,0
  2200.     Data 4284954427,0,4285936462,0,4285542475,0,4283903031,0,4284297782,0,4284823863,0,4284955706,0,4285545283,0
  2201.     Data 4286396499,0,4286001749,0,4285082449,0,4281934641,0,4281738030,0,4285279568,0,4287835482,0,4290456158,0
  2202.     Data 4291504222,0,4292556636,0,4293155414,0,4293355092,1,4292689754,0,4290324057,0,4283834437,0,4283179847,0
  2203.     Data 4285410388,0,4283638604,0,4283442247,0,4287184203,0,4288825678,1,4290472027,0,4291921000,0,4292647029,0
  2204.     Data 4292977019,0,4293043324,0,4293109117,2,4292977531,0,4292845432,0,4287192413,0,4282129475,0,4287374933,0
  2205.     Data 4290063198,1,4285802051,0,4280754212,0,4284555590,0,4287112029,0,4287243869,0,4287177821,0,4287045980,0
  2206.     Data 4282851383,0,4281282090,0,0,4,4284834635,0,4283320892,0,4281017893,0,4285877309,0,4289685585,0
  2207.     Data 4288827215,0,4286592332,0,4282979397,0,4286267211,0,4290081109,0,4291200356,0,4291265893,0,4290343256,0
  2208.     Data 4286137677,0,4281998914,0,4280885036,0,4281738030,0,4285279568,0,4288621658,0,4292028510,0,4292159582,0
  2209.     Data 4292556636,0,4293155414,0,4293355092,1,4292689754,0,4290652503,0,4285279292,0,4283049786,0,4282129988,0
  2210.     Data 4284423505,0,4286653528,0,4288167249,0,4288825678,1,4289946451,0,4291000666,0,4291789414,0,4292382576,0
  2211.     Data 4293043067,0,4293109117,2,4292450418,0,4291264863,0,4286270286,0,4282129475,0,4287374933,0,4290063198,1
  2212.     Data 4285802051,0,4280820259,0,4285478462,0,4288957777,0,4289880917,0,4289485140,0,4288562000,0,4283378994,0
  2213.     Data 4281282090,0,0,5,4283519039,0,4280624164,0,4285414971,0,4289287504,0,4288957518,0,4286789196,0
  2214.     Data 4282847304,0,4285936204,0,4289616978,0,4290802778,0,4291198556,0,4290803543,0,4285680192,0,4280950572,0
  2215.     Data 4283115069,0,4285344331,0,4288162905,0,4289604444,0,4290849118,0,4291700830,0,4292357214,0,4292556636,0
  2216.     Data 4292623195,1,4291440733,0,4289473366,0,4285214267,0,4282722608,0,4280688171,0,4284161352,0,4287307614,0
  2217.     Data 4288686429,0,4289149015,0,4288825166,0,4289155151,0,4289616721,0,4290670936,0,4291198301,0,4291396705,0
  2218.     Data 4291462242,0,4291264609,0,4290539358,0,4288108623,0,4284366902,0,4285476931,0,4287571541,0,4289210715,0
  2219.     Data 4290063198,1,4285802051,0,4280885795,0,4285610301,0,4288957518,0,4289287248,0,4286596161,0,4282985519,0
  2220.     Data 4283516218,0,0,6,4284374086,0,4282729527,0,4283972408,0,4285085498,1,4283968569,0,4281734967,0
  2221.     Data 4283311672,0,4285217338,0,4285810749,0,4286140478,1,4283512631,0,4281278258,0,4285605964,0,4287901018,0
  2222.     Data 4288162906,0,4289014620,0,4290128478,0,4290783582,0,4291307870,3,4290521694,0,4288883541,0,4285083451,0
  2223.     Data 4282918450,0,4281276210,0,4283307589,0,4285536087,0,4287503454,0,4288620637,0,4289082967,0,4289148759,0
  2224.     Data 4289215830,0,4289681235,0,4289880658,1,4288043336,0,4286008638,0,4285414971,0,4284363065,0,4283050040,0
  2225.     Data 4286392395,0,4290063198,1,4289604445,0,4288490587,0,4284753729,0,4280623394,0,4283313712,0,4285085498,1
  2226.     Data 4283972408,0,4282597686,0,4284374086,0,0,8,4283124027,0,4281282090,1,4281018663,0,4280360993,0
  2227.     Data 4280889648,0,4281615424,0,4282533184,0,4282268216,0,4280886314,0,4280950321,0,4281867581,0,4287309395,0
  2228.     Data 4289014876,0,4286459479,0,4287442265,0,4289276764,5,4289735517,0,4288752469,0,4284558392,0,4282786358,0
  2229.     Data 4282060864,1,4282716740,0,4284684622,0,4286520918,0,4287897692,0,4288094300,0,4287964250,0,4287576144,0
  2230.     Data 4287381577,0,4287250757,0,4284101172,0,4281017124,1,4282525236,0,4285345104,0,4287638873,0,4289342300,0
  2231.     Data 4289801053,0,4288752473,0,4285541455,0,4283313469,0,4281282090,3,4283124027,0,0,14,4280624163,0
  2232.     Data 4283136368,0,4286895295,0,4291485117,0,4290094229,0,4283053135,0,4280950321,0,4281016358,0,4287047499,0
  2233.     Data 4289014876,0,4286459479,0,4286066263,0,4286197335,5,4288621659,0,4288227666,0,4282327597,0,4280492066,0
  2234.     Data 4280491555,2,4280688420,0,4281212464,0,4281998402,0,4282063940,0,4282063939,0,4282064451,0,4281671481,0
  2235.     Data 4280951333,0,4282263594,0,4283707185,1,4284428093,0,4285673298,0,4286066263,0,4286459479,0,4288818267,0
  2236.     Data 4287703119,0,4281868843,0,4282662966,0,4285163598,3,4285295183,0,0,16,4282077259,0,4283322954,0
  2237.     Data 4284110155,0,4284438604,0,4282399799,0,4280623138,0,4283836980,0,4285607492,0,4285935187,0,4286000983,4
  2238.     Data 4287311193,0,4289014876,0,4287769946,0,4285737806,0,4281606699,0,4280360993,1,4279842646,0,4279454336,1
  2239.     Data 4279908703,0,4280557870,0,4281146679,0,4281605186,1,4281999675,0,4282919727,0,4283510320,0,4283969587,1
  2240.     Data 4283509561,0,4282851908,0,4282654791,0,4282720327,0,4283375688,0,4282851135,0,4280753958,0,4280887334,0
  2241.     Data 4281611053,1,4282597686,0,4284768842,0,0,17,4282926649,1,4283716160,0,4285163598,0,4282926649,0
  2242.     Data 4280426529,0,4281541927,0,4282853680,0,4284624449,0,4284952389,4,4285082434,0,4285211967,0,4284294471,0
  2243.     Data 4283114564,0,4280951081,0,4280360993,1,4279971652,0,4279648620,0,4279325072,0,4279583600,0,4280230696,0
  2244.     Data 4281672495,0,4282787386,1,4283115832,0,4283838259,0,4283378486,0,4282787386,1,4282918972,0,4283182656,0
  2245.     Data 4284232004,0,4285215304,1,4284230467,0,4281866806,0,4283182391,0,4285483323,0,4284890169,0,4284168503,0
  2246.     Data 4283188024,0,4284045123,0,0,20,4282926649,0,4280492321,0,4282134058,0,4283512113,0,4284888632,0
  2247.     Data 4284428089,0,4283705400,0,4284823354,0,4285809726,0,4286139203,0,4284892221,0,4283250738,0,4282328125,0
  2248.     Data 4281932607,0,4282328624,0,4282592813,0,4282922286,0,4281477414,0,4280166194,0,4279648104,0,4279777884,0
  2249.     Data 4280295974,0,4281869864,0,4283050799,0,4283247412,0,4283378486,1,4282853436,0,4282393921,1,4283445058,0
  2250.     Data 4285415491,0,4287317576,0,4288825678,0,4289089615,0,4287710541,0,4283834695,0,4286399053,0,4290605909,0
  2251.     Data 4289814610,0,4287055684,0,4282001705,0,4282597686,0,0,20,4282926649,0,4280952100,0,4287454021,0
  2252.     Data 4290210388,0,4289023567,0,4286000715,0,4282913608,0,4287581776,0,4291330399,0,4292648053,0,4292580723,0
  2253.     Data 4291986024,0,4286003284,0,4283635785,0,4287577420,0,4289485137,0,4290737750,0,4284892472,0,4280360993,4
  2254.     Data 4280491813,0,4281277753,0,4281605186,1,4283248449,0,4284891968,1,4286071876,0,4288301132,0,4288825678,0
  2255.     Data 4288891726,0,4290078547,0,4289227088,0,4285614402,0,4287387977,0,4290737750,1,4288110664,0,4282265642,0
  2256.     Data 4282597686,0,0,20,4282926649,0,4280952100,0,4287454021,0,4290605909,0,4290408020,0,4287714892,0
  2257.     Data 4284890948,0,4289558114,0,4292714103,0,4292977531,0,4292977275,0,4292845177,0,4287585878,0,4285678663,0
  2258.     Data 4290079580,0,4291525219,0,4291723108,0,4285286718,0,4281084712,0,4283452990,0,4284505672,1,4282137394,0
  2259.     Data 4280820516,0,4283249720,0,4284234560,1,4284563264,0,4284891968,1,4286071876,0,4288301132,0,4288825678,1
  2260.     Data 4289023567,0,4287578701,0,4283637319,0,4285608010,0,4289155407,1,4286924099,0,4282001705,0,4282597686,0
  2261.     Data 0,20,4282926649,0,4280952100,0,4287454021,0,4290408276,0,4289748818,0,4286923851,0,4284033606,0
  2262.     Data 4288635994,0,4291923049,0,4292317295,0,4292448625,1,4286860629,0,4284756040,0,4288894549,0,4290866526,0
  2263.     Data 4291986024,0,4285418048,0,4281282090,0,4284176709,0,0,1,4284176965,0,4283123514,0,4282529329,0
  2264.     Data 4282330926,4,4282855472,0,4283838771,0,4284035380,2,4283312948,0,4281604658,0,4282458931,0,4284035380,1
  2265.     Data 4283840566,0,4283385402,0,4284176965,0,0,20,4283716160,0,4282268209,0,4285747262,0,4287124292,0
  2266.     Data 4286332992,0,4284230206,0,4282128188,0,4285349953,0,4287651144,0,4288176463,0,4288308049,1,4284232004,0
  2267.     Data 4282587708,0,4285346879,0,4286990918,0,4288308049,0,4284697406,0,4282597686,0,4284571464,0,0,2
  2268.     Data 4285032012,0,4282795320,0,4281940016,14,4282795320,0,4284769098,0,0,22,4285163598,0,4281940016,0
  2269.     Data 4280360993,13,4283190076,0,0,32
  2270.  
  2271.     For i = 0 To 2
  2272.  
  2273.         bm(i) = _NewImage(64, 64, 32)
  2274.         _Dest bm(i)
  2275.  
  2276.         Read cl
  2277.         Read rleCount
  2278.  
  2279.         For y = 0 To 63
  2280.             For x = 0 To 63
  2281.                 PSet (x, y), cl
  2282.                 rleCount = rleCount - 1
  2283.                 If rleCount < 0 Then
  2284.                     Read cl
  2285.                     Read rleCount
  2286.                 End If
  2287.             Next
  2288.         Next
  2289.     Next
  2290.  
  2291.     _Dest 0
  2292.  

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D Physics Engine
« Reply #11 on: April 24, 2021, 12:00:34 am »
justsomeguy

Is vector addition useful for 2D collisions of balls?    The balls have velocity and direction. When they collide do the vectors pass through the centers of the balls?

The other thing is that the result of the addition is a single vector of a certain magnitude and direction but how doe that apply to new directions and velocity after the collision?

Can the math for a 2 ball collision be derived from vector addition alone?


« Last Edit: April 24, 2021, 03:57:29 am by NOVARSEG »

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: 2D Physics Engine
« Reply #12 on: April 24, 2021, 08:40:53 am »
THIS is REALLY impressive.... Well done!!

J
Logic is the beginning of wisdom.

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D Physics Engine
« Reply #13 on: April 24, 2021, 08:48:30 am »
Hello

I hope I can answer your questions correctly.
Quote
Is vector addition useful for 2D collisions of balls?
I will say that vector math is essential for the 2d collision of balls or circles in this case. Vectors are simply a data type that I use that hold two numbers. The vectors can be used as a position, a velocity ( x axis speed, y axis speed), or a vertex of a polygon.
Code: QB64: [Select]
  1. Type tVECTOR2d
  2.     x As _Float
  3.     y As _Float
A collision between two circles can be derived when the distance between the circles is less than both of their radius added together.
Code: QB64: [Select]
  1.     Call vectorSubVectorND(normal, B.position, A.position) ' Subtract two vectors position A and position B
  2.     dist_sqr = vectorLengthSq(normal)  ' Calculate the distance between the balls or circles
  3.     radius = A.shape.radius + B.shape.radius ' Add both circle A and circle B radius
  4.  
  5.     If (dist_sqr >= radius * radius) Then ' Deterime if they have collided
  6.               'No collision
  7.     Else
  8.               'Collision has occured
  9.  

Code: QB64: [Select]
  1. Function vectorLengthSq (v As tVECTOR2d)
  2.     vectorLengthSq = v.x * v.x + v.y * v.y

The method employed is a faster method of calculating collisions because it does not use square roots.

Quote
The balls have velocity and direction. When they collide do the vectors pass through the centers of the balls?

Not necessarily. A glancing blow from two balls must also be accounted for. A ball hitting another ball straight on versus a glancing blow will result in very different trajectories for both balls.

Quote
The other thing is that the result of the addition is a single vector of a certain magnitude and direction but how doe that apply to new directions and velocity after the collision?

Well that is not an easy answer. Here is a wikipedia article https://en.wikipedia.org/wiki/Elastic_collision. I apologize if that is a cop out answer, but classes are taught on the math involved and I'm no expert on the math, I simply ported the physics code from C++/Java.

Thanks for the question. If you have any other questions let me know.
 


Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D Physics Engine
« Reply #14 on: April 24, 2021, 09:25:08 pm »
Lets say 2 point masses collide at 0, 0  both have equal velocity.

The vector sum of the two masses give the  direction / velocity of both masses at the moment of collision.

After the collision, the masses head in new direction / velocity determine by the vector sum.
Ok that is the theory

Object 1  =  x = -2, y = -2   heading towards 0, 0

object 2   =   x =2, y = -2 heading towards 0, 0


Say the vector sum points away from 0, 0  so

Object 1  =  x = 2, y = 2   heading away from 0, 0

object 2   =   x = - 2, y = 2 heading away from 0, 0

vector sum =   (2+ -2 )x,  (2 + 2 )y =  0x, 4y  which seems to make sense as the objects collide at right angles in this case.

Also the new direction  of the objects should be at right angles to the original directions

The directions before collision are

Object 1  =  x = -2, y = -2   heading towards 0, 0

object 2   =   x =2, y = -2 heading towards 0, 0

Find the direction / velocity after collision

vector sum of object  1 with 0x ,4y =  -2x,   2y   heading away from 0, 0
(the new direction is right angle to the original direction.)

vector sum of object  2 with 0x ,4y =  2x,   2y  heading away from 0, 0
(the new direction is right angle to the original direction.)

Might be wrong but it does seem to work for this simple case.
********************************************************

Another one

Object 1  =  x = -2, y = -2   heading towards 0, 0

object 2   =   x =2,  y = 2   heading towards 0, 0

Intuition says the objects reverse direction after collision.  The vector sum is

(-2 + 2)x,   (-2 + 2)y  = 0x,,0y

Vector sum of Object 1  with 0x, 0y =  (-2 + 0)x,   (-2 + 0)y = -2x,  -2y   heading away from 0, 0


« Last Edit: April 24, 2021, 10:59:29 pm by NOVARSEG »