'$dynamic
'**********************************************************************************************
'   FzxNGN written by justsomeguy
'   Physics code ported from RandyGaul's Impulse Engine
'   https://github.com/RandyGaul/ImpulseEngine
'   http://RandyGaul.net
'**********************************************************************************************
'/*
'    Copyright (c) 2013 Randy Gaul http://RandyGaul.net
 
'    This software is provided 'as-is', without any express or implied
'    warranty. In no event will the authors be held liable for any damages
'    arising from the use of this software.
 
'    Permission is granted to anyone to use this software for any purpose,
'    including commercial applications, and to alter it and redistribute it
'    freely, subject to the following restrictions:
'      1. The origin of this software must not be misrepresented; you must not
'         claim that you wrote the original software. If you use this software
'         in a product, an acknowledgment in the product documentation would be
'         appreciated but is not required.
'      2. Altered source versions must be plainly marked as such, and must not be
'         misrepresented as being the original software.
'      3. This notice may not be removed or altered from any source distribution.
'
'    Port to QB64 by justsomeguy
'*/
'
 
'**********************************************************************************************
'   Setup Types and Variables
'**********************************************************************************************
 
 
 
TYPE tTRIANGLE 
' Not used  
 
    ty 
AS INTEGER ' cSHAPE_CIRCLE = 1, cSHAPE_POLYGON = 2    radius 
AS _FLOAT ' Only necessary for circle shapes    u 
AS tMATRIX2d 
' Only necessary for polygons    flipTexture 
AS INTEGER 'flag for flipping texture depending on direction 
TYPE tPOLY 
'list of vertices for all objects in simulation  
TYPE tPOLYATTRIB 
'keep track of polys in monlithic list of vertices     start 
AS INTEGER ' starting vertex of the polygon    count 
AS INTEGER ' number of vertices in polygon 
 
 
 
    localAnchor1 
AS tVECTOR2d
    localAnchor2 
AS tVECTOR2d
 
 
    terrainPosition 
AS tVECTOR2d
 
    wheelOneOffset 
AS tVECTOR2d
    wheelTwoOffset 
AS tVECTOR2d
    auxOneOffset 
AS tVECTOR2d
 
 
 
CONST cMAXNUMOFTRIANGLES 
= 100 CONST cMAXNUMBEROFOBJECTS 
= 1000 ' Max number of objects at one time CONST cMAXNUMBEROFPOLYGONS 
= 10000 ' Max number of total vertices included in all objects CONST cMAXNUMBEROFJOINTS 
= 100 CONST cMAXNUMBEROFHITS 
= 10000 CONST cEPSILON_SQ 
= cEPSILON 
* cEPSILON
 CONST cBIAS_RELATIVE 
= 0.95 CONST cBIAS_ABSOLUTE 
= 0.01 CONST cPENETRATION_ALLOWANCE 
= 0.05 CONST cPENETRATION_CORRECTION 
= 0.4 ' misspelled in original code CONST cGLOBAL_FRICTION 
= .992 CONST cPARAMETER_POSITION 
= 1 CONST cPARAMETER_VELOCITY 
= 2 CONST cPARAMETER_FORCE 
= 3 CONST cPARAMETER_ANGULARVELOCITY 
= 4 CONST cPARAMETER_TORQUE 
= 5 CONST cPARAMETER_ORIENT 
= 6 CONST cPARAMETER_STATICFRICTION 
= 7 CONST cPARAMETER_DYNAMICFRICTION 
= 8 CONST cPARAMETER_RESTITUTION 
= 9 CONST cPARAMETER_COLOR 
= 10 CONST cPARAMETER_ENABLE 
= 11 CONST cPARAMETER_STATIC 
= 12 CONST cPARAMETER_TEXTURE 
= 13 CONST cPARAMETER_FLIPTEXTURE 
= 14 CONST cPARAMETER_COLLISIONMASK 
= 15 CONST cRENDER_COLLISIONS 
= 0 CONST cPLAYER_FORCE 
= 600000  
DIM SHARED sNUMBEROFJOINTS 
AS INTEGER: sNUMBEROFJOINTS 
= 3 ' if zero then no joints at all  
DIM poly
(cMAXNUMBEROFPOLYGONS
) AS tPOLY
 DIM body
(sNUMBEROFBODIES
) AS tBODY
 DIM joints
(cMAXNUMBEROFJOINTS
) AS tJOINT
 DIM hits
(cMAXNUMBEROFHITS
) AS tHIT
 DIM veh
(sNUMBEROFVEHICLES
) AS tVEHICLE
 DIM inputDevice 
AS tINPUTDEVICE
  
 
 
    rackposition 
AS tVECTOR2d
 
 
 
 
'**********************************************************************************************
camera.zoom = .25
'RANDOMIZE TIMER
CALL buildSimpleScene
(poly
(), body
(), joints
(), texture
(), veh
()) '**********************************************************************************************
 
    CALL renderBodies
(poly
(), body
(), joints
(), hits
(), camera
)     CALL animateScene
(poly
(), body
(), joints
(), hits
(), texture
(), camera
, veh
(), inputDevice
)     CALL impulseStep
(poly
(), body
(), joints
(), hits
(), cDT
, cITERATIONS
)  
'**********************************************************************************************
'   End of Main loop
'**********************************************************************************************
 
 
'**********************************************************************************************
'   Scene Creation and Handling Ahead
'**********************************************************************************************
SUB animateScene 
(poly
() AS tPOLY
, body
() AS tBODY
, joints
() AS tJOINT
, hits
() AS tHIT
, textureMap
() AS LONG, camera 
AS tCAMERA
, veh
() AS tVEHICLE
, iDevice 
AS tINPUTDEVICE
)     ' Cache some widely used ID's
    DIM cueBall 
AS _INTEGER64: cueBall 
= objectManagerID
(body
(), "cSCENE_BALL16")  
    DIM AS _FLOAT cueAngle
, cueDistance
, cueDrawingDistance
     DIM AS tVECTOR2d cueNormal
, cuePos
, cueBall2Camera
, cuePower
, mousePosVector
  
    ' Ignore this
    veh(0).vehicleName = "Nothing" 'this is just to clear the warnings
 
    ' Mouse and keyboard handling, mainly mouse, keyboard is too slow for indirect
    CALL handleInputDevice
(iDevice
)     ' Change cueball to camera coordinate system.
    CALL worldToCamera
(body
(), camera
, cueBall
, cueBall2Camera
)     CALL vectorSet
(mousePosVector
, iDevice.x
, iDevice.y
)  
    ' Some Maths
    cueNormal.x = cueBall2Camera.x - iDevice.x ' x-Component of Distance
    cueNormal.y = cueBall2Camera.y - iDevice.y ' y-Component of Distance
    cueAngle 
= _ATAN2(-cueNormal.y
, -cueNormal.x
) ' Use mouse to control the cue angle    cueDistance = vectorDistance(mousePosVector, cueBall2Camera)
    cueDrawingDistance = (cueDistance * .25) + 450 ' Use this to guage power
    cuePos.x 
= COS(cueAngle
) * cueDrawingDistance 
+ body
(cueBall
).position.x 
' Use these to calculate cue position    cuePos.y 
= SIN(cueAngle
) * cueDrawingDistance 
+ body
(cueBall
).position.y
    cuePower.x = cueNormal.x * cPLAYER_FORCE
    cuePower.y = cueNormal.y * cPLAYER_FORCE
 
    guideLine.a.x 
= COS(cueAngle 
+ _PI) * (25 * camera.zoom
) + cueBall2Camera.x
    guideLine.a.y 
= SIN(cueAngle 
+ _PI) * (25 * camera.zoom
) + cueBall2Camera.y
    guideLine.b.x 
= COS(cueAngle 
+ _PI) * (1000 * camera.zoom
) + cueBall2Camera.x
    guideLine.b.y 
= SIN(cueAngle 
+ _PI) * (1000 * camera.zoom
) + cueBall2Camera.y
 
    ' Aim the Stick
    IF bodyAtRest
(body
(cueBall
)) THEN ' AIM         CALL setBody
(body
(), cPARAMETER_ENABLE
, cue
, 1, 0)         CALL setBody
(body
(), cPARAMETER_ORIENT
, cue
, cueAngle
, 0)         CALL setBody
(body
(), cPARAMETER_POSITION
, cue
, cuePos.x
, cuePos.y
)         LINE (guideLine.a.x
, guideLine.a.y
)-(guideLine.b.x
, guideLine.b.y
)         camera.position = body(cueBall).position
        CALL setBody
(body
(), cPARAMETER_ENABLE
, cue
, 0, 0)  
    ' Camera Zoom
    camera.zoom = camera.zoom + (iDevice.wCount * .01): iDevice.wCount = 0
    IF camera.zoom 
< .125 THEN camera.zoom 
= .125  
    ' Hit the ball
    ' Verify cue ball is not moving - I really need to check that all balls are not moving.
    IF bodyAtRest
(body
(cueBall
)) THEN         IF iDevice.lb1 
= -1 AND iDevice.b1 
= 0 THEN ' only fire once             'Apply hitting Force and English
            CALL setBody
(body
(), cPARAMETER_FORCE
, cueBall
, cuePower.x
, cuePower.y
)             CALL setBody
(body
(), cPARAMETER_ANGULARVELOCITY
, cueBall
, genPool.english.x
, 0)             ' Reset English
            genPool.english.x = 0
            genPool.english.y = 0
 
        genPool.english.x = genPool.english.x + 1
        IF genPool.english.x 
> 200 THEN genPool.english.x 
= 200  
        genPool.english.x = genPool.english.x - 1
        IF genPool.english.x 
< -200 THEN genPool.english.x 
= -200  
    ' Instruction GUI
        genPool.instructionImage 
= _COPYIMAGE(textureMap
(22))        genPool.instructions = 1
    IF TIMER - sTIMER 
> 20 THEN genPool.instructions 
= 0 ' instruction timeout 20 sec         genPool.instructionImage 
= _COPYIMAGE(textureMap
(22))        IF TIMER - sTIMER 
> 15 THEN 'After 15 seconds fade it out         _PUTIMAGE (0, 0), genPool.instructionImage
, 0 ' INSTRUCTIONS  
        ' Put all balls back in their rack positions
 
        ' Verify cue ball is not moving - I really need to check that all balls are not moving.
        IF bodyAtRest
(body
(cueBall
)) THEN             CALL setBody
(body
(), cPARAMETER_ANGULARVELOCITY
, cueBall
, genPool.english.x
, 0)             CALL setBody
(body
(), cPARAMETER_FORCE
, cueBall
, cuePower.x 
* 2, cuePower.y 
* 2)             genPool.english.x = 0
            genPool.english.y = 0
 
    ' Check your shot feature
        DIM ghostBody
(UBOUND(body
)) AS tBODY 
' Create a new list of bodies         CALL copyBodies
(body
(), ghostBody
()) ' Copy all the bodies to the new list         ' Apply Forces to the Cue Ball in the Bodies
        CALL setBody
(ghostBody
(), cPARAMETER_FORCE
, cueBall
, cuePower.x
, cuePower.y
)         CALL setBody
(ghostBody
(), cPARAMETER_ANGULARVELOCITY
, cueBall
, genPool.english.x
, 0)         ' Create a Ghost Image Overlay
        ' Hide the table since we only car abou the balls
        CALL setBodyEx
(ghostBody
(), cPARAMETER_ENABLE
, "cSCENE_TABLE", 0, 0)         ' Run Simulation for about 2 secs
        FOR index 
= 0 TO 240 ' number of frames for look ahead .. 240 / 120 fps = 2 sec             CALL impulseStep
(poly
(), ghostBody
(), joints
(), hits
(), cDT
, cITERATIONS
)             CALL handlePocketSensors
(ghostBody
(), hits
(), cueBall
)             CALL renderBodies
(poly
(), ghostBody
(), joints
(), hits
(), camera
)         ' Return Screen to normal
        ' Display overlay
        ' Free up overlay
 
    ballCount = 0
    FOR index 
= 1 TO 16 'Make balls roll across ball return         ' check if balls are off the table and then give it "gravity"
        IF body
(pool
(index
).objId
).position.y 
> 750 THEN             CALL setBody
(body
(), cPARAMETER_FORCE
, pool
(index
).objId
, 200000, 100000)             ballCount = ballCount + 1
        ' rerack table if all the balls are in the ball return
        IF ballCount 
> 14 AND bodyAtRest
(body
(cueBall
)) THEN CALL reRackBalls
(body
())  
    ' Show english Indicator - Only Left and Right are supported
 
 
    CALL handlePocketSensors
(body
(), hits
(), cueBall
)     CALL cleanUpInputDevice
(iDevice
)  
    FOR index 
= 0 TO 5 ' check if ball has been pocketed         ballId = isBodyTouching(hits(), poolSensors(index).objId)
                body(ballId).position = pool(16).rackposition ' Move to break position
                body(ballId).position = pool(17).rackposition ' Move to ball return
            CALL bodyStop
(body
(ballId
))  
SUB reRackBalls 
(body
() AS tBODY
)         body(pool(index).objId).position = pool(index).rackposition
        CALL bodyStop
(body
(pool
(index
).objId
))  
SUB handleInputDevice 
(iDevice 
AS tINPUTDEVICE
)     ' iDevice.keyHit = _KEYHIT ' --- THIS IS TOO SLOW
        iDevice.wCount = iDevice.wCount + iDevice.w
 
SUB cleanUpInputDevice 
(iDevice 
AS tINPUTDEVICE
)     iDevice.lb1 = iDevice.b1
    iDevice.lb2 = iDevice.b2
    iDevice.lb3 = iDevice.b3
    iDevice.lastKeyHit = iDevice.keyHit
 
    bodyAtRest 
= (body.velocity.x 
< 1 AND body.velocity.x 
> -1 AND body.velocity.y 
< 1 AND body.velocity.y 
> -1) 
SUB copyBodies 
(body
() AS tBODY
, newBody
() AS tBODY
)         newBody(index) = body(index)
 
SUB buildSimpleScene 
(p
() AS tPOLY
, body
() AS tBODY
, j
() AS tJOINT
, textureMap
() AS LONG, v
() AS tVEHICLE
)  
    trans = 0 ' Adjust <0-255> to see the hidden rail bodies
    ballSize = 36
    bumperRestitution = .5
    bumperSF = .01 '.5
    bumperDF = .01 '.3
    j(0).jointName = "Nothing" ' this is just to clear the warning and does nothing
    v(0).vehicleName = "Nothing" ' Clear Warning
    CALL loadBitmap
(textureMap
())  
    '********************************************************
    '   Setup World
    '********************************************************
 
    CALL vectorSet
(world.minusLimit
, -6500, -2000)     CALL vectorSet
(world.plusLimit
, 70000, 10000)     CALL vectorSet
(world.spawn
, -5000, -800)     CALL vectorSet
(world.gravity
, 0.0, 0.0)     CALL vectorSet
(world.terrainPosition
, -7000.0, 1000.0)     DIM o 
AS tVECTOR2d: 
CALL vectorMultiplyScalarND
(o
, world.gravity
, cDT
): sRESTING 
= vectorLengthSq
(o
) + cEPSILON
  
    '********************************************************
    '   Build Table
    '********************************************************
 
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_TABLE", 1000, 600)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_TABLE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_TABLE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLLISIONMASK
, "cSCENE_TABLE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_TEXTURE
, "cSCENE_TABLE", textureMap
(19), 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURN", 600, 100)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BALLRETURN", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BALLRETURN", 0, 800)     CALL setBodyEx
(body
(), cPARAMETER_COLLISIONMASK
, "cSCENE_BALLRETURN", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_TEXTURE
, "cSCENE_BALLRETURN", textureMap
(21), 0)  
 
    '********************************************************
    '   Build Bumpers
    '********************************************************
 
    ' These are hidden using transparent colors
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER1", 370, 100)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER1", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER1", -445, -565)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER1", _RGBA32(0, 0, 255, trans
), 0) ' blue     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER1", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER1", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER1", bumperDF
, 0)  
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER2", 370, 100)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER2", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER2", -445, 565)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER2", _RGBA32(255, 0, 0, trans
), 0) ' red     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER2", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER2", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER2", bumperDF
, 0)  
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER3", 370, 100)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER3", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER3", 425, -565)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER3", _RGBA32(0, 255, 0, trans
), 0) ' green     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER3", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER3", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER3", bumperDF
, 0)  
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER4", 370, 100)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER4", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER4", 425, 565)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER4", _RGBA32(255, 255, 0, trans
), 0) ' red and green     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER4", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER4", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER4", bumperDF
, 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER5", 100, 390)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER5", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER5", -970, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER5", _RGBA32(255, 0, 255, trans
), 0) 'red and blue     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER5", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER5", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER5", bumperDF
, 0)  
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BUMPER6", 100, 390)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BUMPER6", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BUMPER6", 970, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BUMPER6", _RGBA32(0, 255, 255, trans
), 0) ' green and blue     CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, "cSCENE_BUMPER6", bumperRestitution
, 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BUMPER6", bumperSF
, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BUMPER6", bumperDF
, 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER1", 50, 600)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BORDER1", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BORDER1", 970, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BORDER1", _RGBA32(255, 255, 255, trans
), 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER2", 50, 600)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BORDER2", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BORDER2", -970, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BORDER2", _RGBA32(255, 255, 255, trans
), 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER3", 1000, 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BORDER3", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BORDER3", 0, -560)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BORDER3", _RGBA32(255, 255, 255, trans
), 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BORDER4", 1000, 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BORDER4", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BORDER4", 0, 560)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BORDER4", _RGBA32(255, 255, 255, trans
), 0)  
 
    '********************************************************
    '   Build Ball Return hidden surfaces
    '********************************************************
    ' These are hidden using transparent colors
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNBOTTOM", 600, 10)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BALLRETURNBOTTOM", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BALLRETURNBOTTOM", 0, 850)     CALL setBodyEx
(body
(), cPARAMETER_ORIENT
, "cSCENE_BALLRETURNBOTTOM", 0.01, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BALLRETURNBOTTOM", _RGBA32(255, 255, 255, trans
), 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BALLRETURNBOTTOM", 10, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BALLRETURNBOTTOM", 10, 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNTOP", 600, 10)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BALLRETURNTOP", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BALLRETURNTOP", 0, 750)     CALL setBodyEx
(body
(), cPARAMETER_ORIENT
, "cSCENE_BALLRETURNTOP", 0.01, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BALLRETURNTOP", _RGBA32(255, 255, 255, trans
), 0)     CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, "cSCENE_BALLRETURNTOP", 10, 0)     CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, "cSCENE_BALLRETURNTOP", 10, 0)  
 
 
 
    iD = createBoxBodiesEx(p(), body(), "cSCENE_BALLRETURNEND", 10, 40)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_BALLRETURNEND", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_BALLRETURNEND", 550, 800)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_BALLRETURNEND", _RGBA32(255, 255, 255, trans
), 0)  
 
    '********************************************************
    '   Pocket sensors
    '********************************************************
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR1", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR1", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR1", -5, 525)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR1", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(0).objId = objectManagerID(body(), "cSCENE_SENSOR1")
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR2", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR2", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR2", -5, -525)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR2", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(1).objId = objectManagerID(body(), "cSCENE_SENSOR2")
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR3", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR3", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR3", -900, -490)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR3", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(2).objId = objectManagerID(body(), "cSCENE_SENSOR3")
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR4", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR4", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR4", 900, -490)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR4", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(3).objId = objectManagerID(body(), "cSCENE_SENSOR4")
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR5", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR5", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR5", -900, 490)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR5", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(4).objId = objectManagerID(body(), "cSCENE_SENSOR5")
 
    iD = createCircleBodyEx(body(), "cSCENE_SENSOR6", 50)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_SENSOR6", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_SENSOR6", 900, 490)     CALL setBodyEx
(body
(), cPARAMETER_COLOR
, "cSCENE_SENSOR6", _RGBA32(255, 255, 255, trans
), 0)     poolSensors(5).objId = objectManagerID(body(), "cSCENE_SENSOR6")
 
    '********************************************************
    '   Place Balls
    '********************************************************
 
        CALL vectorSet
(pool
(index
).rackposition
, rX
, rY
)  
        iD = createCircleBodyEx(body(), b, ballSize)
        CALL setBodyEx
(body
(), cPARAMETER_TEXTURE
, b
, textureMap
(index
), 0)         CALL setBodyEx
(body
(), cPARAMETER_RESTITUTION
, b
, 1.00, 0)         CALL setBodyEx
(body
(), cPARAMETER_STATICFRICTION
, b
, .50, 0) '.5         CALL setBodyEx
(body
(), cPARAMETER_DYNAMICFRICTION
, b
, .30, 0) '.3         CALL setBodyEx
(body
(), cPARAMETER_POSITION
, b
, pool
(index
).rackposition.x
, pool
(index
).rackposition.y
)         pool(index).objId = iD
        pool(index).status = 0
 
    '********************************************************
    '   Cue Stick
    '********************************************************
 
    CALL vectorSet
(tv
, 500, 0)  
    iD = createBoxBodiesEx(p(), body(), "cSCENE_CUE", 400, 10)
    CALL setBodyEx
(body
(), cPARAMETER_STATIC
, "cSCENE_CUE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_POSITION
, "cSCENE_CUE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_COLLISIONMASK
, "cSCENE_CUE", 0, 0)     CALL setBodyEx
(body
(), cPARAMETER_TEXTURE
, "cSCENE_CUE", textureMap
(18), 0)  
 
    PRINT "QB Pool: No rules."     PRINT "- 'I' or 'i' to show these instructions."     PRINT "- Use Mouse to Aim."     PRINT "- Mouse also controls power."     PRINT "- Mousewheel to adjust zoom."     PRINT "- LEFT and RIGHT arrow keys for English."     PRINT "- 'R' or 'r' to rerack balls."     PRINT "- <SPACE> to hit harder. Example for a break."     PRINT "- <TAB> to test your shot. ;)"     genPool.instructions = 1
 
 
SUB bodyStop 
(body 
AS tBODY
)     CALL vectorSet
(body.velocity
, 0, 0)     body.angularVelocity = 0
 
SUB bodyOffset 
(body
() AS tBODY
, p
() AS tPOLY
, index 
AS LONG, vec 
AS tVECTOR2d
)     FOR i 
= 0 TO body
(index
).pa.count
         CALL vectorAddVector
(p
(body
(index
).pa.start 
+ i
).vert
, vec
)  
 
    objectContainsString = -1
    FOR j 
= start 
TO objectManager.objectCount
             objectContainsString = j
'**********************************************************************************************
'   Collision Helper Tools
'**********************************************************************************************
 
    isBodyTouchingBody = 0
    DO WHILE hits
(hitcount
).A 
<> hits
(hitcount
).B
         IF hits
(hitcount
).A 
= A 
AND hits
(hitcount
).B 
= B 
THEN             isBodyTouchingBody = hitcount
 
    isBodyTouchingStatic = 0
    DO WHILE hits
(hitcount
).A 
<> hits
(hitcount
).B
             IF body
(hits
(hitcount
).B
).mass 
= 0 THEN                 isBodyTouchingStatic = hitcount
                IF body
(hits
(hitcount
).A
).mass 
= 0 THEN                     isBodyTouchingStatic = hitcount
 
    isBodyTouching = 0
    DO WHILE hits
(hitcount
).A 
<> hits
(hitcount
).B
             isBodyTouching = hits(hitcount).B
            isBodyTouching = hits(hitcount).A
 
 
FUNCTION highestCollisionVelocity 
(hits
() AS tHIT
, A 
AS INTEGER) ' this function is a bit dubious and may not do as you think     highestCollisionVelocity = 0
    DO WHILE hits
(hitcount
).A 
<> hits
(hitcount
).B
         IF hits
(hitcount
).A 
= A 
AND ABS(hits
(hitcount
).cv
) > hiCv 
AND hits
(hitcount
).cv 
< 0 THEN             hiCv 
= ABS(hits
(hitcount
).cv
)    highestCollisionVelocity = hiCv
 
'**********************************************************************************************
'   Physics and Collision Stuff Ahead
'**********************************************************************************************
 
    dts = dt * .5
    CALL vectorAddVectorScalar
(b.velocity
, b.force
, b.invMass 
* dts
)     CALL vectorAddVectorScalar
(b.velocity
, world.gravity
, dts
)     b.angularVelocity = b.angularVelocity + (b.torque * b.invInertia * dts)
 
    body.velocity.x = body.velocity.x * (1 - dt)
    body.velocity.y = body.velocity.y * (1 - dt)
    body.angularVelocity = body.angularVelocity * (1 - dt)
    CALL vectorAddVectorScalar
(body.position
, body.velocity
, dt
)     body.orient = body.orient + (body.angularVelocity * dt)
    CALL matrixSetRadians
(body.shape.u
, body.orient
)     CALL impulseIntegrateForces
(body
, dt
)  
    DIM c
(cMAXNUMBEROFOBJECTS
) AS tVECTOR2d
     DIM manifolds
(sNUMBEROFBODIES 
* sNUMBEROFBODIES
) AS tMANIFOLD
     DIM collisions
(sNUMBEROFBODIES 
* sNUMBEROFBODIES
, cMAXNUMBEROFOBJECTS
) AS tVECTOR2d
     '    // Generate new collision info
    ' // erase hitlist
        hits(hitCount) = dHit
        hitCount = hitCount + 1
    hitCount = 0
 
    FOR i 
= 0 TO objectManager.objectCount 
' number of bodies         A = body(i)
            FOR j 
= i 
+ 1 TO objectManager.objectCount
                 B = body(j)
                IF (A.collisionMask 
AND B.collisionMask
) THEN                     'Mainfold solve - handle collisions
                    IF AABBOverlapVector
(A.position
, 2000, 2000, B.position
, 2000, 2000) THEN                         IF A.shape.ty 
= cSHAPE_CIRCLE 
AND B.shape.ty 
= cSHAPE_CIRCLE 
THEN                             CALL collisionCCHandle
(m
, c
(), A
, B
)                             IF A.shape.ty 
= cSHAPE_POLYGON 
AND B.shape.ty 
= cSHAPE_POLYGON 
THEN                                 CALL collisionPPHandle
(p
(), body
(), m
, c
(), i
, j
)                                 IF A.shape.ty 
= cSHAPE_CIRCLE 
AND B.shape.ty 
= cSHAPE_POLYGON 
THEN                                     CALL collisionCPHandle
(p
(), body
(), m
, c
(), i
, j
)                                     IF B.shape.ty 
= cSHAPE_CIRCLE 
AND A.shape.ty 
= cSHAPE_POLYGON 
THEN                                         CALL collisionPCHandle
(p
(), body
(), m
, c
(), i
, j
)  
                            m.A = i 'identify the index of objects
                            m.B = j
                            manifolds(manifoldCount) = m
                            FOR k 
= 0 TO m.contactCount
                                 hits(hitCount).A = i
                                hits(hitCount).B = j
                                hits(hitCount).position = c(k)
                                collisions(manifoldCount, k) = c(k)
                                hitCount = hitCount + 1
                            manifoldCount = manifoldCount + 1
 
    '   // Integrate forces
    FOR i 
= 0 TO objectManager.objectCount
         IF body
(i
).enable 
THEN CALL impulseIntegrateForces
(body
(i
), dt
)     '   // Initialize collision
    FOR i 
= 0 TO manifoldCount 
- 1         ' this is the stupidest thing ever since QB will not let you split arrays
        FOR k 
= 0 TO manifolds
(i
).contactCount 
- 1             c(k) = collisions(i, k)
        CALL manifoldInit
(manifolds
(i
), body
(), c
())     ' joint pre Steps
 
    FOR i 
= 1 TO sNUMBEROFJOINTS
         CALL jointPrestep
(j
(i
), body
(), dt
)  
    '// Solve collisions
    FOR j 
= 0 TO iterations 
- 1         FOR i 
= 0 TO manifoldCount 
- 1             FOR k 
= 0 TO manifolds
(i
).contactCount 
- 1                 c(k) = collisions(i, k)
            CALL manifoldApplyImpulse
(manifolds
(i
), body
(), c
())             'store the hit speed for later
            FOR k 
= 0 TO hitCount 
- 1                 IF manifolds
(i
).A 
= hits
(k
).A 
AND manifolds
(i
).B 
= hits
(k
).B 
THEN                     hits(k).cv = manifolds(i).cv
        FOR i 
= 1 TO sNUMBEROFJOINTS
             CALL jointApplyImpulse
(j
(i
), body
())  
    '// Integrate velocities
    FOR i 
= 0 TO objectManager.objectCount
         IF body
(i
).enable 
THEN CALL impulseIntegrateVelocity
(body
(i
), dt
)     '// Correct positions
    FOR i 
= 0 TO manifoldCount 
- 1         CALL manifoldPositionalCorrection
(manifolds
(i
), body
())     '// Clear all forces
    FOR i 
= 0 TO objectManager.objectCount
         CALL vectorSet
(body
(i
).force
, 0, 0)         body(i).torque = 0
 
SUB bodyApplyImpulse 
(body 
AS tBODY
, impulse 
AS tVECTOR2d
, contactVector 
AS tVECTOR2d
)     CALL vectorAddVectorScalar
(body.velocity
, impulse
, body.invMass
)     body.angularVelocity = body.angularVelocity + body.invInertia * vectorCross(contactVector, impulse)
 
SUB bodySetStatic 
(body 
AS tBODY
)     body.inertia = 0.0
    body.invInertia = 0.0
    body.mass = 0.0
    body.invMass = 0.0
 
SUB manifoldInit 
(m 
AS tMANIFOLD
, body
() AS tBODY
, contacts
() AS tVECTOR2d
)     DIM tv1 
AS tVECTOR2d 
'temporary Vectors     m.e = scalarMin(body(m.A).restitution, body(m.B).restitution)
    m.sf 
= SQR(body
(m.A
).staticFriction 
* body
(m.A
).staticFriction
)    m.df 
= SQR(body
(m.A
).dynamicFriction 
* body
(m.A
).dynamicFriction
)    FOR i 
= 0 TO m.contactCount 
- 1         CALL vectorSubVectorND
(contacts
(i
), body
(m.A
).position
, ra
)         CALL vectorSubVectorND
(contacts
(i
), body
(m.B
).position
, rb
)  
        CALL vectorCrossScalar
(tv1
, rb
, body
(m.B
).angularVelocity
)         CALL vectorCrossScalar
(tv2
, ra
, body
(m.A
).angularVelocity
)         CALL vectorAddVector
(tv1
, body
(m.B
).velocity
)         CALL vectorSubVectorND
(tv2
, body
(m.A
).velocity
, tv2
)         CALL vectorSubVectorND
(rv
, tv1
, tv2
)  
        IF vectorLengthSq
(rv
) < sRESTING 
THEN             m.e = 0.0
 
SUB manifoldApplyImpulse 
(m 
AS tMANIFOLD
, body
() AS tBODY
, contacts
() AS tVECTOR2d
)     DIM tv1 
AS tVECTOR2d 
'temporary Vectors  
 
    DIM tangentImpulse 
AS tVECTOR2d
  
    IF impulseEqual
(body
(m.A
).invMass 
+ body
(m.B
).invMass
, 0.0) THEN         CALL manifoldInfiniteMassCorrection
(body
(m.A
), body
(m.B
))  
    FOR i 
= 0 TO m.contactCount 
- 1         '// Calculate radii from COM to contact
        '// Vec2 ra = contacts[i] - A->position;
        '// Vec2 rb = contacts[i] - B->position;
        CALL vectorSubVectorND
(ra
, contacts
(i
), body
(m.A
).position
)         CALL vectorSubVectorND
(rb
, contacts
(i
), body
(m.B
).position
)  
        '// Relative velocity
        '// Vec2 rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
        CALL vectorCrossScalar
(tv1
, rb
, body
(m.B
).angularVelocity
)         CALL vectorCrossScalar
(tv2
, ra
, body
(m.A
).angularVelocity
)         CALL vectorAddVectorND
(rv
, tv1
, body
(m.B
).velocity
)         CALL vectorSubVector
(rv
, body
(m.A
).velocity
)         CALL vectorSubVector
(rv
, tv2
)  
        '// Relative velocity along the normal
        '// real contactVel = Dot( rv, normal );
        contactVel = vectorDot(rv, m.normal)
 
        '// Do not resolve if velocities are separating
        m.cv = contactVel
        '// real raCrossN = Cross( ra, normal );
        '// real rbCrossN = Cross( rb, normal );
        '// real invMassSum = A->im + B->im + Sqr( raCrossN ) * A->iI + Sqr( rbCrossN ) * B->iI;
        raCrossN = vectorCross(ra, m.normal)
        rbCrossN = vectorCross(rb, m.normal)
        invMassSum = body(m.A).invMass + body(m.B).invMass + (raCrossN * raCrossN) * body(m.A).invInertia + (rbCrossN * rbCrossN) * body(m.B).invInertia
 
        '// Calculate impulse scalar
        j = -(1.0 + m.e) * contactVel
        j = j / invMassSum
        j = j / m.contactCount
 
        '// Apply impulse
        CALL vectorMultiplyScalarND
(impulse
, m.normal
, j
)         CALL vectorNegND
(tv1
, impulse
)         CALL bodyApplyImpulse
(body
(m.A
), tv1
, ra
)         CALL bodyApplyImpulse
(body
(m.B
), impulse
, rb
)  
        '// Friction impulse
        '// rv = B->velocity + Cross( B->angularVelocity, rb ) - A->velocity - Cross( A->angularVelocity, ra );
        CALL vectorCrossScalar
(tv1
, rb
, body
(m.B
).angularVelocity
)         CALL vectorCrossScalar
(tv2
, ra
, body
(m.A
).angularVelocity
)         CALL vectorAddVectorND
(rv
, tv1
, body
(m.B
).velocity
)         CALL vectorSubVector
(rv
, body
(m.A
).velocity
)         CALL vectorSubVector
(rv
, tv2
)  
        '// Vec2 t = rv - (normal * Dot( rv, normal ));
        '// t.Normalize( );
        CALL vectorMultiplyScalarND
(t
, m.normal
, vectorDot
(rv
, m.normal
))         CALL vectorSubVectorND
(t
, rv
, t
)  
        '// j tangent magnitude
        jt = -vectorDot(rv, t)
        jt = jt / invMassSum
        jt = jt / m.contactCount
 
        '// Don't apply tiny friction impulses
 
        '// Coulumb's law
            CALL vectorMultiplyScalarND
(tangentImpulse
, t
, jt
)             CALL vectorMultiplyScalarND
(tangentImpulse
, t
, -j 
* m.df
)  
        '// Apply friction impulse
        '// A->ApplyImpulse( -tangentImpulse, ra );
        '// B->ApplyImpulse( tangentImpulse, rb );
        CALL vectorNegND
(tv1
, tangentImpulse
)         CALL bodyApplyImpulse
(body
(m.A
), tv1
, ra
)         CALL bodyApplyImpulse
(body
(m.B
), tangentImpulse
, rb
)  
SUB manifoldPositionalCorrection 
(m 
AS tMANIFOLD
, body
() AS tBODY
)     correction = scalarMax(m.penetration - cPENETRATION_ALLOWANCE, 0.0) / (body(m.A).invMass + body(m.B).invMass) * cPENETRATION_CORRECTION
    CALL vectorAddVectorScalar
(body
(m.A
).position
, m.normal
, -body
(m.A
).invMass 
* correction
)     CALL vectorAddVectorScalar
(body
(m.B
).position
, m.normal
, body
(m.B
).invMass 
* correction
)  
SUB manifoldInfiniteMassCorrection 
(A 
AS tBODY
, B 
AS tBODY
)     CALL vectorSet
(A.velocity
, 0, 0)     CALL vectorSet
(B.velocity
, 0, 0)  
'**********************************************************************************************
'   Collision Stuff Ahead
'**********************************************************************************************
 
SUB collisionCCHandle 
(m 
AS tMANIFOLD
, contacts
() AS tVECTOR2d
, A 
AS tBODY
, B 
AS tBODY
)  
    CALL vectorSubVectorND
(normal
, B.position
, A.position
) ' Subtract two vectors position A and position B     dist_sqr = vectorLengthSq(normal) ' Calculate the distance between the balls or circles
    radius = A.shape.radius + B.shape.radius ' Add both circle A and circle B radius
 
    IF (dist_sqr 
>= radius 
* radius
) THEN         m.contactCount = 0
        m.contactCount = 1
 
            m.penetration = A.shape.radius
            CALL vectorSet
(m.normal
, 1.0, 0.0)             CALL vectorSetVector
(contacts
(0), A.position
)             m.penetration = radius - distance
            CALL vectorDivideScalarND
(m.normal
, normal
, distance
)  
            CALL vectorMultiplyScalarND
(contacts
(0), m.normal
, A.shape.radius
)             CALL vectorAddVector
(contacts
(0), A.position
)  
    CALL collisionCPHandle
(p
(), body
(), m
, contacts
(), B
, A
)  
    'A is the Circle
    'B is the POLY
    m.contactCount = 0
 
    CALL vectorSubVectorND
(center
, body
(A
).position
, body
(B
).position
)     CALL matrixTranspose
(body
(B
).shape.u
, tm
)     CALL matrixMultiplyVector
(tm
, center
, center
)  
    FOR i 
= 0 TO body
(B
).pa.count
         CALL vectorSubVectorND
(tv
, center
, p
(body
(B
).pa.start 
+ i
).vert
)         s = vectorDot(p(body(B).pa.start + i).norm, tv)
            separation = s
            faceNormal = i
    v1 = p(body(B).pa.start + faceNormal).vert
    i2 = body(B).pa.start + arrayNextIndex(faceNormal, body(B).pa.count)
    v2 = p(i2).vert
 
        m.contactCount = 1
        CALL matrixMultiplyVector
(body
(B
).shape.u
, p
(body
(B
).pa.start 
+ faceNormal
).norm
, m.normal
)         CALL vectorMultiplyScalarND
(contacts
(0), m.normal
, ARadius
)         CALL vectorAddVector
(contacts
(0), body
(A
).position
)         m.penetration = ARadius
 
 
    CALL vectorSubVectorND
(tv1
, center
, v1
)     CALL vectorSubVectorND
(tv2
, v2
, v1
)     dot1 = vectorDot(tv1, tv2)
    CALL vectorSubVectorND
(tv1
, center
, v2
)     CALL vectorSubVectorND
(tv2
, v1
, v2
)     dot2 = vectorDot(tv1, tv2)
    m.penetration = ARadius - separation
        m.contactCount = 1
        CALL vectorSubVectorND
(n
, v1
, center
)         CALL matrixMultiplyVector
(body
(B
).shape.u
, n
, n
)         m.normal = n
        CALL matrixMultiplyVector
(body
(B
).shape.u
, v1
, v1
)         CALL vectorAddVectorND
(v1
, v1
, body
(B
).position
)         contacts(0) = v1
            m.contactCount = 1
            CALL vectorSubVectorND
(n
, v2
, center
)             CALL matrixMultiplyVector
(body
(B
).shape.u
, v2
, v2
)             CALL vectorAddVectorND
(v2
, v2
, body
(B
).position
)             contacts(0) = v2
            CALL matrixMultiplyVector
(body
(B
).shape.u
, n
, n
)             m.normal = n
            n = p(body(B).pa.start + faceNormal).norm
            CALL vectorSubVectorND
(tv1
, center
, v1
)             m.contactCount = 1
            CALL matrixMultiplyVector
(body
(B
).shape.u
, n
, n
)             m.normal = n
            CALL vectorMultiplyScalarND
(contacts
(0), m.normal
, ARadius
)             CALL vectorAddVector
(contacts
(0), body
(A
).position
)  
    DIM o
(cMAXNUMBEROFPOLYGONS
) AS tVECTOR2d
  
    o(0) = face(0)
    o(1) = face(1)
 
 
        o(sp) = face(0)
        sp = sp + 1
 
        o(sp) = face(1)
        sp = sp + 1
 
        'out[sp] = face[0] + alpha * (face[1] - face[0]);
        CALL vectorSubVectorND
(tempv
, face
(1), face
(0))         CALL vectorMultiplyScalar
(tempv
, alpha
)         CALL vectorAddVectorND
(o
(sp
), tempv
, face
(0))         sp = sp + 1
    face(0) = o(0)
    face(1) = o(1)
    collisionPPClip = sp
 
    DIM referenceNormal 
AS tVECTOR2d
     DIM uRef 
AS tMATRIX2d: uRef 
= b
(RefPoly
).shape.u
     DIM uInc 
AS tMATRIX2d: uInc 
= b
(IncPoly
).shape.u
     referenceNormal = p(b(RefPoly).pa.start + referenceIndex).norm
 
    '        // Calculate normal in incident's frame of reference
    '        // referenceNormal = RefPoly->u * referenceNormal; // To world space
    CALL matrixMultiplyVector
(uRef
, referenceNormal
, referenceNormal
)     '        // referenceNormal = IncPoly->u.Transpose( ) * referenceNormal; // To incident's model space
    CALL matrixTranspose
(uInc
, uTemp
)     CALL matrixMultiplyVector
(uTemp
, referenceNormal
, referenceNormal
)  
    FOR i 
= 0 TO b
(IncPoly
).pa.count
         dot = vectorDot(referenceNormal, p(b(IncPoly).pa.start + i).norm)
            minDot = dot
            incidentFace = i
 
    '// Assign face vertices for incidentFace
    '// v[0] = IncPoly->u * IncPoly->m_vertices[incidentFace] + IncPoly->body->position;
    CALL matrixMultiplyVector
(uInc
, p
(b
(IncPoly
).pa.start 
+ incidentFace
).vert
, v
(0))     CALL vectorAddVector
(v
(0), b
(IncPoly
).position
)  
    '// incidentFace = incidentFace + 1 >= (int32)IncPoly->m_vertexCount ? 0 : incidentFace + 1;
    incidentFace = arrayNextIndex(incidentFace, b(IncPoly).pa.count)
 
    '// v[1] = IncPoly->u * IncPoly->m_vertices[incidentFace] +  IncPoly->body->position;
    CALL matrixMultiplyVector
(uInc
, p
(b
(IncPoly
).pa.start 
+ incidentFace
).vert
, v
(1))     CALL vectorAddVector
(v
(1), b
(IncPoly
).position
)  
    m.contactCount = 0
 
 
    penetrationA = collisionPPFindAxisLeastPenetration(p(), body(), faceA(), A, B)
 
 
    penetrationB = collisionPPFindAxisLeastPenetration(p(), body(), faceB(), B, A)
 
 
 
 
    IF impulseGT
(penetrationA
, penetrationB
) THEN         RefPoly = A
        IncPoly = B
        referenceIndex = faceA(0)
        flip = 0
        RefPoly = B
        IncPoly = A
        referenceIndex = faceB(0)
        flip = 1
 
    DIM incidentFace
(2) AS tVECTOR2d
  
    CALL collisionPPFindIncidentFace
(p
(), body
(), incidentFace
(), RefPoly
, IncPoly
, referenceIndex
)  
    v1 = p(body(RefPoly).pa.start + referenceIndex).vert
    referenceIndex = arrayNextIndex(referenceIndex, body(RefPoly).pa.count)
    v2 = p(body(RefPoly).pa.start + referenceIndex).vert
    '// Transform vertices to world space
    '// v1 = RefPoly->u * v1 + RefPoly->body->position;
    '// v2 = RefPoly->u * v2 + RefPoly->body->position;
    CALL matrixMultiplyVector
(body
(RefPoly
).shape.u
, v1
, v1t
)     CALL vectorAddVectorND
(v1
, v1t
, body
(RefPoly
).position
)     CALL matrixMultiplyVector
(body
(RefPoly
).shape.u
, v2
, v2t
)     CALL vectorAddVectorND
(v2
, v2t
, body
(RefPoly
).position
)  
    '// Calculate reference face side normal in world space
    '// Vec2 sidePlaneNormal = (v2 - v1);
    '// sidePlaneNormal.Normalize( );
    DIM sidePlaneNormal 
AS tVECTOR2d
     CALL vectorSubVectorND
(sidePlaneNormal
, v2
, v1
)     CALL vectorNormalize
(sidePlaneNormal
)  
    '// Orthogonalize
    '// Vec2 refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );
    DIM refFaceNormal 
AS tVECTOR2d
     CALL vectorSet
(refFaceNormal
, sidePlaneNormal.y
, -sidePlaneNormal.x
)  
    '// ax + by = c
    '// c is distance from origin
    '// real refC = Dot( refFaceNormal, v1 );
    '// real negSide = -Dot( sidePlaneNormal, v1 );
    '// real posSide = Dot( sidePlaneNormal, v2 );
    DIM refC 
AS _FLOAT: refC 
= vectorDot
(refFaceNormal
, v1
)     DIM negSide 
AS _FLOAT: negSide 
= -vectorDot
(sidePlaneNormal
, v1
)     DIM posSide 
AS _FLOAT: posSide 
= vectorDot
(sidePlaneNormal
, v2
)  
 
    '// Clip incident face to reference face side planes
    '// if(Clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
    DIM negSidePlaneNormal 
AS tVECTOR2d
     CALL vectorNegND
(negSidePlaneNormal
, sidePlaneNormal
)  
    IF collisionPPClip
(negSidePlaneNormal
, negSide
, incidentFace
()) < 2 THEN EXIT SUB     IF collisionPPClip
(sidePlaneNormal
, posSide
, incidentFace
()) < 2 THEN EXIT SUB  
    CALL vectorSet
(m.normal
, refFaceNormal.x
, refFaceNormal.y
)  
    '// Keep points behind reference face
    DIM cp 
AS INTEGER: cp 
= 0 '// clipped points behind reference face     separation = vectorDot(refFaceNormal, incidentFace(0)) - refC
        contacts(cp) = incidentFace(0)
        m.penetration = -separation
        cp = cp + 1
        m.penetration = 0
 
    separation = vectorDot(refFaceNormal, incidentFace(1)) - refC
        contacts(cp) = incidentFace(1)
        m.penetration = m.penetration + -separation
        cp = cp + 1
        m.penetration = m.penetration / cp
    m.contactCount = cp
 
 
 
    FOR i 
= 0 TO body
(A
).pa.count
         k = body(A).pa.start + i
 
        '// Retrieve a face normal from A
        '// Vec2 n = A->m_normals[i];
        '// Vec2 nw = A->u * n;
        n = p(k).norm
        CALL matrixMultiplyVector
(body
(A
).shape.u
, n
, nw
)  
 
        '// Transform face normal into B's model space
        '// Mat2 buT = B->u.Transpose( );
        '// n = buT * nw;
        CALL matrixTranspose
(body
(B
).shape.u
, buT
)         CALL matrixMultiplyVector
(buT
, nw
, n
)  
        '// Retrieve support point from B along -n
        '// Vec2 s = B->GetSupport( -n );
        CALL vectorGetSupport
(p
(), body
(), B
, nn
, s
)  
        '// Retrieve vertex on face from A, transform into
        '// B's model space
        '// Vec2 v = A->m_vertices[i];
        '// v = A->u * v + A->body->position;
        '// v -= B->body->position;
        '// v = buT * v;
 
        v = p(k).vert
        CALL matrixMultiplyVector
(body
(A
).shape.u
, v
, tv
)         CALL vectorAddVectorND
(v
, tv
, body
(A
).position
)  
        CALL vectorSubVector
(v
, body
(B
).position
)         CALL matrixMultiplyVector
(buT
, v
, tv
)  
        CALL vectorSubVector
(s
, tv
)         d = vectorDot(n, s)
 
            bestDistance = d
            bestIndex = i
 
 
    faceIndex(0) = bestIndex
 
    collisionPPFindAxisLeastPenetration = bestDistance
 
'**********************************************************************************************
'   Shape Creation Ahead
'**********************************************************************************************
 
    CALL matrixSetScalar
(u
, 1, 0, 0, 1)     sh.ty = ty
    sh.radius = radius
    sh.u = u
    sh.scaleTextureX = 1.0
    sh.scaleTextureY = 1.0
 
'**********************************************************************************************
'   Scene Creation Tools ahead
'**********************************************************************************************
 
        CASE cPARAMETER_POSITION:
             CALL vectorSet
(body
(Index
).position
, arg1
, arg2
)         CASE cPARAMETER_VELOCITY:
             CALL vectorSet
(body
(Index
).velocity
, arg1
, arg2
)             CALL vectorSet
(body
(Index
).force
, arg1
, arg2
)         CASE cPARAMETER_ANGULARVELOCITY:
             body(Index).angularVelocity = arg1
            body(Index).torque = arg1
            body(Index).orient = arg1
            CALL matrixSetRadians
(body
(Index
).shape.u
, body
(Index
).orient
)         CASE cPARAMETER_STATICFRICTION:
             body(Index).staticFriction = arg1
        CASE cPARAMETER_DYNAMICFRICTION:
             body(Index).dynamicFriction = arg1
        CASE cPARAMETER_RESTITUTION:
             body(Index).restitution = arg1
            body(Index).c = arg1
            body(Index).enable = arg1
            CALL bodySetStatic
(body
(Index
))             body(Index).shape.texture = arg1
        CASE cPARAMETER_FLIPTEXTURE: 
'does the texture flip directions when moving left or right             body(Index).shape.flipTexture = arg1
        CASE cPARAMETER_COLLISIONMASK:
             body(Index).collisionMask = arg1
 
    index = objectManagerID(body(), objName)
 
        CALL setBody
(body
(), Parameter
, index
, arg1
, arg2
)  
 
    CALL shapeCreate
(shape
, cSHAPE_CIRCLE
, radius
)     CALL bodyCreate
(body
(), index
, shape
)     'no vertices have to created for circles
    CALL circleInitialize
(body
(), index
)     ' Even though circles do not have vertices, they still must be included in the vertices list
        body(index).pa.start = 0
        body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
    body(index).pa.count = 1
    body
(index
).c 
= _RGB32(255, 255, 255)    createCircleBody = index
 
    CALL shapeCreate
(shape
, cSHAPE_CIRCLE
, radius
)     CALL bodyCreateEx
(body
(), objName
, shape
, index
)     'no vertices have to created for circles
    CALL circleInitialize
(body
(), index
)     ' Even though circles do not have vertices, they still must be included in the vertices list
        body(index).pa.start = 0
        body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
    body(index).pa.count = 1
    body
(index
).c 
= _RGB32(255, 255, 255)    createCircleBodyEx = index
 
 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)     CALL bodyCreate
(body
(), index
, shape
)     CALL boxCreate
(p
(), body
(), index
, xs
, ys
)     CALL polygonInitialize
(body
(), p
(), index
)     body
(index
).c 
= _RGB32(255, 255, 255)    createBoxBodies = index
 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)     CALL bodyCreateEx
(body
(), objName
, shape
, index
)     CALL boxCreate
(p
(), body
(), index
, xs
, ys
)     CALL polygonInitialize
(body
(), p
(), index
)     body
(index
).c 
= _RGB32(255, 255, 255)    createBoxBodiesEx = index
 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)     CALL bodyCreate
(body
(), index
, shape
)     CALL trapCreate
(p
(), body
(), index
, xs
, ys
, yoff1
, yoff2
)     CALL polygonInitialize
(body
(), p
(), index
)     body
(index
).c 
= _RGB32(255, 255, 255) 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)     CALL bodyCreateEx
(body
(), objName
, shape
, index
)     CALL trapCreate
(p
(), body
(), index
, xs
, ys
, yoff1
, yoff2
)     CALL polygonInitialize
(body
(), p
(), index
)     body
(index
).c 
= _RGB32(255, 255, 255) 
 
    objectManagerAdd = objectManager.objectCount
    objectManager.objectCount = objectManager.objectCount + 1
    IF objectManager.objectCount 
> sNUMBEROFBODIES 
THEN         sNUMBEROFBODIES = sNUMBEROFBODIES * 1.5
 
    objectManagerID = -1
 
    FOR i 
= 0 TO objectManager.objectCount
         IF body
(i
).objectHash 
= uID 
THEN             objectManagerID = i
 
    index = objectManagerAdd(body())
    body(index).objectName = objName
    body(index).objectHash = computeHash(objName)
    CALL bodyCreate
(body
(), index
, shape
)  
 
    CALL vectorSet
(body
(index
).position
, 0, 0)     CALL vectorSet
(body
(index
).velocity
, 0, 0)     body(index).angularVelocity = 0.0
    body(index).torque = 0.0
    body(index).orient = 0.0
 
    CALL vectorSet
(body
(index
).force
, 0, 0)     body(index).staticFriction = 0.5
    body(index).dynamicFriction = 0.3
    body(index).restitution = 0.2
    body(index).shape = shape
    body(index).collisionMask = 255
    body(index).enable = 1
 
    DIM verts
(vertlength
) AS tVECTOR2d
  
    CALL vectorSet
(verts
(0), -sizex
, -sizey
)     CALL vectorSet
(verts
(1), sizex
, -sizey
)     CALL vectorSet
(verts
(2), sizex
, sizey
)     CALL vectorSet
(verts
(3), -sizex
, sizey
)  
    CALL vertexSet
(p
(), body
(), index
, verts
(), vertlength
)  
    DIM verts
(vertlength
) AS tVECTOR2d
  
    CALL vectorSet
(verts
(0), -sizex
, -sizey 
- yOff2
)     CALL vectorSet
(verts
(1), sizex
, -sizey 
- yOff1
)     CALL vectorSet
(verts
(2), sizex
, sizey
)     CALL vectorSet
(verts
(3), -sizex
, sizey
)  
    CALL vertexSet
(p
(), body
(), index
, verts
(), vertlength
)  
 
 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)  
        CALL bodyCreate
(body
(), index 
+ i
, shape
)         CALL terrainCreate
(p
(), body
(), index 
+ i
, elevation
(i
), elevation
(i 
+ 1), sliceWidth
, nominalHeight
)         CALL polygonInitialize
(body
(), p
(), index 
+ i
)         body
(index 
+ i
).c 
= _RGB32(255, 255, 255)        CALL bodySetStatic
(body
(index 
+ i
))  
 
 
 
    CALL shapeCreate
(shape
, cSHAPE_POLYGON
, 0)  
        CALL bodyCreateEx
(body
(), objName 
+ "_" + LTRIM$(STR$(i
)), shape
, index
)         CALL terrainCreate
(p
(), body
(), index
, elevation
(i
), elevation
(i 
+ 1), sliceWidth
, nominalHeight
)         CALL polygonInitialize
(body
(), p
(), index
)         body
(index
).c 
= _RGB32(255, 255, 255)        CALL bodySetStatic
(body
(index
))  
 
        start 
= objectManagerID
(body
(), objName 
+ "_" + LTRIM$(STR$(i
)))        p1 = (sliceWidth / 2) - p(body(start).pa.start).vert.x
        p2 = nominalHeight - p(body(start).pa.start + 1).vert.y
        CALL setBody
(body
(), cPARAMETER_POSITION
, start
, world.terrainPosition.x 
+ p1 
+ (sliceWidth 
* i
), world.terrainPosition.y 
+ p2
)  
 
    vertLength = 3 ' numOfslices + 1
    DIM verts
(vertLength
) AS tVECTOR2d
  
    CALL vectorSet
(verts
(0), 0, nominalHeight
)     CALL vectorSet
(verts
(1), (0) * sliceWidth
, -nominalHeight 
- ele1
)     CALL vectorSet
(verts
(2), (1) * sliceWidth
, -nominalHeight 
- ele2
)     CALL vectorSet
(verts
(3), (1) * sliceWidth
, nominalHeight
)     CALL vertexSet
(p
(), body
(), index
, verts
(), vertLength
)  
 
 
    DIM verts
(vertlength
) AS tVECTOR2d
  
    CALL vectorSet
(verts
(0), -sizex
, -sizey
)     CALL vectorSet
(verts
(1), sizex
, -sizey
)     CALL vectorSet
(verts
(2), sizex
, sizey
)     CALL vectorSet
(verts
(3), -sizex
, sizey
)     CALL vectorSet
(verts
(4), -sizex
, sizey 
/ 2)     CALL vectorSet
(verts
(5), sizex 
/ 2, sizey 
/ 2)     CALL vectorSet
(verts
(6), sizex 
/ 2, -sizey 
/ 2)     CALL vectorSet
(verts
(7), -sizex
, sizey 
/ 2)  
    CALL vertexSet
(p
(), body
(), index
, verts
(), vertlength
)  
    DIM highestXCoord 
AS _FLOAT: highestXCoord 
= verts
(0).x
         x = verts(i).x
            highestXCoord = x
            rightMost = i
                IF verts
(i
).y 
< verts
(rightMost
).y 
THEN                     rightMost = i
        hull(outCount) = indexHull
        nextHullIndex = 0
            IF nextHullIndex 
= indexHull 
THEN                 nextHullIndex = i
            CALL vectorSubVectorND
(e1
, verts
(nextHullIndex
), verts
(hull
(outCount
)))             CALL vectorSubVectorND
(e2
, verts
(i
), verts
(hull
(outCount
)))             c = vectorCross(e1, e2)
            IF c 
< 0.0 THEN nextHullIndex 
= i
             IF c 
= 0.0 AND (vectorLengthSq
(e2
) > vectorLengthSq
(e1
)) THEN                 nextHullIndex = i
        outCount = outCount + 1
        indexHull = nextHullIndex
        IF nextHullIndex 
= rightMost 
THEN             body(index).pa.count = outCount - 1
 
        body(index).pa.start = 0
        body(index).pa.start = body(index - 1).pa.start + body(index - 1).pa.count + 1
 
        p(body(index).pa.start + i).vert = verts(hull(i))
 
        CALL vectorSubVectorND
(face
, p
(body
(index
).pa.start 
+ arrayNextIndex
(i
, body
(index
).pa.count
)).vert
, p
(body
(index
).pa.start 
+ i
).vert
)         CALL vectorSet
(p
(body
(index
).pa.start 
+ i
).norm
, face.y
, -face.x
)         CALL vectorNormalize
(p
(body
(index
).pa.start 
+ i
).norm
)  
    arrayNextIndex 
= ((i 
+ 1) MOD (count 
+ 1)) 
'**********************************************************************************************
'   Rendering Stuff Ahead
'**********************************************************************************************
 
    fpsLast = fps
    fps = 0
 
    AABBOverlap 
= Ax 
< Bx 
+ Bw 
AND Ax 
+ Aw 
> Bx 
AND Ay 
< By 
+ Bh 
AND Ay 
+ Ah 
> By
 
    AABBOverlapVector = AABBOverlap(A.x, A.y, Aw, Ah, B.x, B.y, Bw, Bh)
 
 
SUB renderBodies 
(p
() AS tPOLY
, body
() AS tBODY
, j
() AS tJOINT
, hits
() AS tHIT
, camera 
AS tCAMERA
)     DIM camoffset 
AS tVECTOR2d
     DIM AS _FLOAT cx
, cy
, cwx
, cwy
, ox
, oy
, owx
, owy
, ccw
, cch
     FOR i 
= 0 TO objectManager.objectCount
             'AABB to cut down on rendering objects out of camera view
            cx = camera.position.x - ccw
            cy = camera.position.y - cch
            ox = body(i).position.x - 2000
            oy = body(i).position.y - 2000
            owx = 4000
            owy = 4000
            IF AABBOverlap
(cx
, cy
, cwx
, cwy
, ox
, oy
, owx
, owy
) THEN                 IF body
(i
).shape.ty 
= cSHAPE_CIRCLE 
THEN                     IF body
(i
).shape.texture 
= 0 THEN                         CALL renderWireframeCircle
(body
(), i
, camera
)                         CALL renderTexturedCircle
(body
(), i
, camera
)                         IF body
(i
).shape.texture 
= 0 THEN                             CALL renderWireframePoly
(p
(), body
(), i
, camera
)                             CALL renderTexturedBox
(p
(), body
(), i
, camera
)         FOR i 
= 1 TO sNUMBEROFJOINTS
             CALL renderJoints
(j
(i
), body
(), camera
)         hitcount = 0
        DO WHILE hits
(hitcount
).A 
<> hits
(hitcount
).B
             CALL vectorSubVectorND
(camoffset
, hits
(hitcount
).position
, camera.position
)             CIRCLE (camoffset.x
, camoffset.y
), 5, _RGB(255, 6, 11)             hitcount = hitcount + 1
 
 
SUB renderJoints 
(j 
AS tJOINT
, b
() AS tBODY
, camera 
AS tCAMERA
)     DIM b1 
AS tBODY: b1 
= b
(j.body1
)     DIM b2 
AS tBODY: b2 
= b
(j.body2
)     DIM R1 
AS tMATRIX2d: R1 
= b1.shape.u
     DIM R2 
AS tMATRIX2d: R2 
= b2.shape.u
  
    DIM x1 
AS tVECTOR2d: x1 
= b1.position
     DIM p1 
AS tVECTOR2d: 
CALL matrixMultiplyVector
(R1
, j.localAnchor1
, p1
)  
    CALL vectorAddVectorND
(p1
, p1
, x1
)  
    DIM x2 
AS tVECTOR2d: x2 
= b2.position
     DIM p2 
AS tVECTOR2d: 
CALL matrixMultiplyVector
(R2
, j.localAnchor2
, p2
)  
    CALL vectorAddVectorND
(p2
, p2
, x2
)  
    CALL vectorSubVector
(p1
, camera.position
)     CALL vectorSubVector
(x2
, camera.position
)  
    LINE (p1.x
, p1.y
)-(x2.x
, x2.y
), _RGB(255, 255, 127) 'yellow  
    DIM a 
AS tVECTOR2d 
' dummy vertices  
    FOR i 
= 0 TO b
(index
).pa.count
         element = b(index).pa.start + i
        element_next = b(index).pa.start + arrayNextIndex(i, b(index).pa.count)
        a = p(element).vert
        b = p(element_next).vert
        CALL worldToCamera
(b
(), camera
, index
, a
)         CALL worldToCamera
(b
(), camera
, index
, b
)         LINE (a.x
, a.y
)-(b.x
, b.y
), b
(index
).c
  
 
    bm = b(index).shape.texture
 
        vert(i) = p(b(index).pa.start + i).vert
        vert(i).x = vert(i).x + b(index).shape.offsetTextureX
        vert(i).y = vert(i).y + b(index).shape.offsetTextureY
        vert(i).x = vert(i).x * b(index).shape.scaleTextureX
        vert(i).y = vert(i).y * b(index).shape.scaleTextureY
        CALL worldToCamera
(b
(), camera
, index
, vert
(i
))     IF b
(index
).velocity.x 
> 1 OR b
(index
).shape.flipTexture 
= 0 THEN         _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
)         _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
)         _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
)         _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
)  
 
    DIM tv 
AS tVECTOR2d: tv 
= b
(index
).position
     tv.x = tv.x * camera.zoom
    tv.y = tv.y * camera.zoom
    CALL worldToCameraNR
(b
(), camera
, index
, tv
)     CIRCLE (tv.x
, tv.y
), b
(index
).shape.radius 
* camera.zoom
, b
(index
).c
     LINE (tv.x
, tv.y
)-(tv.x 
+ COS(b
(index
).orient
) * (b
(index
).shape.radius 
* camera.zoom
), tv.y 
+ SIN(b
(index
).orient
) * (b
(index
).shape.radius
) * camera.zoom
), b
(index
).c
  
    'DIM tv AS tVECTOR2d
    ' CALL vectorSet(tv, _WIDTH / 2 * (1 / camera.zoom), _HEIGHT / 2 * (1 / camera.zoom))
 
    bm = b(index).shape.texture
    CALL vectorSet
(vert
(0), -b
(index
).shape.radius
, -b
(index
).shape.radius
)     CALL vectorSet
(vert
(1), -b
(index
).shape.radius
, b
(index
).shape.radius
)     CALL vectorSet
(vert
(2), b
(index
).shape.radius
, b
(index
).shape.radius
)     CALL vectorSet
(vert
(3), b
(index
).shape.radius
, -b
(index
).shape.radius
)         CALL worldToCamera
(b
(), camera
, index
, vert
(i
))     _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
)     _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
)  
 
SUB worldToCamera 
(body
() AS tBODY
, camera 
AS tCAMERA
, index 
AS INTEGER, vert 
AS tVECTOR2d
)     CALL vectorSet
(tv
, _WIDTH / 2 * (1 / camera.zoom
), _HEIGHT / 2 * (1 / camera.zoom
)) ' Camera Center     CALL matrixMultiplyVector
(body
(index
).shape.u
, vert
, vert
) ' Rotate body     CALL vectorAddVector
(vert
, body
(index
).position
) ' Add Position     CALL vectorSubVector
(vert
, camera.position
) 'Sub Camera Position     CALL vectorAddVector
(vert
, tv
) ' Add to camera Center     CALL vectorMultiplyScalar
(vert
, camera.zoom
) 'Zoom everything  
SUB worldToCameraNR 
(body
() AS tBODY
, camera 
AS tCAMERA
, index 
AS INTEGER, vert 
AS tVECTOR2d
)     CALL vectorSet
(tv
, _WIDTH / 2 * (1 / camera.zoom
), _HEIGHT / 2 * (1 / camera.zoom
)) ' Camera Center     CALL vectorSetVector
(vert
, body
(index
).position
) ' Add Position     CALL vectorSubVector
(vert
, camera.position
) 'Sub Camera Position     CALL vectorAddVector
(vert
, tv
) ' Add to camera Center     CALL vectorMultiplyScalar
(vert
, camera.zoom
) 'Zoom everything  
 
    CALL matrixSetRadians
(b.shape.u
, radians
)  
'**********************************************************************************************
'   Object initialization Ahead
'**********************************************************************************************
 
    CALL circleComputeMass
(b
(), index
, .10)  
    b(index).mass = cPI * b(index).shape.radius * b(index).shape.radius * density
        b(index).invMass = 1.0 / b(index).mass
        b(index).invMass = 0.0
 
    b(index).inertia = b(index).mass * b(index).shape.radius * b(index).shape.radius
 
        b(index).invInertia = 1.0 / b(index).inertia
        b(index).invInertia = 0.0
 
    CALL polygonComputeMass
(body
(), p
(), index
, .10)  
    DIM c 
AS tVECTOR2d 
' centroid  
    k_inv3 = 1.0 / 3.0
 
    FOR ii 
= 0 TO b
(index
).pa.count
         p1 = p(b(index).pa.start + ii).vert
        p2 = p(b(index).pa.start + arrayNextIndex(ii, b(index).pa.count)).vert
        D = vectorCross(p1, p2)
        triangleArea = .5 * D
        area = area + triangleArea
        weight = triangleArea * k_inv3
        CALL vectorAddVectorScalar
(c
, p1
, weight
)         CALL vectorAddVectorScalar
(c
, p2
, weight
)         intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x
        inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y
        I = I + (0.25 * k_inv3 * D) * (intx2 + inty2)
 
    CALL vectorMultiplyScalar
(c
, 1.0 / area
)  
    FOR ii 
= 0 TO b
(index
).pa.count
         CALL vectorSubVector
(p
(b
(index
).pa.start 
+ ii
).vert
, c
)  
    b(index).mass = density * area
        b(index).invMass = 1.0 / b(index).mass
        b(index).invMass = 0.0
 
    b(index).inertia = I * density
        b(index).invInertia = 1.0 / b(index).inertia
        b(index).invInertia = 0.0
'**********************************************************************************************
'   Joint Stuff Ahead
'**********************************************************************************************
    CALL vectorSet
(anchor
, x
, y
)     DIM Rot1 
AS tMATRIX2d: Rot1 
= body
(b1
).shape.u
     DIM Rot2 
AS tMATRIX2d: Rot2 
= body
(b2
).shape.u
     DIM Rot1T 
AS tMATRIX2d: 
CALL matrixTranspose
(Rot1
, Rot1T
)     DIM Rot2T 
AS tMATRIX2d: 
CALL matrixTranspose
(Rot2
, Rot2T
)  
    j.body1 = b1
    j.body2 = b2
 
    CALL vectorSubVectorND
(tv
, anchor
, body
(b1
).position
)     CALL matrixMultiplyVector
(Rot1T
, tv
, j.localAnchor1
)  
    CALL vectorSubVectorND
(tv
, anchor
, body
(b2
).position
)     CALL matrixMultiplyVector
(Rot2T
, tv
, j.localAnchor2
)  
    CALL vectorSet
(j.P
, 0, 0)  
    j.softness = 0.001
    j.biasFactor = 100
 
 
    DIM Rot1 
AS tMATRIX2d: Rot1 
= body
(j.body1
).shape.u
     DIM Rot2 
AS tMATRIX2d: Rot2 
= body
(j.body2
).shape.u
  
 
    CALL matrixMultiplyVector
(Rot1
, j.localAnchor1
, j.r1
)     CALL matrixMultiplyVector
(Rot2
, j.localAnchor2
, j.r2
)  
    b1invMass = body(j.body1).invMass
    b2invMass = body(j.body2).invMass
 
    b1invInertia = body(j.body1).invInertia
    b2invInertia = body(j.body2).invInertia
 
    Call matrixSetScalar
(K1
, b1invMass 
+ b2invMass
, 0,_
                                                  0, b1invMass + b2invMass)
    Call matrixSetScalar
(K2
, b1invInertia 
* j.r1.y 
* j.r1.y
, -b1invInertia 
* j.r1.x 
* j.r1.y
,_
                             -b1invInertia * j.r1.x * j.r1.y,  b1invInertia * j.r1.x * j.r1.x)
 
    Call matrixSetScalar
(K3
,  b2invInertia 
* j.r2.y 
* j.r2.y
, - b2invInertia 
* j.r2.x 
* j.r2.y
,_
                              -b2invInertia * j.r2.x * j.r2.y,   b2invInertia * j.r2.x * j.r2.x)
 
    CALL matrixAddMatrix
(K1
, K2
, K
)     CALL matrixAddMatrix
(K3
, K
, K
)     K.m00 = K.m00 + j.softness
    K.m11 = K.m11 + j.softness
    CALL matrixInvert
(K
, j.M
)  
    DIM p1 
AS tVECTOR2d: 
CALL vectorAddVectorND
(p1
, body
(j.body1
).position
, j.r1
)     DIM p2 
AS tVECTOR2d: 
CALL vectorAddVectorND
(p2
, body
(j.body2
).position
, j.r2
)     DIM dp 
AS tVECTOR2d: 
CALL vectorSubVectorND
(dp
, p2
, p1
)  
    CALL vectorMultiplyScalarND
(j.bias
, dp
, -j.biasFactor 
* inv_dt
)     'Call vectorSet(j.bias, 0, 0)
    CALL vectorSet
(j.P
, 0, 0)  
SUB jointApplyImpulse 
(j 
AS tJOINT
, body
() AS tBODY
)  
 
    'Vec2 dv = body2->velocity + Cross(body2->angularVelocity, r2) - body1->velocity - Cross(body1->angularVelocity, r1);
    CALL vectorCrossScalar
(cross2
, j.r2
, body
(j.body2
).angularVelocity
)     CALL vectorCrossScalar
(cross1
, j.r1
, body
(j.body1
).angularVelocity
)     CALL vectorAddVectorND
(dv
, body
(j.body2
).velocity
, cross2
)     CALL vectorSubVectorND
(dv
, dv
, body
(j.body1
).velocity
)     CALL vectorSubVectorND
(dv
, dv
, cross1
)  
    ' impulse = M * (bias - dv - softness * P);
    CALL vectorMultiplyScalarND
(tv
, j.P
, j.softness
)     CALL vectorSubVectorND
(impulse
, j.bias
, dv
)     CALL vectorSubVectorND
(impulse
, impulse
, tv
)     CALL matrixMultiplyVector
(j.M
, impulse
, impulse
)  
    ' body1->velocity -= body1->invMass * impulse;
 
    CALL vectorMultiplyScalarND
(tv
, impulse
, body
(j.body1
).invMass
)     CALL vectorSubVectorND
(body
(j.body1
).velocity
, body
(j.body1
).velocity
, tv
)  
    ' body1->angularVelocity -= body1->invI * Cross(r1, impulse);
    crossScalar = vectorCross(j.r1, impulse)
    body(j.body1).angularVelocity = body(j.body1).angularVelocity - body(j.body1).invInertia * crossScalar
 
    CALL vectorMultiplyScalarND
(tv
, impulse
, body
(j.body2
).invMass
)     CALL vectorAddVectorND
(body
(j.body2
).velocity
, body
(j.body2
).velocity
, tv
)  
    crossScalar = vectorCross(j.r2, impulse)
    body(j.body2).angularVelocity = body(j.body2).angularVelocity + body(j.body2).invInertia * crossScalar
 
    CALL vectorAddVectorND
(j.P
, j.P
, impulse
)  
 
'**********************************************************************************************
'   Vector Math Ahead
'**********************************************************************************************
 
    v.x = x
    v.y = y
 
SUB vectorSetVector 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
)     o.x = v.x
    o.y = v.y
 
SUB vectorNeg 
(v 
AS tVECTOR2d
)     v.x = -v.x
    v.y = -v.y
 
SUB vectorNegND 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
)     o.x = -v.x
    o.y = -v.y
 
    v.x = v.x * s
    v.y = v.y * s
 
    o.x = v.x * s
    o.y = v.y * s
 
    v.x = v.x / s
    v.y = v.y / s
 
    o.x = v.x / s
    o.y = v.y / s
 
    v.x = v.x + s
    v.y = v.y + s
 
    o.x = v.x + s
    o.y = v.y + s
 
SUB vectorMultiplyVector 
(v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     v.x = v.x * m.x
    v.y = v.y * m.y
 
SUB vectorMultiplyVectorND 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     o.x = v.x * m.x
    o.y = v.y * m.y
 
SUB vectorDivideVector 
(v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     v.x = v.x / m.x
    v.y = v.y / m.y
 
SUB vectorAddVector 
(v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     v.x = v.x + m.x
    v.y = v.y + m.y
 
SUB vectorAddVectorND 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     o.x = v.x + m.x
    o.y = v.y + m.y
 
    v.x = v.x + m.x * s
    v.y = v.y + m.y * s
 
SUB vectorAddVectorScalarND 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
, m 
AS tVECTOR2d
, s 
AS _FLOAT)     o.x = v.x + m.x * s
    o.y = v.y + m.y * s
 
SUB vectorSubVector 
(v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     v.x = v.x - m.x
    v.y = v.y - m.y
 
SUB vectorSubVectorND 
(o 
AS tVECTOR2d
, v 
AS tVECTOR2d
, m 
AS tVECTOR2d
)     o.x = v.x - m.x
    o.y = v.y - m.y
 
    vectorLengthSq = v.x * v.x + v.y * v.y
 
    vectorLength 
= SQR(vectorLengthSq
(v
)) 
    xp = v.x * c - v.y * s
    yp = v.x * s + v.y * c
    v.x = xp
    v.y = yp
 
SUB vectorNormalize 
(v 
AS tVECTOR2d
)     lenSQ = vectorLengthSq(v)
        invLen 
= 1.0 / SQR(lenSQ
)        v.x = v.x * invLen
        v.y = v.y * invLen
 
SUB vectorMin 
(a 
AS tVECTOR2d
, b 
AS tVECTOR2d
, o 
AS tVECTOR2d
)     o.x = scalarMin(a.x, b.x)
    o.y = scalarMin(a.y, b.y)
 
SUB vectorMax 
(a 
AS tVECTOR2d
, b 
AS tVECTOR2d
, o 
AS tVECTOR2d
)     o.x = scalarMax(a.x, b.x)
    o.y = scalarMax(a.y, b.y)
 
    vectorDot = a.x * b.x + a.y * b.y
 
    dx = b.x - a.x
    dy = b.y - a.y
    vectorSqDist = dx * dx + dy * dy
 
    vectorDistance 
= SQR(vectorSqDist
(a
, b
)) 
    vectorCross = a.x * b.y - a.y * b.x
 
    o.x = v.y * -a
    o.y = v.x * a
 
    vectorArea = (((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)))
 
    vectorLeft = vectorArea(a, b, c) > 0
 
FUNCTION vectorLeftOn 
(a 
AS tVECTOR2d
, b 
AS tVECTOR2d
, c 
AS tVECTOR2d
)     vectorLeftOn = vectorArea(a, b, c) >= 0
 
    vectorRight = vectorArea(a, b, c) < 0
 
FUNCTION vectorRightOn 
(a 
AS tVECTOR2d
, b 
AS tVECTOR2d
, c 
AS tVECTOR2d
)     vectorRightOn = vectorArea(a, b, c) <= 0
 
        vectorCollinear = (vectorArea(a, b, c) = 0)
 
        ab.x = b.x - a.x
        ab.y = b.y - a.y
        bc.x = c.x - b.x
        bc.y = c.y - b.y
 
        dot = ab.x * bc.x + ab.y * bc.y
        magA 
= SQR(ab.x 
* ab.x 
+ ab.y 
* ab.y
)        magB 
= SQR(bc.x 
* bc.x 
+ bc.y 
* bc.y
)        angle 
= _ACOS(dot 
/ (magA 
* magB
))        vectorCollinear = angle < thresholdAngle
 
SUB vectorGetSupport 
(p
() AS tPOLY
, body
() AS tBODY
, index 
AS INTEGER, dir 
AS tVECTOR2d
, bestVertex 
AS tVECTOR2d
)     bestVertex.x = -9999999
    bestVertex.y = -9999999
    bestProjection = -9999999
 
    FOR i 
= 0 TO body
(index
).pa.count
         v = p(i + body(index).pa.start).vert
        projection = vectorDot(v, dir)
        IF projection 
> bestProjection 
THEN             bestVertex = v
            bestProjection = projection
 
'**********************************************************************************************
'   Matrix Stuff Ahead
'**********************************************************************************************
 
    m.m00 = c
    m.m01 = -s
    m.m10 = s
    m.m11 = c
 
    m.m00 = a
    m.m01 = b
    m.m10 = c
    m.m11 = d
 
SUB matrixAbs 
(m 
AS tMATRIX2d
, o 
AS tMATRIX2d
)  
SUB matrixGetAxisX 
(m 
AS tMATRIX2d
, o 
AS tVECTOR2d
)     o.x = m.m00
    o.y = m.m10
 
SUB matrixGetAxisY 
(m 
AS tMATRIX2d
, o 
AS tVECTOR2d
)     o.x = m.m01
    o.y = m.m11
 
SUB matrixTransposeI 
(m 
AS tMATRIX2d
)  
SUB matrixTranspose 
(m 
AS tMATRIX2d
, o 
AS tMATRIX2d
)     tm.m00 = m.m00
    tm.m01 = m.m10
    tm.m10 = m.m01
    tm.m11 = m.m11
    o = tm
 
SUB matrixInvert 
(m 
AS tMATRIX2d
, o 
AS tMATRIX2d
)  
    a = m.m00: b = m.m01: c = m.m10: d = m.m11
    det = a * d - b * c
 
    det = 1 / det
    tm.m00 = det * d: tm.m01 = -det * b
    tm.m10 = -det * c: tm.m11 = det * a
    o = tm
 
SUB matrixMultiplyVector 
(m 
AS tMATRIX2d
, v 
AS tVECTOR2d
, o 
AS tVECTOR2d
)     t.x = m.m00 * v.x + m.m01 * v.y
    t.y = m.m10 * v.x + m.m11 * v.y
    o = t
 
SUB matrixAddMatrix 
(m 
AS tMATRIX2d
, x 
AS tMATRIX2d
, o 
AS tMATRIX2d
)     o.m00 = m.m00 + x.m00
    o.m01 = m.m01 + x.m01
    o.m10 = m.m10 + x.m10
    o.m11 = m.m11 + x.m11
 
SUB matrixMultiplyMatrix 
(m 
AS tMATRIX2d
, x 
AS tMATRIX2d
, o 
AS tMATRIX2d
)     o.m00 = m.m00 * x.m00 + m.m01 * x.m10
    o.m01 = m.m00 * x.m01 + m.m01 * x.m11
    o.m10 = m.m10 * x.m00 + m.m11 * x.m10
    o.m11 = m.m10 * x.m01 + m.m11 * x.m11
 
'**********************************************************************************************
'   Mostly Unused Stuff Ahead
'**********************************************************************************************
 
SUB polygonMakeCCW 
(obj 
AS tTRIANGLE
)     IF vectorLeft
(obj.a
, obj.b
, obj.c
) = 0 THEN  
    polygonIsReflex = vectorRight(t.a, t.b, t.c)
 
        scalarMin = a
        scalarMin = b
 
        scalarMax = a
        scalarMax = b
 
SUB lineIntersection 
(l1 
AS tLINE2d
, l2 
AS tLINE2d
, o 
AS tVECTOR2d
)     o.x = 0
    o.y = 0
    a1 = l1.b.y - l1.a.y
    b1 = l1.a.x - l1.b.x
    c1 = a1 * l1.a.x + b1 * l1.a.y
    a2 = l2.b.y - l2.a.y
    b2 = l2.a.x - l2.b.x
    c2 = a2 * l2.a.x + b2 * l2.a.y
    det = a1 * b2 - a2 * b1
 
        o.x = (b2 * c1 - b1 * c2) / det
        o.y = (a1 * c2 - a2 * c1) / det
 
FUNCTION lineSegmentsIntersect 
(l1 
AS tLINE2d
, l2 
AS tLINE2d
)     dx = l1.b.x - l1.a.x
    dy = l1.b.y - l1.a.y
    da = l2.b.x - l2.a.x
    db = l2.b.y - l2.a.y
        lineSegmentsIntersect = 0
        s = (dx * (l2.a.y - l1.a.y) + dy * (l1.a.x - l2.a.x)) / (da * dy - db * dx)
        t = (da * (l1.a.y - l2.a.y) + db * (l2.a.x - l1.a.x)) / (db * dx - da * dy)
        lineSegmentsIntersect 
= (s 
>= 0 AND s 
<= 1 AND t 
>= 0 AND t 
<= 1) 
'**********************************************************************************************
'   Impulse Specific Math Ahead
'**********************************************************************************************
 
    impulseEqual 
= ABS(a 
- b
) <= cEPSILON
 
        impulseClamp = min
            impulseClamp = max
            impulseClamp = a
 
    impulseRound 
= INT(a 
+ 0.5) 
    impulseRandom_float 
= ((max 
- min
) * RND + min
) 
    impulseRandomInteger 
= INT((max 
- min
) * RND + min
) 
    impulseGT = (a >= b * cBIAS_RELATIVE + a * cBIAS_ABSOLUTE)
 
'**********************************************************************************************
'   Troubleshooting Tools
'**********************************************************************************************
 
    PRINT "---------------------------"     PRINT n; 
" u:"; u.m00; 
"|"; u.m10
     PRINT "       "; u.m10; 
"|"; u.m11
  
'**********************************************************************************************
'   String Hash
'**********************************************************************************************
 
    p_pow = 1
        hash_value 
= (hash_value 
+ (ASC(MID$(s
, i
)) - 97 + 1) * p_pow
)        p_pow 
= (p_pow 
* p
) MOD m
    computeHash = hash_value
'**********************************************************************************************
'   Load Bitmap
'**********************************************************************************************
        IF textureMap
(index
) > -2 THEN             PRINT "Unable to load image "; fl; 
" with return of "; textureMap
(index
)