2D Physics Engine
#1
Hello All,

I've made a demo of the physics engine ("fzxNGN") I've been working for the last few years. Its a port of the Impulse engine written by Randy Gaul. Its been 100% ported to QB64 and not a 3rd part wrapper. Some may recognize the demo from a few years back, I've simply updated it to the newer engine, and changed some of the graphics.

Inside the compressed file you will find two directories, "fzxNGN_BASE_v2" and "fzxDemo" The "fzxNGN_BASE" is the core of the engine. The actual demo is located in "fzxDemo/fzxDemo.bas". Hopefully, all you have to do is run the "fzxDemo.bas" to play with the demo. I develop in Linux, so there may be some changes needed for Windows and Mac. And the speed of your machine may vary from mine so adjusting the dt(delta time) and iterations may help. My machine is probably old enough to drive a car in my state. Undecided

Code: (Select All)
DIM AS LONG iterations: iterations = 2
DIM SHARED AS DOUBLE dt: dt = 1 / 60

The idea of this mini game is to cross the sketchy bridge and then make the loop-to-loop. Beware the sketchy bridge will break. The more red the bridge is, the more stress its under. This was in part a test of a game mechanic I was kicking around for a driving/platformer.

Current features:
  • Rigid body simulation
  • Circle and polygon primitives
  • Joint simulation
  • Camera library to help with large play fields
  • Input Library
  • Finite State machine helper functions
  • Perlin noise library
  • XML parsing library - (not 100% and not fully integrated)
  • LERP functions
  • FPS helper functions
  • Countless vector, matrix math functions

Caveats:
  • Not documented
  • Work in progress
  • Not fully optimized
  • Possibility of vestigial code, or code that has yet to be updated.
When time permits, I'll work on proper documentation.

As far as license goes:
Quote: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.

I will try to use this thread to post more updates for the engine.

[Image: screenshot.png]


Attached Files
.7z   fzxNGN_Demo_1.7z (Size: 1.61 MB / Downloads: 35)
Reply
#2
@justsomeguy Thank you for sharing. You can probably place this under Libraries > One Hit Wonders for better visibility.

Also hope you'll be pushing this to a GitHub repo.  Smile
Reply
#3
I have thrown together a bare bones example/demo so that you guys can see minimum to get something going.

Code: (Select All)
'**********************************************************************************************
'   fzxBareBones
'**********************************************************************************************

'$DYNAMIC
OPTION _EXPLICIT
_TITLE "fzxNGN Bare Bones"

' Initialize FZXNGN types, globals and constants
'$include:'..\fzxNGN_BASE_v2\fzxNGN_ini.bas'

SCREEN _NEWIMAGE(1024, 768, 32)

DIM AS LONG iterations: iterations = 2
DIM SHARED AS DOUBLE dt: dt = 1 / 60

'**********************************************************************************************
' Build the playfield
'**********************************************************************************************

buildScene

'**********************************************************************************************
' This is the main loop
'**********************************************************************************************

DO
  CLS
  fzxHandleInputDevice
  animatescene
  fzxImpulseStep dt, iterations
  renderBodies
  _DISPLAY
LOOP UNTIL INKEY$ = CHR$(27)

SYSTEM

' This provides access to all of the fzxNGN functionality
'$include:'..\fzxNGN_BASE_v2\fzxNGN_BASE.bas'

'**********************************************************************************************
'    This is where you interact with the world.
'**********************************************************************************************

SUB animatescene
  DIM AS LONG temp

  ' Create a object on mouse click
  IF __fzxInputDevice.mouse.b1.NegEdge THEN
    ' Drop a ball or a box, flip a coin
    IF RND > .5 THEN
      temp = fzxCreateCircleBodyEx("b" + _TRIM$(STR$(RND * 1000000000)), 10)
    ELSE
      temp = fzxCreateBoxBodyEx("b" + _TRIM$(STR$(RND * 1000000000)), 10, 10)
    END IF
    ' Set the bodies parameters
    ' Put the body where the mouse is on the screen
    fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxInputDevice.mouse.worldPosition.x, __fzxInputDevice.mouse.worldPosition.y
    ' Give it the mouse's velocity, so you can throw it
    fzxSetBody cFZX_PARAMETER_VELOCITY, temp, __fzxInputDevice.mouse.velocity.x, __fzxInputDevice.mouse.velocity.y
    ' Change its orientation or angle
    fzxSetBody cFZX_PARAMETER_ORIENT, temp, _D2R(RND * 360), 0
    ' Set the bouncyness
    fzxSetBody cFZX_PARAMETER_RESTITUTION, temp, .95, 0 ' Bounce
    ' Set the friction values of the body
    fzxSetBody cFZX_PARAMETER_STATICFRICTION, temp, .1, 0
    fzxSetBody cFZX_PARAMETER_DYNAMICFRICTION, temp, .85, 0
    ' Bodies wont live forever
    fzxSetBody cFZX_PARAMETER_LIFETIME, temp, RND * 20 + 10, 0
  END IF

END SUB

'********************************************************
'   Build you world here
'********************************************************

SUB buildScene
  DIM AS LONG temp

  'Initialize camera
  __fzxCamera.zoom = 1
  fzxCalculateFOV
  ' Set camera position
  fzxVector2DSet __fzxCamera.position, __fzxWorld.spawn.x, __fzxWorld.spawn.y - 300

  '********************************************************
  '   Setup World
  '********************************************************

  fzxVector2DSet __fzxWorld.minusLimit, -200000, -200000
  fzxVector2DSet __fzxWorld.plusLimit, 200000, 200000
  fzxVector2DSet __fzxWorld.spawn, 0, 0
  fzxVector2DSet __fzxWorld.gravity, 0.0, 10.0

  ' Some math used on the impulse side
  ' Todo: move this elsewhere
  DIM o AS tFZX_VECTOR2d
  fzxVector2DMultiplyScalarND o, __fzxWorld.gravity, dt
  __fzxWorld.resting = fzxVector2DLengthSq(o) + cFZX_EPSILON

  '********************************************************
  '   Build Level
  '********************************************************

  temp = fzxCreateBoxBodyEx("floor", 800, 10)
  fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxWorld.spawn.x, __fzxWorld.spawn.y
  fzxSetBody cFZX_PARAMETER_STATIC, temp, 0, 0

END SUB

SUB renderBodies STATIC
  DIM i AS LONG
  DIM AS tFZX_VECTOR2d scSize, scMid, scUpperLeft, camUpperLeft, aabbUpperLeft, aabbSize, aabbHalfSize
  DIM AS LONG ub: ub = UBOUND(__fzxBody)

  ' Todo : move this to camera functions
  fzxVector2DSet aabbSize, 40000, 40000
  fzxVector2DSet aabbHalfSize, aabbSize.x / 2, aabbSize.y / 2

  fzxVector2DSet scUpperLeft, 0, 0
  fzxVector2DSet scSize, _WIDTH, _HEIGHT

  fzxVector2DDivideScalarND scMid, scSize, 2
  fzxVector2DSubVectorND camUpperLeft, __fzxCamera.position, scMid

  'Draw all of the bodies that are visible
  i = 0: DO WHILE i < ub
    IF __fzxBody(i).enable THEN
      'fzxAABB to cut down on rendering objects out of camera view
      fzxVector2DSubVectorND aabbUpperLeft, __fzxBody(i).fzx.position, aabbHalfSize
      IF fzxAABBOverlap(camUpperLeft.x, camUpperLeft.y, scSize.x, scSize.y, aabbUpperLeft.x, aabbUpperLeft.y, aabbSize.x, aabbSize.y) THEN
        IF __fzxBody(i).shape.ty = cFZX_SHAPE_CIRCLE THEN
          renderWireFrameCircle i, _RGB32(0, 255, 0)
        ELSE IF __fzxBody(i).shape.ty = cFZX_SHAPE_POLYGON THEN
            renderWireFramePoly i
          END IF
        END IF
      END IF
    END IF
    i = i + 1
  LOOP

END SUB

SUB renderWireFrameCircle (index AS LONG, c AS LONG)
  DIM AS tFZX_VECTOR2d o1, o2
  fzxWorldToCameraEx __fzxBody(index).fzx.position, o1
  CIRCLE (o1.x, o1.y), __fzxBody(index).shape.radius * __fzxCamera.zoom, c
  o2.x = o1.x + (__fzxBody(index).shape.radius * __fzxCamera.zoom) * COS(__fzxBody(index).fzx.orient)
  o2.y = o1.y + (__fzxBody(index).shape.radius * __fzxCamera.zoom) * SIN(__fzxBody(index).fzx.orient)
  LINE (o1.x, o1.y)-(o2.x, o2.y), c
END SUB

SUB renderWireFramePoly (index AS LONG)
  DIM vert(3) AS tFZX_VECTOR2d

  fzxGetBodyVert index, 0, vert(0)
  fzxWorldToCamera index, vert(0)

  fzxGetBodyVert index, 1, vert(1)
  fzxWorldToCamera index, vert(1)

  fzxGetBodyVert index, 2, vert(2)
  fzxWorldToCamera index, vert(2)

  fzxGetBodyVert index, 3, vert(3)
  fzxWorldToCamera index, vert(3)

  LINE (vert(0).x, vert(0).y)-(vert(1).x, vert(1).y), _RGB(0, 255, 0)
  LINE (vert(1).x, vert(1).y)-(vert(2).x, vert(2).y), _RGB(0, 255, 0)
  LINE (vert(2).x, vert(2).y)-(vert(3).x, vert(3).y), _RGB(0, 255, 0)
  LINE (vert(3).x, vert(3).y)-(vert(0).x, vert(0).y), _RGB(0, 255, 0)
END SUB



[Image: barebonesscreenshot2.png]
Reply
#4
I have created a GITHUB for the engine, now lets see if the QB64 devs will implement git into the QB64 IDE. Big Grin
https://github.com/mechatronic3000/fzxNGN
Reply
#5
I made some modifications to the scene to play around with the physics.

I'm wondering about something curious that happens, I'll call it the "popcorn effect". If you start filing the room I created with objects at some point the objects seem to get extra impulse from somewhere. It looks like popcorn and doesn't stop until the end of object lifetimes starts removing them, allowing the scene to settle down.

Any idea why this happens? Are there settings to control this kind of chaos from happening?

Code: (Select All)
'**********************************************************************************************
'   fzxBareBones
'**********************************************************************************************

'$DYNAMIC
OPTION _EXPLICIT
_TITLE "fzxNGN Bare Bones"

' Initialize FZXNGN types, globals and constants
'$include:'..\fzxNGN_BASE_v2\fzxNGN_ini.bas'

SCREEN _NEWIMAGE(1024, 768, 32)

DIM AS LONG iterations: iterations = 2
DIM SHARED AS DOUBLE dt: dt = 1 / 60

'**********************************************************************************************
' Build the playfield
'**********************************************************************************************

buildScene

'**********************************************************************************************
' This is the main loop
'**********************************************************************************************

DO
    CLS
    fzxHandleInputDevice
    animatescene
    fzxImpulseStep dt, iterations
    renderBodies
    _DISPLAY
LOOP UNTIL INKEY$ = CHR$(27)

SYSTEM

' This provides access to all of the fzxNGN functionality
'$include:'..\fzxNGN_BASE_v2\fzxNGN_BASE.bas'

'**********************************************************************************************
'    This is where you interact with the world.
'**********************************************************************************************

SUB animatescene
    DIM AS LONG temp

    ' Create a object on mouse click
    IF __fzxInputDevice.mouse.b1.NegEdge THEN
        ' Drop a ball or a box, flip a coin
        IF RND > .5 THEN
            temp = fzxCreateCircleBodyEx("b" + _TRIM$(STR$(RND * 1000000000)), 20)
        ELSE
            temp = fzxCreateBoxBodyEx("b" + _TRIM$(STR$(RND * 1000000000)), 20, 20)
        END IF
        ' Set the bodies parameters
        ' Put the body where the mouse is on the screen
        fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxInputDevice.mouse.worldPosition.x, __fzxInputDevice.mouse.worldPosition.y
        ' Give it the mouse's velocity, so you can throw it
        fzxSetBody cFZX_PARAMETER_VELOCITY, temp, __fzxInputDevice.mouse.velocity.x, __fzxInputDevice.mouse.velocity.y
        ' Change its orientation or angle
        fzxSetBody cFZX_PARAMETER_ORIENT, temp, _D2R(RND * 360), 0
        ' Set the bouncyness
        fzxSetBody cFZX_PARAMETER_RESTITUTION, temp, .95, 0 ' Bounce
        ' Set the friction values of the body
        fzxSetBody cFZX_PARAMETER_STATICFRICTION, temp, .1, 0
        fzxSetBody cFZX_PARAMETER_DYNAMICFRICTION, temp, .85, 0
        ' Bodies wont live forever
        fzxSetBody cFZX_PARAMETER_LIFETIME, temp, RND * 20 + 10, 0
    END IF

END SUB

'********************************************************
'   Build you world here
'********************************************************

SUB buildScene
    DIM AS LONG temp

    'Initialize camera
    __fzxCamera.zoom = .5
    fzxCalculateFOV
    ' Set camera position
    fzxVector2DSet __fzxCamera.position, __fzxWorld.spawn.x, __fzxWorld.spawn.y - 300

    '********************************************************
    '   Setup World
    '********************************************************

    fzxVector2DSet __fzxWorld.minusLimit, -200000, -200000
    fzxVector2DSet __fzxWorld.plusLimit, 200000, 200000
    fzxVector2DSet __fzxWorld.spawn, 0, 0
    fzxVector2DSet __fzxWorld.gravity, 0.0, 10.0

    ' Some math used on the impulse side
    ' Todo: move this elsewhere
    DIM o AS tFZX_VECTOR2d
    fzxVector2DMultiplyScalarND o, __fzxWorld.gravity, dt
    __fzxWorld.resting = fzxVector2DLengthSq(o) + cFZX_EPSILON

    '********************************************************
    '   Build Level
    '********************************************************

    temp = fzxCreateBoxBodyEx("floor", 800, 10)
    fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxWorld.spawn.x, __fzxWorld.spawn.y
    fzxSetBody cFZX_PARAMETER_STATIC, temp, 0, 0


    temp = fzxCreateBoxBodyEx("Wall1", 10, 400)
    fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxWorld.spawn.x - 810, __fzxWorld.spawn.y - 390
    fzxSetBody cFZX_PARAMETER_STATIC, temp, 0, 0

    temp = fzxCreateBoxBodyEx("Wall2", 10, 400)
    fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxWorld.spawn.x + 810, __fzxWorld.spawn.y - 390
    fzxSetBody cFZX_PARAMETER_STATIC, temp, 0, 0

    temp = fzxCreateBoxBodyEx("Ceiling", 800, 10)
    fzxSetBody cFZX_PARAMETER_POSITION, temp, __fzxWorld.spawn.x, __fzxWorld.spawn.y - 780
    fzxSetBody cFZX_PARAMETER_STATIC, temp, 0, 0




END SUB

SUB renderBodies STATIC
    DIM i AS LONG
    DIM AS tFZX_VECTOR2d scSize, scMid, scUpperLeft, camUpperLeft, aabbUpperLeft, aabbSize, aabbHalfSize
    DIM AS LONG ub: ub = UBOUND(__fzxBody)

    ' Todo : move this to camera functions
    fzxVector2DSet aabbSize, 40000, 40000
    fzxVector2DSet aabbHalfSize, aabbSize.x / 2, aabbSize.y / 2

    fzxVector2DSet scUpperLeft, 0, 0
    fzxVector2DSet scSize, _WIDTH, _HEIGHT

    fzxVector2DDivideScalarND scMid, scSize, 2
    fzxVector2DSubVectorND camUpperLeft, __fzxCamera.position, scMid

    'Draw all of the bodies that are visible
    i = 0: DO WHILE i < ub
        IF __fzxBody(i).enable THEN
            'fzxAABB to cut down on rendering objects out of camera view
            fzxVector2DSubVectorND aabbUpperLeft, __fzxBody(i).fzx.position, aabbHalfSize
            IF fzxAABBOverlap(camUpperLeft.x, camUpperLeft.y, scSize.x, scSize.y, aabbUpperLeft.x, aabbUpperLeft.y, aabbSize.x, aabbSize.y) THEN
                IF __fzxBody(i).shape.ty = cFZX_SHAPE_CIRCLE THEN
                    renderWireFrameCircle i, _RGB32(0, 255, 0)
                ELSE IF __fzxBody(i).shape.ty = cFZX_SHAPE_POLYGON THEN
                        renderWireFramePoly i
                    END IF
                END IF
            END IF
        END IF
        i = i + 1
    LOOP

END SUB

SUB renderWireFrameCircle (index AS LONG, c AS LONG)
    DIM AS tFZX_VECTOR2d o1, o2
    fzxWorldToCameraEx __fzxBody(index).fzx.position, o1
    CIRCLE (o1.x, o1.y), __fzxBody(index).shape.radius * __fzxCamera.zoom, c
    o2.x = o1.x + (__fzxBody(index).shape.radius * __fzxCamera.zoom) * COS(__fzxBody(index).fzx.orient)
    o2.y = o1.y + (__fzxBody(index).shape.radius * __fzxCamera.zoom) * SIN(__fzxBody(index).fzx.orient)
    LINE (o1.x, o1.y)-(o2.x, o2.y), c
END SUB

SUB renderWireFramePoly (index AS LONG)
    DIM vert(3) AS tFZX_VECTOR2d

    fzxGetBodyVert index, 0, vert(0)
    fzxWorldToCamera index, vert(0)

    fzxGetBodyVert index, 1, vert(1)
    fzxWorldToCamera index, vert(1)

    fzxGetBodyVert index, 2, vert(2)
    fzxWorldToCamera index, vert(2)

    fzxGetBodyVert index, 3, vert(3)
    fzxWorldToCamera index, vert(3)

    LINE (vert(0).x, vert(0).y)-(vert(1).x, vert(1).y), _RGB(0, 255, 0)
    LINE (vert(1).x, vert(1).y)-(vert(2).x, vert(2).y), _RGB(0, 255, 0)
    LINE (vert(2).x, vert(2).y)-(vert(3).x, vert(3).y), _RGB(0, 255, 0)
    LINE (vert(3).x, vert(3).y)-(vert(0).x, vert(0).y), _RGB(0, 255, 0)
END SUB


Attached Files Image(s)
   
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#6
By the way ... this engine is awesome!
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#7
(06-17-2023, 06:44 PM)TerryRitchie Wrote: I made some modifications to the scene to play around with the physics.

I'm wondering about something curious that happens, I'll call it the "popcorn effect". If you start filing the room I created with objects at some point the objects seem to get extra impulse from somewhere. It looks like popcorn and doesn't stop until the end of object lifetimes starts removing them, allowing the scene to settle down.

Any idea why this happens? Are there settings to control this kind of chaos from happening?

' Set the bouncyness
fzxSetBody cFZX_PARAMETER_RESTITUTION, temp, .95, 0 ' Bounce

Try lowering the restitution to something like .5. Using .95 is like a super ball on steroids. I did that to make it more bouncy.
Reply
#8
TerryRitchie and justsomeguy I downloaded the Bare bones. Guess what?.....That right! I had file problems. Do I need to leave because I'm the bearer of bad news. Maybe not because you and justsomeguy are curious.

I had to adjust the path in '$include:'..\fzxNGN_BASE_v2\fzxNGN_ini.bas' to '$include:'\fzxNGN_BASE_v2\fzxNGN_ini.bas'. This second one runs great. The reason it runs like this is probably where I put it.  That's better than the last time. I hope I did not caused too much trouble by the way I did that. I enjoyed the program.

To justsomeguy, This was aimed at TerryRitchie. I had some file problems in his banner program I could not figure out without help. He did great in helping. if I had not move the folder like said I would still have trouble getting the path right. Thank you for this program. I liked it.
Reply
#9
(06-17-2023, 08:11 PM)GareBear Wrote: TerryRitchie and justsomeguy I downloaded the Bare bones. Guess what?.....That right! I had file problems. Do I need to leave because I'm the bearer of bad news. Maybe not because you and justsomeguy are curious.

I had to adjust the path in '$include:'..\fzxNGN_BASE_v2\fzxNGN_ini.bas' to '$include:'\fzxNGN_BASE_v2\fzxNGN_ini.bas'. This second one runs great. The reason it runs like this is probably where I put it.  That's better than the last time. I hope I did not caused too much trouble by the way I did that. I enjoyed the program.

To justsomeguy, This was aimed at TerryRitchie. I had some file problems in his banner program I could not figure out without help. He did great in helping. if I had not move the folder like said I would still have trouble getting the path right. Thank you for this program. I liked it.

Copy the "assets" folder found in the example folder to your QB64PE folder. I had the same issue until I copied the folder.
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#10
justsomeguy, I must have downloaded it after you tweaked it. I found the line you had on the forum and what I got is the same. It has a lot of bouncing. More came almost as fast as I clicked. That way it looked to me. Keep it up.
Reply




Users browsing this thread: 3 Guest(s)