QB64 Phoenix Edition
2D Physics Engine Help - Printable Version

+- QB64 Phoenix Edition (https://staging.qb64phoenix.com)
+-- Forum: QB64 Rising (https://staging.qb64phoenix.com/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://staging.qb64phoenix.com/forumdisplay.php?fid=3)
+---- Forum: Help Me! (https://staging.qb64phoenix.com/forumdisplay.php?fid=10)
+---- Thread: 2D Physics Engine Help (/showthread.php?tid=1749)

Pages: 1 2


2D Physics Engine Help - TerryRitchie - 06-12-2023

Over the past few months I've made a few attempts at creating a 2D physics library for QB64 but have failed miserably. My first few attempts were writing something from scratch. I quickly realized that while I have a fair grasp on basic trig and vector math I have nowhere near the knowledge to implement such things as angular momentum, raytracing, and 2D collision physics. Even after trying to tutor myself on the subject I seem to still be just as confused (if not more).

I then decided to follow a few video tutorials I found on Youtube related to creating a 2D physics engine. Of course these are all either meant for C++, Java, or JavaScript using OOP. I figured the process of converting the Java OOP code to functions and procedures would be fairly straight forward ... not so much. (Below I provide a link to the video series I am following along with the code I created so far).

My next thought was to incorporate the Box2D physics engine (the engine that Rovio used to create Angry Birds) into QB64. The Library is written in C++ and I figured by using DECLARE LIBRARY I could get this done. However, my C++ knowledge is lacking as well. Trying to figure out where pointers are used versus variables, their types, and when I need _OFFSETs is just confusing this old thick headed brain of mine. Here is a link to the Box2D physics engine:

https://box2d.org/about/

I feel QB64 needs a 2D physics engine to help attract more users. I know my games would be vastly improved if I had access to something that could create Angry Birds style game physics.

Is anyone with more knowledge on either subject, importing Box2D into QB64, or tutoring and helping me build an engine, willing to help? Box2D also has a light version, Box2D_Lite, that may be a good start if porting the engine is an option.

Below is a link to the video series I was following. I got to the end of Lesson 9 and am totally confused on what the presenter did when introducing the FOR loop.

The video series:

https://www.youtube.com/watch?v=vcgtwY39FT0&list=PLtrSb4XxIVbpZpV65kk73OoUcIrBzoSiO&index=2

And the code I hacked together so far trying to follow along and convert OOP to QB64 on the fly.

Code: (Select All)
'https://www.youtube.com/watch?v=XG6yOtEpRSw&list=PLtrSb4XxIVbpZpV65kk73OoUcIrBzoSiO&index=2


OPTION _EXPLICIT

CONST MIN_VALUE = -2.802597E-45


TYPE Type_Vector2
    x AS SINGLE
    y AS SINGLE
END TYPE

TYPE Type_Ray2D
    origin AS Type_Vector2
    direction AS Type_Vector2
END TYPE

TYPE Type_RaycastResult
    ppoint AS Type_Vector2
    normal AS Type_Vector2
    t AS SINGLE
    hit AS INTEGER

END TYPE

TYPE Type_Line2D
    from AS Type_Vector2
    too AS Type_Vector2
    colour AS _UNSIGNED LONG
    lifetime AS INTEGER
END TYPE

TYPE Type_Rigidbody2D
    position AS Type_Vector2
    rotation AS SINGLE
END TYPE

TYPE Type_Box2D '              rotated bounding box
    size AS Type_Vector2
    halfSize AS Type_Vector2
    rigidbody AS Type_Rigidbody2D
END TYPE

TYPE Type_AABB '              axis aligned bounding box (not rotated)
    size AS Type_Vector2
    halfSize AS Type_Vector2
    rigidbody AS Type_Rigidbody2D
END TYPE

TYPE Type_Circle
    Radius AS SINGLE
    rigidbody AS Type_Rigidbody2D
END TYPE


DIM __AABB AS Type_AABB
DIM __Circle AS Type_Circle
DIM __rigidbody2D AS Type_Rigidbody2D
DIM Vertices(4) AS Type_Vector2





SUB Line2D.setFromToo (__line2D AS Type_Line2D, from AS Type_Vector2, too AS Type_Vector2)

    __line2D.from = from
    __line2D.too = too

END SUB


SUB Line2D (__line2D AS Type_Line2D, from AS Type_Vector2, too AS Type_Vector2, colour AS _UNSIGNED LONG, lifetime AS INTEGER)

    __line2D.from = from
    __line2D.too = too
    __line2D.colour = colour
    __line2D.lifetime = lifetime

END SUB


FUNCTION Line2D.beginFrame (__line2d AS Type_Line2D)

    __line2d.lifetime = __line2d.lifetime - 1
    Line2D.beginFrame = __line2d.lifetime

END FUNCTION


SUB Line2D.getFrom (__line2d AS Type_Line2D, from AS Type_Vector2)

    from = __line2d.from

END SUB

SUB Line2D.getToo (__line2d AS Type_Line2D, too AS Type_Vector2)

    too = __line2d.too

END SUB


SUB Line2D.getStart (__line2d AS Type_Line2D, start AS Type_Vector2)

    start = __line2d.from

END SUB

SUB Line2D.getEnd (__line2d AS Type_Line2D, endd AS Type_Vector2)

    endd = __line2d.too

END SUB


FUNCTION Line2D.getColour (__line2d AS Type_Line2D)

    Line2D.getColour = __line2d.colour

END FUNCTION


FUNCTION Line2D.lengthSquared (__line2d AS Type_Line2D)

    DIM from AS Type_Vector2
    DIM too AS Type_Vector2
    DIM length AS Type_Vector2

    Line2D.getFrom __line2d, from
    Line2D.getToo __line2d, too

    length.x = too.x - from.x
    length.y = too.y - from.y

    Line2D.lengthSquared = lengthSquared(length)

END FUNCTION





'********************************
'*          RIGIDBODY          * <-----------------------------------------------------------------------
'********************************


'--------------------------------
'---- IntersectionDetector2D ----
'--------------------------------

FUNCTION PointOnLine (TestPoint AS Type_Vector2, __line2D AS Type_Line2D)

    ' based on the Slope Intercept Form of the equation of a straight line
    '
    '
    '  |                                      S = Line Start = (0,1)
    '  |                            (8,5)    E = Line End  = (8,5)
    ' 5+                            __ù      Need to get values of this formula: y = m * x + b
    '  |                          _-          Solve for m:
    '  |                        _--              - dy = Ey - Sy = 5 - 1 = 4
    ' 4+                    __-                - dx = Ex - Sx = 8 - 0 = 8
    '  |                  _-                          dy    4    1
    '  |      P        _--                      - m = ---- = --- = --- (m solved)
    ' 3+      ù      __-                                dx    8    2
    '  |    (2,3)  _-                          Solve for b:
    '  |        _--                              - b = y - mx  (plug in x (0) and y (1) from line start)
    ' 2+    __-                                            1
    '  |  _-                                    - b = 1 - --- * 0 = 1 - 0 = 1 (b solved)
    '  |_--                                                2
    ' 1ù                                      Plug in values from Px along with solved m and b to compare y result with Py
    '(0,1)                                              1
    '  |                                        - y = --- * x + 1 = y = .5 * 2 + 1 = y = 2 (FALSE) 2 is not equal to Py (3)
    '  +---+---+---+---+---+---+---+---+---            2
    ' 0    1  2  3  4  5  6  7  8
    '

    DIM lineStart AS Type_Vector2 ' start vector of line (x,y)
    DIM lineEnd AS Type_Vector2 '  end  vector of line (x,y)
    DIM dx AS SINGLE '              run  (delta in the x direction)
    DIM dy AS SINGLE '              rise (delta in the y direction)
    DIM m AS SINGLE '              slope (rise over run)
    DIM b AS SINGLE '              the y intercept

    PointOnLine = 0 '                                            assume point not on line

    '----------
    lineStart = __line2D.from
    lineEnd = __line2D.too
    'Line2D.getStart __line2D, lineStart '                        get line start vector (x,y)
    'Line2D.getEnd __line2D, lineEnd '                            get line end  vector (x,y)
    '----------

    dy = lineEnd.y - lineStart.y '                              calculate rise
    dx = lineEnd.x - lineStart.x '                              calculate run
    IF dx = 0 THEN '                                            vertical line? (avoid divide by 0)
        IF TestPoint.x = lineStart.x THEN '                      yes, do x values match?
            PointOnLine = -1 '                                  yes, must be on the line
            EXIT FUNCTION '                                      leave
        END IF
    END IF
    m = dy / dx '                                                calculate slope
    b = lineStart.y - (m * lineStart.x) '                        calculate y intercept
    IF TestPoint.y = m * TestPoint.x + b THEN PointOnLine = -1 ' point on line if y = mx + b

END FUNCTION


FUNCTION PointInCircle (TestPoint AS Type_Vector2, __circle AS Type_Circle)

    ' Check for point within circle.
    '
    '
    '              *********                      - Simply use Pythagoras to solve.
    '          ****        ****
    '      ***\                ***                - Calculate x and y sides from point to center
    '    **    \                  **              - if x side * x side + y side * y side <= radius * radius then point within circle
    '    *      \            x2    *              - (this method negates having to use square root)
    '  *  Radius\    +--------------*---__ù p2
    '  *          \ y2|              _*--          p1: x1 = p1.x - center.x
    '  *            \  |          __-- *                y1 = p1.y - center.y
    ' *              \ |      __--      *              x1 * x1 + y1 * y1 <= radius * radius (TRUE - point within circle)
    ' *              \|  __--  L2      *
    ' *    Center x,y ù--              *          p2: x2 = p2.x - center.x
    ' *                |\              *              y2 = p2.y - center.y
    ' *                | \ L1          *              x2 * x2 + y2 * y2 <= radius * radius (FALSE - point NOT within circle)
    '  *            y1|  \            *
    '  *              |  \          *
    '  *              +----ù        *
    '    *              x1  p1      *
    '    **                      **
    '      ***                ***
    '          ****        ****
    '              *********

    DIM circleCenter AS Type_Vector2 '  center coordinate of circle (x,y)
    DIM centerToPoint AS Type_Vector2 ' x,y lengths
    DIM radius AS SINGLE '              radius of circle

    PointInCircle = 0 '                                                          assume point not within circle

    '----------
    circleCenter = __circle.rigidbody.position
    'Circle.getCenter __circle, circleCenter '                                    get center coordinate of circle (x,y)
    '----------

    '----------
    radius = __circle.Radius
    'radius = Circle.getRadius(__circle) '                                        get radius of circle
    '----------

    centerToPoint.x = TestPoint.x - circleCenter.x '                            calculate x distance from point to center of circle
    centerToPoint.y = TestPoint.y - circleCenter.y '                            calculate y distance from point to center of circle
    IF lengthSquared(centerToPoint) <= radius * radius THEN PointInCircle = -1 ' return true if length <= radus of circle

END FUNCTION


FUNCTION PointInAABB (TestPoint AS Type_Vector2, box AS Type_AABB)

    ' Check for a point inside standard rectangle (AABB axis aligned bounding box)
    '
    '      .                              .          Four simple checks needed to see if point is within a non rotated rectangle
    '      .                              .
    '      .                              .          p1: p1.x <= max.x (TRUE) AND
    ' ..... +-------------------------------+ .....        min.x <= p1.x (TRUE) AND
    '      |min(x,y)                      |              p1.y <= max.y (TRUE) AND
    '      |                              |              min.y <= p1.y (TRUE) = All TRUE means within rectangle
    '      |                              |
    '      |                      p1      |          p2: p2.x <= max.x (FALSE) AND
    '      |                      .      |              min.x <= p2.x (TRUE) AND
    '      |                              |  p2        p2.y <= max.y (TRUE) AND
    '      |                              |  .          min.y <= p2.y (TRUE) = Any FALSE means NOT within rectangle
    '      |                              |
    '      |                              |
    '      |                              |
    '      |                      max(x,y)|
    ' ..... +-------------------------------+ .....
    '      .                              .
    '      .                              .
    '      .                              .

    DIM min AS Type_Vector2 ' upper left  rectangular coordinate (x,y)
    DIM max AS Type_Vector2 ' lower right rectangular coordinate (x,y)

    AABB.getMin box, min '                    get upper left  coordinate
    AABB.getMax box, max '                    get lower right coordinate
    PointInAABB = 0 '                          assume point not within AABB
    IF TestPoint.x <= max.x THEN '            perform the four checks
        IF min.x <= TestPoint.x THEN
            IF TestPoint.y <= max.y THEN
                IF min.y <= TestPoint.y THEN
                    PointInAABB = -1 '        if all true report point within
                END IF
            END IF
        END IF
    END IF

END FUNCTION


FUNCTION PointInBox2D (TestPoint AS Type_Vector2, box AS Type_Box2D)
    '
    ' Test for a point in a rotated 2D box by rotating the point into the box's local space
    '
    '              _-\
    '            _-  \        P
    '          _-      \      _ù rotated position
    '        _-        \  _-
    '      _-            \_-
    '    _-            _-\      - Rotate point P around origin C the same degree as rotated box
    '  -  Rotated    _-  \    - Point P is now in the local boxe's space
    '  \  AABB    _-      \    - From here it's just a simple AABB min/max check
    '    \        Cù        \
    '    \        \      _-
    '      \        \    _-    
    '      \        \ _-      |
    '        \        _\        /
    '        \    _-  \      /
    '          \  _-    \    _-
    '          \-        \ _-
    '                      ù original position
    '                      P

    DIM pointLocalBoxSpace AS Type_Vector2
    DIM min AS Type_Vector2
    DIM max AS Type_Vector2

    PointInBox2D = 0 '                                                          assume point not within
    Box2D.getMin box, min '                                                    get upper left coordinate
    Box2D.getMax box, max '                                                    get lower right coordinate
    pointLocalBoxSpace = TestPoint '                                            copy test point

    '+------------------------------------------------+
    '| Translate the point into the box's local space |
    '+------------------------------------------------+

    Rotate pointLocalBoxSpace, box.rigidbody.rotation, box.rigidbody.position ' rotate point into box's local space

    '+-----------------------------------------+
    '| Perform standard point within AABB test |
    '+-----------------------------------------+

    IF pointLocalBoxSpace.x <= max.x THEN '                                    perform the four AABB checks
        IF min.x <= pointLocalBoxSpace.x THEN
            IF pointLocalBoxSpace.y <= max.y THEN
                IF min.y <= pointLocalBoxSpace.y THEN
                    PointInBox2D = -1 '                                        if all true report point within
                END IF
            END IF
        END IF
    END IF

END FUNCTION


FUNCTION lineAndCircle (__line2D AS Type_Line2D, __circle AS Type_Circle)
    '
    ' Use projection to determine if a line is intersecting a circle
    '
    '                                  *********                  Determine if line B is intersecting circle:
    '                              ****        ****              - Line B end points are outside circle (check points within circle)
    '                          ***                ***              - If either end point within circle then line B is intersecting (return TRUE)
    '                        **                      **          - Get line segment x and y lengths and store in ab.x and ab.y
    '                        *                          *        - Get vector x and y lengths from center of circle to start of line segment
    '                      *                            *          - Store in centerToLineStart.x and centerToLineStart.y
    '                      *                              *      - Perform dot product of vectors to get a percentage of line segment
    '                      *                              *        -      centerToLineStart ù ab
    '                    *              Center            *          t = ------------------------  t = 0 to 1 (percentage of A to B)
    '                    *              x,y              *                      ab        ù ab
    '                    *            __+                *      - Add (ab * t) to Start to get point C (closest point to center)
    '                    *        __---  |                *      - check for point C within circle
    '                    *  ___---        |                *      - (this method negates the need to use square root)
    '              A    ___--              |              *
    '              __---  *              |              *
    '        ___---        *                            *
    ' Start ù------------------------------ù-----------------------------ù End
    '                        **          C          **
    '              B            ***  closest point  ***
    '                              ****        ****
    '      |                          *********                        |
    '      |---------------------------- ab --------------------------|
    '      |                                                            |

    DIM LineStart AS Type_Vector2 '          start line vector      (x,y)
    DIM LineEnd AS Type_Vector2 '            end  line vector      (x,y)
    DIM ab AS Type_Vector2 '                  line segment            (ex-sx,ey-sy)
    DIM circleCenter AS Type_Vector2 '        center of circle        (x,y)
    DIM centerToLineStart AS Type_Vector2 '  start of line to center (cx-sx,cy-sy)
    DIM t AS SINGLE '                        percentage of the line  (0 to 1)
    DIM closestPoint AS Type_Vector2 '        closest point on line to center

    lineAndCircle = 0 '                                                              assume no intersection

    '----------
    LineStart = __line2D.from
    LineEnd = __line2D.too
    'Line2D.getStart __line2D, LineStart '                                            get start vector of line (x,y)
    'Line2D.getEnd __line2D, LineEnd '                                                get end  vector of line (x,y)
    '----------

    IF PointInCircle(LineStart, __circle) OR PointInCircle(LineEnd, __circle) THEN ' is either line end point within circle?
        lineAndCircle = -1 '                                                        yes, then line must be intersecting circle
        EXIT FUNCTION '                                                              leave
    END IF
    ab.x = LineEnd.x - LineStart.x '                                                calculate line segment length
    ab.y = LineEnd.y - LineStart.y

    '+--------------------------------------------------------+
    '| Project point (circle position) onto ab (line segment) |
    '| result = parameterized position t                      |
    '+--------------------------------------------------------+

    '----------
    circleCenter = __circle.rigidbody.position
    'Circle.getCenter __circle, circleCenter '                                        get center of circle
    '----------

    centerToLineStart.x = circleCenter.x - LineStart.x '                            calculate length from center to start of line segment
    centerToLineStart.y = circleCenter.y - LineStart.y
    t = Dot(centerToLineStart, ab) / Dot(ab, ab) '                                  perform dot product on vectors to get percentage
    IF t < 0 OR t > 1 THEN EXIT FUNCTION '                                          leave if not between the line segment, no intersection

    '+--------------------------------------------+
    '| Find the closest point to the line segment |
    '+--------------------------------------------+

    closestPoint.x = LineStart.x + ab.x * t '                                        calculate closest line point to center of circle
    closestPoint.y = LineStart.y + ab.y * t
    lineAndCircle = PointInCircle(closestPoint, __circle) '                          return result of closest point within circle

END FUNCTION


FUNCTION lineAndAABB (__line2D AS Type_Line2D, box AS Type_AABB)

    'Raycasting

    DIM lineStart AS Type_Vector2
    DIM lineEnd AS Type_Vector2
    DIM unitVector AS Type_Vector2
    DIM min AS Type_Vector2
    DIM max AS Type_Vector2
    DIM tmin AS SINGLE
    DIM tmax AS SINGLE
    DIM t AS SINGLE

    lineAndAABB = 0
    Line2D.getStart __line2D, lineStart
    Line2D.getEnd __line2D, lineEnd

    IF PointInAABB(lineStart, box) OR PointInAABB(lineEnd, box) THEN
        lineAndAABB = -1
        EXIT FUNCTION
    END IF

    unitVector.x = lineEnd.x - lineStart.x
    unitVector.y = lineEnd.y - lineEnd.y

    Normalize unitVector

    IF unitVector.x <> 0 THEN unitVector.x = 1 / unitVector.x
    IF unitVector.y <> 0 THEN unitVector.y = 1 / unitVector.y

    AABB.getMin box, min
    min.x = min.x - lineStart.x * unitVector.x
    min.y = min.y - lineStart.y * unitVector.y
    AABB.getMax box, max
    max.x = max.x - lineStart.x * unitVector.x
    max.y = max.y - lineStart.y * unitVector.y

    tmin = MathMax(MathMin(min.x, max.x), MathMin(min.y, max.y))
    tmax = MathMin(MathMax(min.x, max.x), MathMax(min.y, max.y))

    IF tmax < 0 OR tmin > tmax THEN EXIT FUNCTION

    IF tmin < 0 THEN t = tmax ELSE t = tmin

    IF t > 0 AND t * t < Line2D.lengthSquared(__line2D) THEN lineAndAABB = -1


END FUNCTION


FUNCTION lineAndBox2D (__line2d AS Type_Line2D, box AS Type_Box2D)

    'Rotate the line into the box's local space

    DIM theta AS SINGLE
    DIM center AS Type_Vector2
    DIM localStart AS Type_Vector2
    DIM localEnd AS Type_Vector2
    DIM localLine AS Type_Line2D
    DIM min AS Type_Vector2
    DIM max AS Type_Vector2
    DIM __aabb AS Type_AABB

    theta = -box.rigidbody.rotation
    center = box.rigidbody.position

    Line2D.getStart __line2d, localStart
    Line2D.getEnd __line2d, localEnd
    Rotate localStart, theta, center
    Rotate localEnd, theta, center

    'Line2D localLine, localStart, localEnd, _RGB32(255, 255, 255), 1  (instead of 2 lines below)

    localLine.from = localStart
    localLine.too = localEnd

    Box2D.getMin box, min
    Box2D.getMax box, max

    AABB __aabb, min, max

    lineAndBox2D = lineAndAABB(localLine, __aabb)

END FUNCTION

' +----------+
' | Raycasts |
' +----------+


FUNCTION RaycastCircle (__circle AS Type_Circle, ray AS Type_Ray2D, result AS Type_RaycastResult)

    DIM originToCircle AS Type_Vector2
    DIM center AS Type_Vector2
    DIM origin AS Type_Vector2
    DIM radius AS SINGLE
    DIM radiusSquared AS SINGLE
    DIM originToCircleLengthSquared AS SINGLE
    DIM direction AS Type_Vector2
    DIM a AS SINGLE
    DIM bSq AS SINGLE
    DIM f AS SINGLE
    DIM t AS SINGLE
    DIM ppoint AS Type_Vector2
    DIM normal AS Type_Vector2

    RaycastCircle = 0

    RaycastResult.reset result

    Circle.getCenter __circle, center
    Ray2D.getOrigin ray, origin

    originToCircle.x = center.x - origin.x
    originToCircle.y = center.y - origin.y

    radius = Circle.getRadius(__circle)

    radiusSquared = radius * radius

    originToCircleLengthSquared = lengthSquared(originToCircle)

    ' Project the vector from the ray origin onto the direction of the ray

    Ray2D.getDirection ray, direction

    a = Dot(originToCircle, direction)

    bSq = originToCircleLengthSquared - (a * a)

    IF radiusSquared - bSq < 0 THEN EXIT FUNCTION

    f = SQR(radiusSquared - bSq)

    t = 0
    IF originToCircleLengthSquared < radiusSquared THEN
        t = a + f ' ray starts inside the circle
    ELSE
        t = a - f ' ray starts outside the circle
    END IF

    IF result.ppoint.x + result.ppoint.y = 0 THEN

        ppoint.x = origin.x + direction.x * t
        ppoint.y = origin.y + direction.y * t

        normal.x = ppoint.x - center.x
        normal.y = ppoint.y - center.y

        Normalize normal

        result.ppoint = ppoint
        result.normal = normal
        result.t = t
        result.hit = -1

    END IF

    RaycastCircle = -1

END FUNCTION


FUNCTION RaycastAABB (box AS Type_AABB, __Ray2D AS Type_Ray2D, result AS Type_RaycastResult)

    'DIM lineStart AS Type_Vector2
    'DIM lineEnd AS Type_Vector2
    DIM unitVector AS Type_Vector2
    DIM min AS Type_Vector2
    DIM max AS Type_Vector2
    DIM tmin AS SINGLE
    DIM tmax AS SINGLE
    DIM t AS SINGLE
    DIM hit AS INTEGER
    DIM ppoint AS Type_Vector2
    DIM normal AS Type_Vector2

    RaycastAABB = 0
    RaycastResult.reset result

    unitVector.x = __Ray2D.direction.x ' lineEnd.x - lineStart.x
    unitVector.y = __Ray2D.direction.y 'lineEnd.y - lineEnd.y

    Normalize unitVector

    IF unitVector.x <> 0 THEN unitVector.x = 1 / unitVector.x
    IF unitVector.y <> 0 THEN unitVector.y = 1 / unitVector.y

    AABB.getMin box, min
    min.x = min.x - __Ray2D.origin.x 'lineStart.x * unitVector.x
    min.y = min.y - __Ray2D.origin.y 'lineStart.y * unitVector.y
    AABB.getMax box, max
    max.x = max.x - __Ray2D.origin.x 'lineStart.x * unitVector.x
    max.y = max.y - __Ray2D.origin.y 'lineStart.y * unitVector.y

    tmin = MathMax(MathMin(min.x, max.x), MathMin(min.y, max.y))
    tmax = MathMin(MathMax(min.x, max.x), MathMax(min.y, max.y))

    IF tmax < 0 OR tmin > tmax THEN EXIT FUNCTION

    IF tmin < 0 THEN t = tmax ELSE t = tmin

    IF t > 0 THEN hit = -1

    IF NOT hit THEN EXIT FUNCTION

    IF result.ppoint.x = 0 AND result.ppoint.y = 0 THEN
        ppoint.x = __Ray2D.origin.x + __Ray2D.direction.x * t
        ppoint.y = __Ray2D.origin.y + __Ray2D.direction.y * t

        normal.x = __Ray2D.origin.x - ppoint.x
        normal.y = __Ray2D.origin.y - ppoint.y

        Normalize normal

        result.ppoint = ppoint
        result.normal = normal
        result.t = t
        result.hit = -1


    END IF

    RaycastAABB = -1


END FUNCTION


FUNCTION RaycastBox2D (box AS Type_Box2D, __Ray2D AS Type_Ray2D, result AS Type_RaycastResult)

    DIM xAxis AS Type_Vector2
    DIM yAxis AS Type_Vector2
    DIM zerozero AS Type_Vector2
    DIM p AS Type_Vector2
    DIM f AS Type_Vector2
    DIM e AS Type_Vector2
    DIM size AS Type_Vector2

    RaycastBox2D = 0
    RaycastResult.reset result

    Box2D.halfSize box, size

    xAxis.x = 1
    xAxis.y = 0
    yAxis.x = 0
    yAxis.y = 1

    Rotate xAxis, -box.rigidbody.rotation, zerozero
    Rotate yAxis, -box.rigidbody.rotation, zerozero

    p.x = box.rigidbody.position.x - __Ray2D.origin.x
    p.y = box.rigidbody.position.y - __Ray2D.origin.y

    ' Project the direction of the ray onto each axis of the box

    f.x = Dot(xAxis, __Ray2D.direction)
    f.y = Dot(yAxis, __Ray2D.direction)

    ' Next, project p onto every axis of the box

    e.x = Dot(xAxis, p)
    e.y = Dot(yAxis, p)





    RaycastBox2D = -1

END FUNCTION








'--------------------------------
'--------- RIGIDBODY2D ----------
'--------------------------------

SUB RigidBody2D.getPosition (__rigidbody2D AS Type_Rigidbody2D, position AS Type_Vector2)

    position.x = __rigidbody2D.position.x
    position.y = __rigidbody2D.position.y

END SUB

SUB RigidBody2D.setPosition (__rigidbody2D AS Type_Rigidbody2D, position AS Type_Vector2)

    __rigidbody2D.position.x = position.x
    __rigidbody2D.position.y = position.y

END SUB

FUNCTION RigidBody2D.getRotation (__rigidbody2D AS Type_Rigidbody2D)

    RigidBody2D.getRotation = __rigidbody2D.rotation

END FUNCTION

SUB RigidBody2D.setRotation (__rigidbody2D AS Type_Rigidbody2D, rotation AS SINGLE)

    __rigidbody2D.rotation = rotation

END SUB



'********************************
'*    PHYSICS2D PRIMATIVES    * <-----------------------------------------------------------------------
'********************************

'--------------------------------
'------------ AABB --------------
'--------------------------------

SUB AABB (__AABB AS Type_AABB, min AS Type_Vector2, max AS Type_Vector2)

    __AABB.size.x = max.x - min.x '                        set size of object
    __AABB.size.y = max.y - min.y
    __AABB.halfSize.x = __AABB.size.x * .5
    __AABB.halfSize.y = __AABB.size.y * .5

END SUB


SUB AABB.halfSize (__AABB AS Type_AABB, halfSize AS Type_Vector2)

    halfSize.x = __AABB.size.x * .5
    halfSize.y = __AABB.size.y * .5

END SUB


SUB AABB.getMin (__AABB AS Type_AABB, min AS Type_Vector2)

    DIM halfSize AS Type_Vector2

    AABB.halfSize __AABB, halfSize
    min.x = __AABB.rigidbody.position.x - halfSize.x
    min.y = __AABB.rigidbody.position.y - halfSize.y

END SUB


SUB AABB.getMax (__AABB AS Type_AABB, max AS Type_Vector2)

    DIM halfSize AS Type_Vector2

    AABB.halfSize __AABB, halfSize
    max.x = __AABB.rigidbody.position.x + halfSize.x
    max.y = __AABB.rigidbody.position.y + halfSize.y

END SUB

'--------------------------------
'----------- Box2D --------------
'--------------------------------

SUB Box2D (__box2D AS Type_Box2D, min AS Type_Vector2, max AS Type_Vector2)

    __box2D.size.x = max.x - min.x '                        set size of object
    __box2D.size.y = max.y - min.y
    __box2D.halfSize.x = __box2D.size.x * .5
    __box2D.halfSize.y = __box2D.size.y * .5

END SUB


SUB Box2D.halfSize (__box2D AS Type_Box2D, halfSize AS Type_Vector2)

    halfSize.x = __box2D.size.x * .5
    halfSize.y = __box2D.size.y * .5

END SUB


SUB Box2D.getMin (__box2D AS Type_Box2D, min AS Type_Vector2)

    DIM halfSize AS Type_Vector2

    Box2D.halfSize __box2D, halfSize
    min.x = __box2D.rigidbody.position.x - halfSize.x
    min.y = __box2D.rigidbody.position.y - halfSize.y

END SUB


SUB Box2D.getMax (__box2D AS Type_Box2D, max AS Type_Vector2)

    DIM halfSize AS Type_Vector2

    Box2D.halfSize __box2D, halfSize
    max.x = __box2D.rigidbody.position.x + halfSize.x
    max.y = __box2D.rigidbody.position.y + halfSize.y

END SUB


SUB Box2D.getVertices (__box2d AS Type_Box2D, Vertices() AS Type_Vector2)

    DIM min AS Type_Vector2
    DIM max AS Type_Vector2
    DIM vert AS Type_Vector2
    DIM vCount AS INTEGER

    Box2D.getMin __box2d, min
    Box2D.getMax __box2d, max

    Vertices(1).x = min.x
    Vertices(1).y = min.y
    Vertices(2).x = min.x
    Vertices(2).y = max.y
    Vertices(3).x = max.x
    Vertices(3).y = min.y
    Vertices(4).x = max.x
    Vertices(4).y = max.y

    IF __box2d.rigidbody.rotation <> 0 THEN
        vCount = 0
        DO
            vert = Vertices(vCount)
            Rotate vert, __box2d.rigidbody.rotation, __box2d.rigidbody.position
        LOOP UNTIL vCount = 4
    END IF

END SUB


'--------------------------------
'----------- Circle -------------
'--------------------------------

FUNCTION Circle.getRadius (__circle AS Type_Circle)

    Circle.getRadius = __circle.Radius

END FUNCTION

SUB Circle.setRadius (__circle AS Type_Circle, radius AS SINGLE)

    __circle.Radius = radius

END SUB


SUB Circle.getCenter (__circle AS Type_Circle, center AS Type_Vector2)

    center = __circle.rigidbody.position

END SUB


'--------------------------------
'--------- Collider2D -----------
'--------------------------------





'--------------------------------
'------------ Ray2D -------------
'--------------------------------


SUB Ray2D (__Ray2D AS Type_Ray2D, origin AS Type_Vector2, direction AS Type_Vector2)

    __Ray2D.origin = origin
    __Ray2D.direction = direction
    Normalize __Ray2D.direction

END SUB

SUB Ray2D.getOrigin (__ray2D AS Type_Ray2D, origin AS Type_Vector2)

    origin = __ray2D.origin

END SUB


SUB Ray2D.getDirection (__ray2D AS Type_Ray2D, direction AS Type_Vector2)

    direction = __ray2D.direction

END SUB


'--------------------------------
'--------- RaycastResult --------
'--------------------------------

SUB RaycastResult (__RaycastResult AS Type_RaycastResult)

    __RaycastResult.ppoint.x = 0
    __RaycastResult.ppoint.y = 0
    __RaycastResult.normal.x = 0
    __RaycastResult.normal.y = 0
    __RaycastResult.t = -1
    __RaycastResult.hit = 0

END SUB

SUB RaycastResult.init (__RaycastResult AS Type_RaycastResult, ppoint AS Type_Vector2, normal AS Type_Vector2, t AS SINGLE, hit AS INTEGER)

    __RaycastResult.ppoint = ppoint
    __RaycastResult.normal = normal
    __RaycastResult.t = t
    __RaycastResult.hit = hit

END SUB

SUB RaycastResult.reset (result AS Type_RaycastResult)

    IF result.ppoint.x OR result.ppoint.y THEN
        result.ppoint.x = 0
        result.ppoint.y = 0
        result.normal.x = 0
        result.normal.y = 0
        result.t = -1
        result.hit = 0
    END IF

END SUB




SUB AddVectors (V1 AS Type_Vector2, V2 AS Type_Vector2, Vout AS Type_Vector2)

    '          -  -
    ' Formula: V1 + V2 = (V1.x, v1.y) + (V2.x, V2.y) = (V1.x + V2.x, V1.y + V2.y)

    ' V1  - input : Vector 1
    ' V2  - input : Vector 2
    ' Vout - output: the new vector

    Vout.x = V1.x + V2.x ' x value of vector 2 gets added to x value of vector 1
    Vout.y = V1.y + V2.y ' y value of vector 2 gets added to y value of vector 1

END SUB


SUB SubtractVectors (V1 AS Type_Vector2, V2 AS Type_Vector2, Vout AS Type_Vector2)

    '          -  -
    ' Formula: V1 + V2 = (V1.x, v1.y) - (V2.x, V2.y) = (V1.x - V2.x, V1.y - V2.y)

    ' V1  - input : Vector 1
    ' V2  - input : Vector 2
    ' Vout - output: the new vector

    Vout.x = V1.x - V2.x ' x value of vector 2 gets subtracted from x value of vector 1
    Vout.y = V1.y - V2.y ' y value of vector 2 gets subtracted from y value of vector 1

END SUB


SUB ScalarMultiplyVector (V AS Type_Vector2, Scalar AS SINGLE, Vout AS Type_Vector2)

    ' "Scaling the vector"

    '          
    ' Formula: V * Scalar = (Vx, Vy) * Scalar = (Vx * Scalar, Vy * Scalar)

    ' V      - input : Vector
    ' Scalar - input : scalar multiplication value
    ' Vout  - output: the new vector

    Vout.x = V.x * Scalar ' x value of vector gets multiplied by scalar
    Vout.y = V.y * Scalar ' y value of vector gets multiplied by scalar

END SUB


FUNCTION Dot (V1 AS Type_Vector2, V2 AS Type_Vector2)

    ' Dot product of vectors
    '          -  -
    ' Formula: V1 ù V2 = (V1.x, V1.y) ù (V2.x, V2.y) = (V1.x * v2.x) + (V1.y * V2.y)

    Dot = V1.x * V2.x + V1.y * V2.y ' multiply vector x values then add multiplied vector y values

END FUNCTION


FUNCTION CrossProductVectors (V1 AS Type_Vector2, V2 AS Type_Vector2)

    ' Also known as a "Wedge Product" or "Perp Product" for 2D vectors

    '          -  -                                ³  x  y  ³
    ' Formula: V1 * V2 = (V1.x, V1.y) * (V2.x, V2.y) = ³V1.x V1.y³ = (V1.x * V2.y) - (V1.y * V2.x)
    '                                                  ³V2.x V2.y³

    CrossProductVectors = V1.x * V2.y - V1.y * V2.x

END FUNCTION


FUNCTION VectorLength (V AS Type_Vector2)

    '                  _______________________
    ' Formula: º V º = û V.x * V.x + V.y * V.y

    VectorLength = _HYPOT(V.x, V.y)

END FUNCTION


SUB Normalize (v AS Type_Vector2)

    ' Also known as a unit vector

    '                                  _______________________
    ' Formula: V / º V º = (V1.x, V1.y) / û V.x * V.x + V.y * v.y

    DIM VecLength AS SINGLE

    VecLength = _HYPOT(v.x, v.y) ' length of vector
    v.x = v.x / VecLength '        normalized x length
    v.y = v.y / VecLength '        normalized y length

END SUB


SUB Rotate (vec AS Type_Vector2, angleDeg AS SINGLE, origin AS Type_Vector2)

    ' Rotate a point around an origin using linear transformations.
    '
    '                                                       Rotating from (x,y) to (x',y')          |
    ' |                    (x',y')                            : L = R cosé                          | All of this shows how to get to this
    ' |                      ù                              : A = x'                              |                          -----------
    ' |                      /.\                              : B = L cosè = R cosè cosé = x cosé    |                                |
    ' |                    / .è\                            : (note - * opposite angles are equal) |                                |
    ' |                    /  .  \                            : C = R siné                          |                          +----+
    ' |                  /  .  \                          : D = C sinè = R sinè siné = y siné    |                          |
    ' |                  /    .    \                          : Y = R sinè                          |                          |
    ' |                /    .    \C                        : X = r cosè                          |                          
    ' |                /      .      \                                    __                          |                  -----------------
    ' |              /      .      \  L stops            : x' = B - |AB| = X cosé - Y siné      |                          
    ' |              /        .        \  here
    ' |            /        .        \  |                All of this just to show how to get from x to x' using      (X cosé - Y siné)
    ' |            /          .          \  |                Use the same linear transformation methods to get y' using  (X siné + Y cosé)
    ' |          R/          .          \ |
    ' |          /            .¿    D      \                Change the origin point of all rotations to (0,0) by subtracting the current
    ' |        /            .------------âù_--ù (x,y)      origin point from the current vector length. Add it back when rotation is
    ' |        /              .        __--.  .            completed.
    ' |      /              . *  __--    .  .
    ' |      /                . __--        .  .
    ' |    /            L __--            .  .
    ' |    /            __--  .            .  .Y
    ' |  /        __--    * .            .  .
    ' |  /      __--          .            .  .
    ' | / é __--              .            .  .
    ' |/__-- è              â.            .  .
    ' ù-----------------------ù-------------ù---ù------------
    '                        A            B
    ' |------------------- X -------------------|

    DIM x AS SINGLE
    DIM y AS SINGLE
    DIM __cos AS SINGLE
    DIM __sin AS SINGLE
    DIM xPrime AS SINGLE
    DIM yPrime AS SINGLE

    x = vec.x - origin.x '                move rotation vector origin to 0
    y = vec.y - origin.y
    __cos = COS(_D2R(angleDeg)) '        get cosine and sine of angle
    __sin = SIN(_D2R(angleDeg))
    xPrime = (x * __cos) - (y * __sin) '  calculate rotated location of vector
    yPrime = (x * __sin) + (y * __cos)
    xPrime = xPrime + origin.x '          move back to original origin
    yPrime = yPrime + origin.y
    vec.x = xPrime '                      pass back rotated vector
    vec.y = yPrime

END SUB


FUNCTION compareXYEpsilon (x AS SINGLE, y AS SINGLE, epsilon AS SINGLE)

    compareXYEpsilon = 0
    IF ABS(x - y) <= epsilon * MathMax(1, MathMax(ABS(x), ABS(y))) THEN compareXYEpsilon = -1

END FUNCTION

FUNCTION compareVecEpsilon (vec1 AS Type_Vector2, vec2 AS Type_Vector2, epsilon AS SINGLE)

    compareVecEpsilon = 0
    IF compareXYEpsilon(vec1.x, vec2.x, epsilon) AND compareXYEpsilon(vec1.y, vec2.y, epsilon) THEN compareVecEpsilon = -1

END FUNCTION

FUNCTION compareXY (x AS SINGLE, y AS SINGLE)

    compareXY = 0
    IF ABS(x - y) <= MIN_VALUE * MathMax(1, MathMax(ABS(x), ABS(y))) THEN compareXY = -1

END FUNCTION

FUNCTION compareVec (vec1 AS Type_Vector2, vec2 AS Type_Vector2)

    compareVec = 0
    IF compareXY(vec1.x, vec2.x) AND compareXY(vec1.y, vec2.y) THEN compareVec = -1

END FUNCTION


FUNCTION lengthSquared (length AS Type_Vector2)

    lengthSquared = length.x * length.x + length.y + length.y

END FUNCTION




FUNCTION MathMax (num1 AS SINGLE, num2 AS SINGLE)

    IF num1 >= num2 THEN MathMax = num1 ELSE MathMax = num2

END FUNCTION


FUNCTION MathMin (num1 AS SINGLE, num2 AS SINGLE)

    IF num1 <= num2 THEN MathMin = num1 ELSE MathMin = num2

END FUNCTION






RE: 2D Physics Engine Help - a740g - 06-12-2023

I think mechatronic3000 ported Randy Gaul's Cute Physics 2D to QB64 a long time ago: 2D Physics Engine (alephc.xyz)

The updated version of that is here:  QBPool/QBPOOL/fzxNGN/physics at main · mechatronic3000/QBPool · GitHub

Box2D will require a considerable effort to port. A QB64-PE wrapper may be possible. I'll need to check.

Update: Randy's latest C++ library is here: RandyGaul/ImpulseEngine: Simple, open source, 2D impulse based physics engine for educational use. (github.com). It's called Impulse Engine and not Cute Physics like I noted above.


RE: 2D Physics Engine Help - madscijr - 06-12-2023

(06-12-2023, 03:16 PM)TerryRitchie Wrote: I figured the process of converting the Java OOP code to functions and procedures would be fairly straight forward ... not so much.
Yikes! I would recommend sticking with procedural code, if you can find it. Regular C is procedural, and would surely be easier to translate to procedural QB64PE.

(06-12-2023, 03:16 PM)TerryRitchie Wrote: My next thought was to incorporate the Box2D physics engine (the engine that Rovio used to create Angry Birds) into QB64. The Library is written in C++ and I figured by using DECLARE LIBRARY I could get this done.
https://box2d.org/about/
Spriggsy is the guy you'll want to talk to about using external libraries or APIs within QB64/PE. However, I think dependencies on libraries outside of the native OS would complicate things...

Good Luck!


RE: 2D Physics Engine Help - Ultraman - 06-12-2023

It all depends on how easy it is to incorporate and how easy it is to map out. I don't know anything about physics engines so I wouldn't even know if I'd be doing it right even if I managed to get it to compile.


RE: 2D Physics Engine Help - a740g - 06-12-2023

(06-12-2023, 04:11 PM)madscijr Wrote:
(06-12-2023, 03:16 PM)TerryRitchie Wrote: I figured the process of converting the Java OOP code to functions and procedures would be fairly straight forward ... not so much.
Yikes! I would recommend sticking with procedural code, if you can find it. Regular C is procedural, and would surely be easier to translate to procedural QB64PE.
Yeah. Box2D is C++. So, it might be problematic to make a binding for QB64-PE.

There is Chipmunk2D that is a plain ANSI C 2D physics library.
Then there is also the tiny single header Physac that I am planning to write a QB64-PE binding for as part of my larger raylib binding library.


RE: 2D Physics Engine Help - justsomeguy - 06-12-2023

I'm mechatronic3000.

I have been working on my physics engine (its 100% written in QB64) to make it more generalized and refactoring large portions of it to be easier to use. Its far from being feature complete.
  • It currently has only two primitives, rectangles and circles. Concave objects are not supported yet.
  • It has joints and you chain them together.

I've attached a simple example program.

Forgive my poor example, It was the only one I had to test the latest version of the refactor.

I'm using OpenGL in this example, but it can easily use native QB64 commands if you wish.

If you guys want I can post more about it.



[Image: screenshot.jpg]


RE: 2D Physics Engine Help - justsomeguy - 06-12-2023

(06-12-2023, 09:34 PM)justsomeguy Wrote: I'm mechatronic3000.

I have been working on my physics engine (its 100% written in QB64) to make it more generalized and refactoring large portions of it to be easier to use. Its far from being feature complete.
  • It currently has only two primitives, rectangles and circles. Concave objects are not supported yet.
  • It has joints and you chain them together.

I've attached a simple example program.

Forgive my poor example, It was the only one I had to test the latest version of the refactor.

I'm using OpenGL in this example, but it can easily use native QB64 commands if you wish.

If you guys want I can post more about it.



[Image: screenshot.jpg]
Sorry, I have yet to update my GITHUB. I will as soon as I get it in better shape.


RE: 2D Physics Engine Help - Kernelpanic - 06-12-2023

Quote:@Terry -  The Library is written in C++ and I figured by using DECLARE LIBRARY I could get this done. However, my C++ knowledge is lacking as well. Trying to figure out where pointers are used versus variables, . . .
@Terry - just in case you want to try something like this, pointers are created like this in C/C++:

int *zgr, **zzgr; (**zzgr is a pointer to a pointer. Yes, you can do that in C, ad nauseam.)

Here is an old example program that flips a number and a character array. - With zzgr = &zgr the pointer-to-pointer is assigned the address of zgr. The pointer then moves backwards through the field (zgr--), and **zzgr displays it. Hope that's halfway understandable.

Code: (Select All)
#include <stdio.h>
#include <stdlib.h>

#define MAX 9

int main(void)
{
  int feld[] = { 1,2,3,4,5,6,7,8,9 };
  char char_feld[] = { 'a','b','c','d','e','f','g','h','z' };
  int i, *zgr,  **zzgr;
  char *char_zgr, **cchar_zgr;

  for ( i = 0; i < MAX; i++ )
  {
      zgr = &feld[i];
      printf("%d ", *zgr);
  }
  printf("\t");
    
  for ( i = 0; i < MAX; i++ )
  {
      char_zgr = &char_feld[i];
      printf("%c ", *char_zgr);
  } 
    printf("\n\n");
    
    //Mit zzgr = &zgr wird dem Zeiger-auf-Zeiger die Adresse
    //von zgr zugewiesen. Der Zeiger zgr geht dann rückwärtz
    //das Feld durch (zgr--), und **zzgr läßt es anzeigen.
  for ( i = 0;  i < MAX; i++ )
  {
      zzgr = &zgr;
      printf("%d ",**zzgr);
      zgr--;
  }
  printf("\t");
    
  for ( i = 0; i < MAX; i++ )
  {
      cchar_zgr = &char_zgr;
      printf("%c ", **cchar_zgr);
      char_zgr--;
  }
    return(0);
}

If you want to compile the program yourself, it says in the screenshot.

[Image: zgr-auf-zgr-2023-06-13.jpg]


RE: 2D Physics Engine Help - Sprezzo - 06-13-2023

i vouch for justsomeguy. he's definitely your man terry


RE: 2D Physics Engine Help - justsomeguy - 06-15-2023

I was looking over your code and WOW you put a lot of effort in your comments with ASCII art and everything. Good job! I wish I had the patience to do that. I will try to improve my source with more commenting.

If you still want to more or less roll your own physics engine, another resource I rolled across is https://github.com/matthias-research/pages/tree/master/tenMinutePhysics
His code is written in fair portable javascript. He has a Youtube channel as well, and can help explain the source.

Let me know if I can help you.