Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
3D Bouncing Balls
#1
This program simulates coloured balls moving and colliding inside a (transparent) box.

   

The 3D full perspective visualisation is enhanced by making the actual balls 3D shapes.  A 2D ball image is mapped via _MAPTRIANGLE to an actual spherical shape.  You will notice that this method has the effect of distorting the ball shape, especially at distances from the centre of the screen.  This arises because of the vanishing-point perspective used in _MAPTIANGLE(3D).

This old program from QB64.org has similarities to Rotating Cylinders in 3D Space Rotating Cylinders in 3D Space, and is the basis for the Quartet of _MAPTRIANGLE(3D) programs in this Section.





Code: (Select All)
' 3D Bouncing Balls Using _MAPTRIANGLE by Magdha 2025-11-26 ex Qwerkey
' Cyperium method for sphere image

CONST False = 0, True = NOT False
_TITLE "3D Bouncing Balls - Esc to Quit"
RANDOMIZE (TIMER)

CONST NoBalls%% = 10, Uscreen% = 1000, Vscreen% = 800, NoPhis%% = 16, NoAlphas%% = 6, XBright! = 0.5
CONST Rad1%% = 25, PerspDist% = 550, DeltaSepn! = 0.25 'Small offset to stop balls sticking
CONST Alpha! = _PI / (2 * NoAlphas%%), Phi! = 2 * _PI / NoPhis%%
DIM BallStats!(NoBalls%% - 1, 9), Cyperium&(NoBalls%% - 1)
DIM MapConv%(NoPhis%%, NoAlphas%%, 1, 2)

'Conversion from 2D
FOR N%% = 0 TO NoPhis%%
    FOR M%% = 0 TO NoAlphas%%
        MapConv%(N%%, M%%, 0, 0) = 128 + CINT(127 * SIN((_PI / 2) - M%% * Alpha!) * COS(N%% * Phi!))
        MapConv%(N%%, M%%, 0, 1) = 128 + CINT(127 * SIN((_PI / 2) - M%% * Alpha!) * SIN(N%% * Phi!))
        MapConv%(N%%, M%%, 1, 0) = CINT(Rad1%% * SIN((_PI / 2) - M%% * Alpha!) * COS(N%% * Phi!))
        MapConv%(N%%, M%%, 1, 1) = CINT(Rad1%% * SIN((_PI / 2) - M%% * Alpha!) * SIN(N%% * Phi!))
        MapConv%(N%%, M%%, 1, 2) = CINT(Rad1%% * COS((_PI / 2) - M%% * Alpha!))
    NEXT M%%
NEXT N%%

'Screen Background
TempImage& = _NEWIMAGE(Uscreen%, Vscreen%, 32)
_DEST TempImage&
COLOR _RGB(255, 255, 255), _RGB(10, 60, 60)
CLS
FOR UCol% = 0 TO Uscreen% - 1
    FOR VCol% = 0 TO Vscreen% - 1
        PSET (UCol%, VCol%), _RGB(100, INT(180 * UCol% / Uscreen%), INT(180 * VCol% / Vscreen%))
    NEXT VCol%
NEXT UCol%
Background& = MakeHardware&(TempImage&)

' Define characteristics of balls
DATA 255,0,0
DATA 219,80,0
DATA 150,100,0
DATA 138,117,0
DATA 80,155,20
DATA 0,255,0
DATA 20,135,100
DATA 0,105,150
DATA 0,60,150
DATA 0,0,255
FOR N%% = 0 TO NoBalls%% - 1
    'Ball mass
    BallStats!(N%%, 0) = 0.1 + (0.8 * N%% / (NoBalls%% - 1))
    IF BallStats!(N%%, 0) = 0 THEN BallStats!(N%%, 0) = 0.000001
    'Ball colours (temporarily use (N%%,1,2,3)
    FOR P%% = 1 TO 3
        READ BallStats!(N%%, P%%)
    NEXT P%%
    TempImage& = _NEWIMAGE(256, 256, 32)
    _DEST TempImage&
    COLOR _RGB(BallStats!(N%%, 1), BallStats!(N%%, 2), BallStats!(N%%, 3)), _RGBA(0, 0, 0, 0)
    'Image data goes from 1 to 255 (not 0 to 255)
    FOR Z% = 128 TO 255
        FOR X% = 1 TO 255
            FOR Y% = 1 TO 255
                DeltaX% = X% - 127
                DeltaY% = Y% - 127
                DeltaZ% = Z% - 127
                Dist! = SQR((DeltaX% * DeltaX%) + (DeltaY% * DeltaY%) + (DeltaZ% * DeltaZ%))
                IF Dist! > 125 AND Dist! < 127 THEN PSET (X%, Y%), _RGB(CINT(Z% * BallStats!(N%%, 1) * (1 - (XBright! * X% / 255)) / 255), CINT(Z% * BallStats!(N%%, 2) * (1 - (XBright! * X% / 255)) / 255), CINT(Z% * BallStats!(N%%, 3) * (1 - (XBright! * X% / 255)) / 255))
            NEXT
        NEXT
    NEXT
    Cyperium&(N%%) = MakeHardware&(TempImage&)
    'Ball positions/velocities
    FOR P%% = 4 TO 6
        BallStats!(N%%, P%%) = (0.5 - RND) * 1000
    NEXT P%%
    FOR P%% = 7 TO 9
        Velocity! = 0.5 - RND
        IF Velocity! < 0 THEN
            Velocity! = Velocity! - 0.5
        ELSE
            Velocity! = Velocity! + 0.5
        END IF
        BallStats!(N%%, P%%) = Velocity! * 2.5
    NEXT P%%
NEXT N%%

'Create screen
SCREEN _NEWIMAGE(Uscreen%, Vscreen%, 32)
_SCREENMOVE 50, 13
_DEST 0
_DISPLAYORDER _HARDWARE 'Just the hardware images.

'Initialise axes and rotations
ZRot! = (RND - 0.5) * 0.0011: YRot! = (RND - 0.5) * 0.0011: XRot! = (RND - 0.5) * 0.0011
ZTime% = 15 + INT(47 * RND): YTime%% = 15 + INT(47 * RND): XTime% = 15 + INT(47 * RND)
ZStart! = TIMER: YStart! = TIMER: XStart! = TIMER
Zaxis! = 0: Yaxis! = 0: Xaxis! = 0
Stereo%% = True

WHILE Stereo%%

    _LIMIT 120 'Game Frames/Second Rate
    _PUTIMAGE (0, 0), Background&

    'Calculate moves
    FOR N%% = 0 TO NoBalls%% - 1
        FOR P%% = 1 TO 3
            BallStats!(N%%, 3 + P%%) = BallStats!(N%%, 3 + P%%) + BallStats!(N%%, 6 + P%%)
        NEXT P%%
    NEXT N%%

    'Check for collsions
    FOR N%% = 0 TO NoBalls%% - 2
        FOR M%% = N%% + 1 TO NoBalls%% - 1
            SepnSqd! = (BallStats!(N%%, 4) - BallStats!(M%%, 4)) * (BallStats!(N%%, 4) - BallStats!(M%%, 4)) + (BallStats!(N%%, 5) - BallStats!(M%%, 5)) * (BallStats!(N%%, 5) - BallStats!(M%%, 5)) + (BallStats!(N%%, 6) - BallStats!(M%%, 6)) * (BallStats!(N%%, 6) - BallStats!(M%%, 6))
            IF SepnSqd! <= 4 * Rad1%% * Rad1%% THEN
                ' Set new velocities
                Multiplier! = 0: NDist! = 0: MDist! = 0
                FOR P%% = 1 TO 3
                    Multiplier! = Multiplier! + (BallStats!(N%%, 6 + P%%) - BallStats!(M%%, 6 + P%%)) * (BallStats!(N%%, 3 + P%%) - BallStats!(M%%, 3 + P%%))
                    NDist! = NDist! + BallStats!(N%%, 3 + P%%) * BallStats!(N%%, 3 + P%%)
                    MDist! = MDist! + BallStats!(M%%, 3 + P%%) * BallStats!(M%%, 3 + P%%)
                NEXT P%%
                NDist! = SQR(NDist!)
                MDist! = SQR(MDist!)
                Multiplier! = 2 * Multiplier! / (SepnSqd! * (BallStats!(N%%, 0) + BallStats!(M%%, 0)))
                FOR P%% = 1 TO 3
                    SepnVect! = BallStats!(N%%, 3 + P%%) - BallStats!(M%%, 3 + P%%)
                    BallStats!(N%%, 6 + P%%) = BallStats!(N%%, 6 + P%%) - (Multiplier! * BallStats!(M%%, 0) * SepnVect!)
                    BallStats!(M%%, 6 + P%%) = BallStats!(M%%, 6 + P%%) + (Multiplier! * BallStats!(N%%, 0) * SepnVect!)
                    IF NDist! > MDist! THEN
                        BallStats!(M%%, 3 + P%%) = BallStats!(M%%, 3 + P%%) - (DeltaSepn! * (BallStats!(N%%, 3 + P%%) - BallStats!(M%%, 3 + P%%)) / SQR(SepnSqd!))
                    ELSE
                        BallStats!(N%%, 3 + P%%) = BallStats!(N%%, 3 + P%%) + (DeltaSepn! * (BallStats!(N%%, 3 + P%%) - BallStats!(M%%, 3 + P%%)) / SQR(SepnSqd!))
                    END IF
                NEXT P%%
            END IF
        NEXT M%%
    NEXT N%%

    'Look for reflection off sides
    FOR N%% = 0 TO NoBalls%% - 1
        FOR P%% = 1 TO 3
            IF BallStats!(N%%, 3 + P%%) > 500 - Rad1%% THEN
                BallStats!(N%%, 6 + P%%) = -BallStats!(N%%, 6 + P%%)
                IF BallStats!(N%%, 3 + P%%) + BallStats!(N%%, 6 + P%%) > 500 - Rad1%% THEN
                    BallStats!(N%%, 3 + P%%) = BallStats!(N%%, 3 + P%%) - DeltaSepn!
                END IF
            ELSEIF BallStats!(N%%, 3 + P%%) < -500 + Rad1%% THEN
                BallStats!(N%%, 6 + P%%) = -BallStats!(N%%, 6 + P%%)
                IF BallStats!(N%%, 3 + P%%) + BallStats!(N%%, 6 + P%%) < -500 + Rad1%% THEN
                    BallStats!(N%%, 3 + P%%) = BallStats!(N%%, 3 + P%%) + DeltaSepn!
                END IF
            END IF
        NEXT P%%
    NEXT N%%

    'Adjust for angle of rotation
    FOR N%% = 0 TO NoBalls%% - 1
        R2! = SQR(BallStats!(N%%, 4) * BallStats!(N%%, 4) + BallStats!(N%%, 5) * BallStats!(N%%, 5) + BallStats!(N%%, 6) * BallStats!(N%%, 6))
        Theta2! = _ACOS(BallStats!(N%%, 6) / R2!)
        Phi2! = _ATAN2(BallStats!(N%%, 5), BallStats!(N%%, 4)) + Zaxis!
        BallStats!(N%%, 1) = R2! * SIN(Theta2!) * COS(Phi2!)
        BallStats!(N%%, 2) = R2! * SIN(Theta2!) * SIN(Phi2!)
        BallStats!(N%%, 3) = R2! * COS(Theta2!)
        R2! = SQR(BallStats!(N%%, 1) * BallStats!(N%%, 1) + BallStats!(N%%, 2) * BallStats!(N%%, 2) + BallStats!(N%%, 3) * BallStats!(N%%, 3))
        Theta2! = _ACOS(BallStats!(N%%, 1) / R2!)
        Phi2! = _ATAN2(BallStats!(N%%, 3), BallStats!(N%%, 2)) + Xaxis!
        BallStats!(N%%, 2) = R2! * SIN(Theta2!) * COS(Phi2!)
        BallStats!(N%%, 3) = R2! * SIN(Theta2!) * SIN(Phi2!)
        BallStats!(N%%, 1) = R2! * COS(Theta2!)
        R2! = SQR(BallStats!(N%%, 1) * BallStats!(N%%, 1) + BallStats!(N%%, 2) * BallStats!(N%%, 2) + BallStats!(N%%, 3) * BallStats!(N%%, 3))
        Theta2! = _ACOS(BallStats!(N%%, 2) / R2!)
        Phi2! = _ATAN2(BallStats!(N%%, 1), BallStats!(N%%, 3)) + Yaxis!
        BallStats!(N%%, 3) = CINT(R2! * SIN(Theta2!) * COS(Phi2!))
        BallStats!(N%%, 1) = CINT(R2! * SIN(Theta2!) * SIN(Phi2!))
        BallStats!(N%%, 2) = CINT(R2! * COS(Theta2!))
    NEXT N%%

    'Display balls
    FOR P%% = 0 TO NoBalls%% - 1
        'Ball views done with _MAPTRIANGLE 3D
        FOR N%% = 0 TO NoPhis%% - 1
            FOR M%% = 0 TO NoAlphas%% - 2
                Ax% = MapConv%(N%%, M%%, 1, 0) + BallStats!(P%%, 1)
                Ay% = MapConv%(N%%, M%%, 1, 1) + BallStats!(P%%, 2)
                Az% = MapConv%(N%%, M%%, 1, 2) + BallStats!(P%%, 3) - PerspDist%
                Bx% = MapConv%(N%%, M%% + 1, 1, 0) + BallStats!(P%%, 1)
                By% = MapConv%(N%%, M%% + 1, 1, 1) + BallStats!(P%%, 2)
                Bz% = MapConv%(N%%, M%% + 1, 1, 2) + BallStats!(P%%, 3) - PerspDist%
                Cx% = MapConv%(N%% + 1, M%% + 1, 1, 0) + BallStats!(P%%, 1)
                Cy% = MapConv%(N%% + 1, M%% + 1, 1, 1) + BallStats!(P%%, 2)
                Cz% = MapConv%(N%% + 1, M%% + 1, 1, 2) + BallStats!(P%%, 3) - PerspDist%
                _MAPTRIANGLE (MapConv%(N%%, M%%, 0, 0), MapConv%(N%%, M%%, 0, 1))-(MapConv%(N%%, M%% + 1, 0, 0), MapConv%(N%%, M%% + 1, 0, 1))-(MapConv%(N%% + 1, M%% + 1, 0, 0), MapConv%(N%% + 1, M%% + 1, 0, 1)), Cyperium&(P%%) TO(Ax%, Ay%, Az%)-(Bx%, By%, Bz%)-(Cx%, Cy%, Cz%)
                _MAPTRIANGLE (MapConv%(N%% + 1, M%% + 1, 0, 0), MapConv%(N%% + 1, M%% + 1, 0, 1))-(MapConv%(N%% + 1, M%%, 0, 0), MapConv%(N%% + 1, M%%, 0, 1))-(MapConv%(N%%, M%%, 0, 0), MapConv%(N%%, M%%, 0, 1)), Cyperium&(P%%) TO(Cx%, Cy%, Cz%)-(MapConv%(N%% + 1, M%%, 1, 0) + BallStats!(P%%, 1), MapConv%(N%% + 1, M%%, 1, 1) + BallStats!(P%%, 2), MapConv%(N%% + 1, M%%, 1, 2) + BallStats!(P%%, 3) - PerspDist%)-(Ax%, Ay%, Az%)
            NEXT M%%
            _MAPTRIANGLE (MapConv%(N%%, NoAlphas%% - 1, 0, 0), MapConv%(N%%, NoAlphas%% - 1, 0, 1))-(128, 128)-(MapConv%(N%% + 1, NoAlphas%% - 1, 0, 0), MapConv%(N%% + 1, NoAlphas%% - 1, 0, 1)), Cyperium&(P%%) TO(Bx%, By%, Bz%)-(BallStats!(P%%, 1), BallStats!(P%%, 2), BallStats!(P%%, 3) + Rad1%% - PerspDist%)-(Cx%, Cy%, Cz%)
        NEXT N%%
    NEXT P%%

    'rotate around z- axis
    Zaxis! = Zaxis! + ZRot!
    IF Zaxis! > _PI THEN
        Zaxis! = Zaxis! - 2 * _PI
    ELSEIF Zaxis! < -_PI THEN
        Zaxis! = Zaxis! + 2 * _PI
    END IF

    'rotate around y- axis
    Yaxis! = Yaxis! + YRot!
    IF Yaxis! > _PI THEN
        Yaxis! = Yaxis! - 2 * _PI
    ELSEIF Yaxis! < -_PI THEN
        Yaxis! = Yaxis! + 2 * _PI
    END IF

    'rotate around x- axis
    Xaxis! = Xaxis! + XRot!
    IF Xaxis! > _PI THEN
        Xaxis! = Xaxis! - 2 * _PI
    ELSEIF Xaxis! < -_PI THEN
        Xaxis! = Xaxis! + 2 * _PI
    END IF

    IF TIMER > ZStart! + ZTime% THEN
        ZRot! = (RND - 0.5) * 0.0011
        ZTime% = 15 + INT(47 * RND)
        ZStart! = TIMER
        RANDOMIZE (ZStart!)
    END IF
    IF TIMER > YStart! + YTime%% THEN
        YRot! = (RND - 0.5) * 0.0011
        YTime%% = 15 + INT(47 * RND)
        YStart! = TIMER
    END IF
    IF TIMER > XStart! + XTime% THEN
        XRot! = (RND - 0.5) * 0.0011
        XTime% = 15 + INT(47 * RND)
        XStart! = TIMER
    END IF

    _DISPLAY
    IF _KEYHIT = 27 THEN Stereo%% = False 'Esc  to quit

WEND

SYSTEM

FUNCTION MakeHardware& (Imagename&)
    MakeHardware& = _COPYIMAGE(Imagename&, 33)
    _FREEIMAGE Imagename&
END FUNCTION
Reply
#2
Nice example!  I tried to do something like that once but never got there.

You're posting some great stuff!

- Dav

Find my programs here in Dav's QB64 Corner
Reply
#3
Thanks Dave.

I'm foisting all my half-decent old Qwerkey stuff onto PE.

I think that I've more-or-less cornered the market in _MAPTRIANGLE(3D).  I keep promising Unseen and the OpenGL guys that I'll try and learn a bit of GL.  But I don't think that that's ever going to happen.

Richard
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)