'$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
)