' 
' 
'3D pong Demo with Grateful Dead inspiration 
'  Some minor code sections by Basic4GL 
' JohnK 
' 

'=====================  multithread test section ======== 
  DECLARE SUB CalcPositions
'========================================================== 



DECLARE SUB ChromeWrapThem  
DECLARE SUB CloseApp
DECLARE SUB DXInit(Sender AS QDXScreen)
DECLARE SUB DXInitSurface(Sender AS QDXScreen)
DECLARE SUB DXscreen_MouseMove (MousX AS INTEGER, MousY AS INTEGER)
DECLARE SUB Form_Key_Press(TheKey AS WORD, Shift AS INTEGER)
DECLARE SUB Make3DObjects
DECLARE SUB MakeParticles
DECLARE SUB MoveParticles
DECLARE SUB MoveThem
DECLARE SUB New_Psychedelic_Background
DECLARE SUB RenderLoop
DECLARE SUB Reset_New_Ball
DECLARE SUB RotateCamera
DECLARE SUB WrapObjects
DECLARE SUB intro
DECLARE SUB scoreboard
DECLARE SUB updateFPS


$TYPECHECK ON                   'force checking of data types 
$INCLUDE <RAPIDQ2.INC>          'objects and constants 
$INCLUDE <RapidQ_D3D.INC>       'constants ++ 
$DEFINE DXWidth 640             'set display window for DxScreen 
$DEFINE DXHeight 480
$DEFINE CourtX 20
$DEFINE CourtZ 10
$DEFINE Ceiling 12
$DEFINE PaddleWidth 3
$DEFINE PaddleThick 1
$DEFINE PaddleHeight 2
$DEFINE BallSize 0.8
$DEFINE MaxSpeed 3.5
$DEFINE pSpeed 2.5
$DEFINE InterPlayDelay 0.5  'delay between serves 
$DEFINE NumParticles 20
CONST tmpBMPFile = "~~3DPongadelic.bmp"


DIM x AS SINGLE:            x=RND(1)* CourtX    'randomize the starting point of the ball x position 
DIM y AS SINGLE:            y=RND(1)* Ceiling   'randomize the starting point of the ball y position 
DIM z AS SINGLE:            z=RND(1)* CourtZ    'randomize the starting point of the ball Height 
DIM bool_x:                 bool_x = true   'Test to See if the Ball Needs to go Left or Right 
DIM bool_y:                 bool_x = true   'Test to See if the Ball Needs to go Up or Down 
DIM bool_z:                 bool_z = true   'Test to See if the Ball Needs to go Cross court 
DIM player1_y AS SINGLE:    player1_y = 0   'Player 1 height position 
DIM player2_y AS SINGLE:    player2_y = 0   'Player 2 height position 
DIM player1_z AS SINGLE:    player1_z = 0   'Player 1 lateral position 
DIM player2_z AS SINGLE:    player2_z = 0   'Player 2 lateral position 
DIM p1_score:               p1_score = 0    'Player 1 score 
DIM p2_score:               p2_score = 0    'Player 2 Score 
DIM FailRate AS SINGLE:     FailRate =30.0  'Player 2(computer) fails about 30% 
DIM FPS AS INTEGER:         FPS = 30        'frames per second 
DIM TheSpeed AS SINGLE:     TheSpeed = 0.2  'arbitrary speed for now 
DIM RotateMe AS INTEGER:    RotateMe = True 'rotate world 
DIM RelX AS SINGLE:         RelX = 0        'camera rotation 
DIM RelY AS SINGLE:         RelY = 0        'camera rotation 
DIM GameEndFlag AS INTEGER: GameEndFlag = False


DIM WorldFrame          AS QD3DFrame        'Master (parent) frame all 3d objects moving together attached to this frame 
DIM AmbientLight        AS QD3DLight        'ambient DXscreen light.. make it dark for dark, daylight handles daytime!! 
DIM DayLight            AS QD3DLight        'set the properties of the light, this is for ambient daylight 
DIM DayLightFrame       AS QD3DFrame        'allow position & orientation of light 
DIM Cam                 AS QD3DCAMERA       'custom camera component 
DIM Paddle1             AS QD3DPrimitive    'our player 1 paddle 
DIM Paddle2             AS QD3DPrimitive    'our player 1 paddle 
DIM TheBall             AS QD3DPrimitive    'Boing ball 
DIM FloorObj            AS QD3DPrimitive    'Floor is a reserved keyword 
DIM SkyBox              AS QD3DPrimitive    'box around the world frame, texture surround scene on it 
DIM wrap                AS QD3DWRAP         'object to hold texture wrapping information 
DIM Particles1          AS QD3DCloneMesh    'A clone mesh is a single mesh on many QD3DFrames 
DIM Particles2          AS QD3DCloneMesh    'A clone mesh is a single mesh on many QD3DFrames 

CREATE MainForm AS QForm
    Caption = "3D Pong"
    BorderStyle =   bsNone
    Width = DXWidth
    Height = DXHeight
    Center
    OnKeyDown = Form_Key_Press
    OnClose = CloseApp

    CREATE DXScreen AS QDXScreen            'really a direct draw surface? 
        Top = 0
        Left = 0
        Width = DXWidth
        Height = DXHeight
        Cursor = crNone
        FullScreen = True
        Init(DXWidth, DXHeight)
        BitCount = Screen.GetPixelDepth         'get whatever mode we are in 
        Use3D = True                            'load Direct3D Retained mode 
        UseHardware = True                      '3D accelerated video cards are cheap, get one!! 
        OnInitialize = DXInit                   'load your meshs/faces to the frames, set lights, camera 
        OnInitializeSurface = DXInitSurface     'screen is ready to be drawn 
        OnMouseMove =   DXscreen_MouseMove      'pass mouse (X%, Y%)  
        OnKeyDown = Form_Key_Press
    END CREATE
END CREATE

'DIM DXTimer AS QDXTimer                            'regularly update display 
'    DXTimer.Enabled = False 
'    DXTimer.Interval = 50 
'    DXTimer.OnTimer = updateFPS 





SUB updateFPS
'   FPS = DXTimer.FrameRate 
'   DXScreen.TextOut(500, 32, "FPS = "+STR$(DXTimer.FrameRate) + "     ",&HFFFFFF, 0)' Print Player 2 Score 
'   DXScreen.ForceUpdate(500, 32, 540, 64) 
'   DXScreen.Flip 
END SUB


'******************************************************* 
'******************************************************* 
'****     INITIALIZATION CODE FOR DirectX Screen ******* 
'******************************************************* 
'******************************************************* 

SUB DXInitSurface(Sender AS QDXScreen)
    DxScreen.setRenderMode(D3DRMRENDERMODE_BLENDEDTRANSPARENCY OR D3DRMRENDERMODE_SORTEDTRANSPARENCY)
    Intro
    RenderLoop
    CloseApp
END SUB



SUB DXInit(Sender AS QDXScreen)

    DXScreen.CreateFrame(WorldFrame)    'create the terrain frame, all other frames will be attached to it in heirarchy 
    DXScreen.CreateFrame(DayLightFrame) 'probably allocates pointer to COM interface, memory, handles, etc 

'       *********  First we create the lights  ****************** 
    DXScreen.CreateLightRGB(D3DRMLIGHT_AMBIENT, 0.6, 0.6, 0.6, AmbientLight)    'ambient is dark for baseline darkness!! 
    DXScreen.AddLight(AmbientLight)                                             'this light moves with root frame 
    DXScreen.CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, 0.9, 0.9, 0.9, DayLight)    'point light 
    DayLightFrame.AddLight(DayLight)                                            'to a frame so we can remove from scene 
    DayLightFrame.SetPosition(0, 8, 0)                                         'x,y,z position, some arbitary value until load a terrain 
    DayLightFrame.SetOrientation(0, -1, 0, 0, 1, 0)                             'points straight down 
    DXScreen.SetTextureQuality(D3DRMTEXTURE_MIPLINEAR)      'gives a smooth appearance to textures 
    WorldFrame.SetPosition(0.0, 0.0, 0.0)           'centered because Heightmap is centered 
    Make3DObjects
'   DXScreen.SetCameraPosition(0.0, 2.5 * Ceiling, -4 * CourtZ) 
'   DXScreen.SetCameraOrientation(0.0,-0.5, 0.7, 0, 1, 0) 
    Cam.Pos.x = 0.0
    Cam.Pos.y = 2.5 * Ceiling
    Cam.Pos.z = -4.5 * CourtZ
    Cam.Orient.x = 0.0
    Cam.Orient.y = -0.5
    Cam.Orient.z = 0.7
    Cam.Update(DXscreen)
    DXScreen.Font.Size = 14
    DXScreen.Font.Bold = True
    MakeParticles
    New_Psychedelic_Background
    ChromeWrapThem
END SUB




SUB RenderLoop
  DO                    ' Main Game Loop 
    '----  move BALL and Paddles  ----------------- 

    TheBall.Frame.SetPosition(x, y, z)
    TheBall.Frame.Move(TheSpeed)                        'rotate it 
    Paddle1.Frame.SetPosition(-CourtX, player1_y, player1_z)
    'add some strategy to the computer 
    IF (x  > (CourtX/4)) THEN Paddle2.Frame.SetPosition(CourtX, y, z)   ' only hit near its side 
    IF RotateMe THEN RotateCamera
    ChromeWrapThem
    MoveParticles
    DXScreen.Render
    scoreboard
    DOEVENTS
    CalcPositions
  LOOP UNTIL GameEndFlag
END SUB


SUB CalcPositions
    'bounce if player 1 hits(Left Player)----------------------------------------------------------------- 
    IF x < (-CourtX + PaddleThick) and (x >-CourtX) THEN
        IF (z <=player1_z + PaddleWidth) AND (z >=player1_z - PaddleWidth) THEN
            IF (y <=player1_y + PaddleHeight) AND (y >=player1_y - PaddleHeight) THEN   bool_x = true
        END IF
    END IF

    IF x <= -CourtX then 
        p1_score = p1_score + 1   'If Ball Goes Past the Player then Score Goes 
        Reset_New_Ball           'put up a new ball 
'		New_Psychedelic_Background 
    END IF

    'IF 2 player game then bounce if player 2 hits(Right Player)---------------------------------------------------------------- 
'   Paddle2.Frame.SetPosition(CourtX, player2_y, player2_z) 
'   if x >= (CourtX + PaddleThick) and (x < CourtX) and _ 
'           (y <=player2_z + PaddleWidth) and (y >=player2_z -PaddleWidth) then bool_x = true 

    'did computer miss? 
    IF (x >= CourtX - PaddleThick) THEN
        IF RND(100) < FailRate THEN             'miss about 30% of the time 
            Paddle2.Frame.SetPosition(CourtX, y - PaddleHeight, z + PaddleWidth)' only hit it right 50% 
            p2_score = p2_score + 1     ' Up By One Point 
            Reset_New_Ball              'put up a new ball 
'		    New_Psychedelic_Background 
        ELSE
            bool_x = false              ' Served it back ok 
        END IF
    END IF

    if bool_x then x = x + TheSpeed  else x = x - TheSpeed  ' Bool(C++) Value Test Value 
    if z <=-CourtZ then bool_z=true         ' Bounce Ball off walls 
    if z >=CourtZ then bool_z=false 
    if bool_z then INC(z, TheSpeed) else DEC(z, TheSpeed)

    if y <=0 then bool_y=true           'bounce off floor 
    if y >= Ceiling then bool_y=false   'bounce off ceiling 
    if bool_y then INC(y,TheSpeed)  else DEC(y, TheSpeed)

END SUB



SUB Reset_New_Ball
    x = RND(1)* CourtX/3 * RND(1)
    y = RND(1) * Ceiling/3
    z = RND(1) * CourtZ/3 * RND(1)

    IF p1_score MOD 6 = 0 THEN
        DXscreen.VIEW.CLEAR
        intro
        DXscreen.VIEW.CLEAR
        IF TheSpeed < MaxSpeed THEN INC(TheSpeed, TheSpeed/6)
    END IF
END SUB




SUB Form_Key_Press(TheKey AS WORD, Shift AS INTEGER)
    SELECT CASE TheKey
    'Player One keyboard control (keeps in bounds)-------------------------------------------------------- 
    CASE = ASC("q")
        IF player1_z <CourtZ then player1_z =player1_z +TheSpeed
    CASE = ASC("a")
        IF player1_z >-CourtZ then player1_z =player1_z -TheSpeed
    'Player Two keyboard control (keeps in bounds)-------------------------------------------------------- 
    CASE = 38, 107      'up arrow and "+" 
        IF player2_z <2.4 then player2_z =player2_z +TheSpeed
    CASE = 40, 109          'dn arrow "-" 
        IF player2_z >-3 then player2_z =player2_z -TheSpeed
    CASE 82 'r 
        IF RotateMe THEN RotateMe = False ELSE RotateMe = True 
    CASE 27     'esc 
        GameEndFlag = True
    END SELECT
END SUB


SUB DXscreen_MouseMove (MousX AS INTEGER, MousY AS INTEGER)
    player1_z = CourtZ - (MouseX/DXScreen.Height) * CourtZ * 2
    player1_y = Ceiling - (MouseY/DXScreen.Width * Ceiling)
END SUB



SUB CloseApp
    WorldFrame.DeleteVisual(SkyBox.Mesh)
    WorldFrame.DeleteVisual(FloorObj.Mesh)
    Application.Terminate
END SUB



SUB intro
    CREATE BMP2 AS QBITMAP
        Width = DXWidth\2
        Height =DXHeight
        Font.Name = "Impact"
        Font.AddStyles(fsBold)
        pixelFormat = pf32Bit
        Transparent = True
        TransparentMode =  tmFixed
        TransparentColor = 0
    END CREATE

    DIM Scalar As INTEGER
    DIM num AS INTEGER

    For num = 1 to 3        'count down 
        For Scalar = 10 TO DXHeight\2 STEP DXHeight\16
            BMP2.FillRect(0, 0, BMP2.Width, BMP2.Height, 0)
            BMP2.Font.Size = Scalar
            BMP2.TextOut(2, 2, STR$(num), &HBBEE, -1)
            DXScreen.Render
            DXscreen.Draw(DXWidth\2-Scalar\2,0, BMP2.BMP) 
            DXscreen.Flip
        Next Scalar
    Next num
END SUB



SUB scoreboard
    DXScreen.TextOut(10, 10, "Player: "+STR$(p2_score) + "     ",&HFFFFFF, -1)' Print Player 1 Score 
    DXScreen.TextOut(170, 10, "r to toggle rotation",&HFFFFFF, -1)' Print Player 1 Score 
    DXScreen.TextOut(DXScreen.Width-200, 10, "Computer: "+STR$(p1_score) + "     ",&HFFFFFF, -1)' Print Player 2 Score 
    DXScreen.Flip
END SUB




SUB Make3DObjects                                   'make the box, floor and paddles     
    DXScreen.CreateMeshBuilder(SkyBox.Mesh)         'Reallocate memory 
	WITH SkyBox
     .ViewFromOutside = False                   'view from inside, it is a skybox (dome) 
     .MakeSphere(DXScreen, 300)
     .Mesh.Scale(5, 5, 5)
     .BoxSides = 4                              'No ceiling 
     .MakeBox (DXScreen)
     .Mesh.Scale(CourtX, Ceiling, CourtZ)
     .Mesh.SetRGBA(1.0, 1.0, 1.0, 0.35) 
	END WITH
    WorldFrame.AddVisual(SkyBox.Mesh)               'this object moves on top of world 

    DXScreen.CreateMeshBuilder(FloorObj.Mesh)
    WITH FloorObj
        .ViewFromOutside = False                'render the inside of the plane only 
        .MakeHorizPlane (DXScreen)              'make a horizontal plane 
        .Mesh.Scale(CourtX, 0.0, CourtZ)
'       .Mesh.SetRGB(0.2, 0.65, 1.0)            'floor color 
'        .Mesh.LoadTexture("c:\rapidq\Ground.bmp") 
    END WITH
    WorldFrame.AddVisual(FloorObj.Mesh)             'add Floor to frame, all will rotate/move together now 


    DXScreen.CreateMeshBuilder(Paddle1.Mesh)        'hold vertices 
    DXScreen.CreateFrame(Paddle1.Frame)             'holds 3d transformation matrix 
    WITH Paddle1
       .DrawCenter.y = -1
'       .MakeCylinder (DXScreen, 12) 
       .MakeCone (DXScreen, 12)
       .Mesh.Scale(1/PaddleThick, 1, 1/PaddleWidth)
       .DrawCenter.y = 0
       .BoxSides = 5                                   'all sides but the floor 
       .ViewFromOutside = True                         'view from outside 
       .MakeBox (DXScreen)
       .Mesh.Scale(PaddleThick, PaddleHeight, PaddleWidth)
       .Mesh.SetRGB(0.8, 0.0, 1.0)
       .Frame.AddVisual(Paddle1.Mesh)                  'put paddle on its own frame for movement 
    END WITH


    DXScreen.CreateMeshBuilder(Paddle2.Mesh)        'hold vertices 
    DXScreen.CreateFrame(Paddle2.Frame)             'holds 3d transformation matrix 
    WITH Paddle2
        .DrawCenter.y = -1
'        .MakeCylinder (DXScreen, 12) 
        .MakeCone (DXScreen, 12)
        .Mesh.Scale(1/PaddleThick, 1, 1/PaddleWidth)
        .DrawCenter.y = 0
        .BoxSides = 5
        .ViewFromOutside = True
        .MakeBox (DXScreen)
        .Mesh.Scale(PaddleThick, PaddleHeight, PaddleWidth)
        .Mesh.SetRGB(0.4, 0.9, 0.2)
        .Frame.AddVisual(Paddle2.Mesh)              'put paddle on its own frame for movement 
    END WITH


    DXScreen.CreateMeshBuilder(TheBall.Mesh)
    DXScreen.CreateFrame(TheBall.Frame)
    WITH TheBall
        .BoxSides = 6
        .ViewFromOutside = True
'		 .Mesh.Load("ball.x") 
        .MakeSphere (DXScreen, 128)
        .Mesh.Scale(BallSize, BallSize, BallSize)
        .Mesh.SetRGB(1.0, 1.0, 1.0)
        .Frame.SetRotation(0.2,0.8, 0.5, 3 * TheSpeed)
        .Frame.AddVisual(TheBall.Mesh)                      'All objects added to the root frame (WorldFrame) 
    END WITH
END SUB




SUB RotateCamera
    DIM tmpRadius AS SINGLE

    tmpRadius = Cam.GetXZRadius         'store it, next calculations affect radius 
    Cam.Pos.x = Cos(RelX)*tmpRadius     'move position around circle 
'   Cam.Pos.y = Cos(RelY)*tmpRadius     'centered at 0,0 
    Cam.Pos.z = Sin(RelX)*tmpRadius
    Cam.Orient.y = -0.5 '-Cos(RelY) 
    Cam.Orient.x = -Cos(RelX)
    Cam.Orient.z = -Sin(RelX)
    Cam.Update(DXScreen)
    INC(RelX, TheSpeed/20)
    INC(RelY, TheSpeed/20)
END SUB



SUB New_Psychedelic_Background
     DIM Pong_a_delc AS QBITMAP
     DIM MyTexture AS QD3DTexture
     DIM x As INTEGER, y As INTEGER, r As INTEGER, g As INTEGER, b As INTEGER
     DIM Bitsz As INTEGER
     DIM t AS SINGLE
    
     t = TIMER
     Bitsz = 2^(RND(5) + 2)
     Pong_a_delc.Width = Bitsz
     Pong_a_delc.Height = Bitsz
     Pong_a_delc.Pixelformat = pf24bit
    
     For x = 0 TO Bitsz
     For y = 0 TO Bitsz
      IF RND(10) > 5 THEN r = 255 ELSE r = 0
      IF RND(10) > 5 THEN g = 255 ELSE g = 0
      IF RND(10) > 5 THEN b = 255 ELSE b = 0
      Pong_a_delc.Pixel(x,y) = RGB(r,g,b)
     Next x
     Next y
     Pong_a_delc.SaveToFile(tmpBMPFile)
     DXScreen.LoadTexture(tmpBMPFile, MyTexture) 'store it on file in order to load it 
     SkyBox.Mesh.SetTexture(MyTexture)
     TheBall.Mesh.SetTexture(MyTexture)
     Pong_a_delc.Line(0, Bitsz\2, Bitsz, Bitsz\2, &HFFFFFF)
     Pong_a_delc.Line(Bitsz\2, 0, Bitsz\2, Bitsz, &HFFFFFF)
     Pong_a_delc.SaveToFile(tmpBMPFile)
     DXScreen.LoadTexture(tmpBMPFile, MyTexture) 'store it on file in order to load it 
     FloorObj.Mesh.SetTexture(MyTexture)
     KILL tmpBMPFile        'don't need it anymore 
     WrapObjects
     DO: LOOP UNTIL ((TIMER - t) > InterPlayDelay)
END SUB



SUB WrapObjects
    DXScreen.createWrap(D3DRMWRAP_FLAT,_    'type of texture projection 
            -CourtX, 0, -CourtZ, _        'origin on the mesh coordinates 
            0, 1, 0, _                   'the z-axis direction vector - texture projects down the z-azis 
            0, 0, 1, _                 'the up vector - the top of the texture points up the y-axis 
            0, 0, _                      'origin coordinates on bitmap 
            0.5/CourtX, 0.5/CourtZ, wrap)   'scale so bitmap covers all object 1/maxx, 1/maxy 
    wrap.apply(FloorObj.Mesh)

    DXScreen.createWrap(D3DRMWRAP_SPHERE,_    'type of texture projection 
            0, 0, 0, _          'origin on the mesh coordinates 
            0, 1, 0, _          'the z-axis direction vector - texture projects down the z-azis 
            0, 0, 1, _          'the up vector - the top of the texture points up the y-axis 
            0, 0, _             'origin coordinates on bitmap 
            1, 1, wrap) 
    wrap.Apply(SkyBox.Mesh)


    DXScreen.createWrap(D3DRMWRAP_CHROME,_    'type of texture projection 
            0, 0, 0, _            'origin on the mesh coordinates 
            0, 0, 1, _            'the z-axis direction vector - texture projects down the z-azis 
            0, 1, 0, _            'the up vector - the top of the texture points up the y-axis 
            0, 0, _               'origin coordinates on bitmap 
            2, 2, wrap)
    wrap.apply(TheBall.Mesh)

END SUB



SUB ChromeWrapThem  'hmmm not working 
	Wrap.ApplyRelative(TheBall.Frame,TheBall.Mesh) 'for each frame 
    wrap.applyRelative(WorldFrame, TheBall.Mesh)
END SUB



SUB MakeParticles
 DIM Face1 AS QD3DFace
 DIM Face2 AS QD3DFace
 DIM Face3 AS QD3DFace
 DIM i AS INTEGER

 DXScreen.CreateFace(Face1)  'create face instance 
 Face1.AddVertex(-1, 0, -1): Face1.AddVertex( 1, 0, -1)  'horiz plane 
 Face1.AddVertex( 1, 0, 1): Face1.AddVertex(-1, 0, 1)
 Face1.AddVertex(-1, 0, 1): Face1.AddVertex( 1, 0, 1)
 Face1.AddVertex( 1, 0, -1): Face1.AddVertex(-1, 0, -1)
 DXScreen.CreateFace(Face2)
 Face2.AddVertex(0, -1, -1): Face2.AddVertex( 0, 1, -1)  'vert y-z plane 
 Face2.AddVertex( 0, 1, 1): Face2.AddVertex(0, -1, 1)
 Face2.AddVertex(0, -1, 1): Face2.AddVertex( 0, 1, 1)
 Face2.AddVertex( 0, 1, -1): Face2.AddVertex(0, -1, -1)
 DXScreen.CreateFace(Face3)
 Face3.AddVertex(-1, -1, 0): Face3.AddVertex( 1, -1, 0)  'horiz plane 
 Face3.AddVertex( 1, 1, 0): Face3.AddVertex(-1, 1, 0)
 Face3.AddVertex(-1, 1, 0): Face3.AddVertex( 1, 1, 0)
 Face3.AddVertex( 1, -1, 0): Face3.AddVertex(-1, -1, 0)

 WITH Particles1
  .CloneNum = NumParticles
  '.File = "YourModel.x"  '3D mesh file in binary .x format 
  .Range.Left  = -CourtX
  .Range.Top  = -CourtZ
  .Range.Right = CourtX
  .Range.Bottom = CourtZ
  .RandPos = True
  .RandScale = True
  .Init(DXScreen)    'set properties first, creates frames, meshbuilder COM objects 
  .Mesh.AddFace(Face1)   'follows init 
  .Mesh.AddFace(Face2)   'follows init 
  .Mesh.AddFace(Face3)   'follows init 
  .Mesh.SetRGBA(1.0, 1.0, 0.0, 0.25)
  .Mesh.Scale(0.5, 0.5, 0.5)
  FOR i = 1 TO .CloneNum
   WorldFrame.AddFrame(Particles1.Frame(i)) 'now particles move with the whole scene 
  NEXT i
 END WITH


 'must do this again for the next Particle system 
 DXScreen.CreateFace(Face1)  'create face instance 
 Face1.AddVertex(-1, 0, -1): Face1.AddVertex( 1, 0, -1)  'horiz plane 
 Face1.AddVertex( 1, 0, 1): Face1.AddVertex(-1, 0, 1)
 Face1.AddVertex(-1, 0, 1): Face1.AddVertex( 1, 0, 1)
 Face1.AddVertex( 1, 0, -1): Face1.AddVertex(-1, 0, -1)
 DXScreen.CreateFace(Face2)
 Face2.AddVertex(0, -1, -1): Face2.AddVertex( 0, 1, -1)  'vert y-z plane 
 Face2.AddVertex( 0, 1, 1): Face2.AddVertex(0, -1, 1)
 Face2.AddVertex(0, -1, 1): Face2.AddVertex( 0, 1, 1)
 Face2.AddVertex( 0, 1, -1): Face2.AddVertex(0, -1, -1)
 DXScreen.CreateFace(Face3)
 Face3.AddVertex(-1, -1, 0): Face3.AddVertex( 1, -1, 0)  'horiz plane 
 Face3.AddVertex( 1, 1, 0): Face3.AddVertex(-1, 1, 0)
 Face3.AddVertex(-1, 1, 0): Face3.AddVertex( 1, 1, 0)
 Face3.AddVertex( 1, -1, 0): Face3.AddVertex(-1, -1, 0)


 WITH Particles2
  .CloneNum = NumParticles
  '.File = "YourModel.x"  '3D mesh file in binary .x format 
  .Range.Left  = -CourtX
  .Range.Top  = -CourtZ
  .Range.Right = CourtX
  .Range.Bottom = CourtZ
  .RandPos = True
  .RandScale = True
  .Init(DXScreen)    'set properties first, creates frames, meshbuilder COM objects 
  .Mesh.AddFace(Face1)   'follows init 
  .Mesh.AddFace(Face2)   'follows init 
  .Mesh.AddFace(Face3)   'follows init 
  .Mesh.SetRGBA(1.0, 1.0, 1.0, 0.25)
  .Mesh.Scale(0.5, 0.5, 0.5)
  FOR i = 1 TO .CloneNum
   WorldFrame.AddFrame(Particles2.Frame(i))
  NEXT i
 END WITH
END SUB



SUB MoveParticles
 DIM i AS INTEGER

 WITH Particles1
 FOR i = 1 TO .CloneNum
     .Pos(i).x = .Pos(i).x + pSpeed * (RND - 0.5)
   IF .Pos(i).x < -CourtX THEN .Pos(i).x = -CourtX
   IF .Pos(i).x >  CourtX THEN .Pos(i).x =  CourtX
     .Pos(i).y = .Pos(i).y + pSpeed * (RND - 0.5)
   IF .Pos(i).y < 0 THEN .Pos(i).y = 0
   IF .Pos(i).y > Ceiling THEN .Pos(i).y = Ceiling
  .Pos(i).z = .Pos(i).z + pSpeed * (RND - 0.5)
   IF .Pos(i).z < -CourtZ THEN .Pos(i).z = -CourtZ
   IF .Pos(i).z >  CourtZ THEN .Pos(i).z =  CourtZ
  .Frame(i).SetPosition(.Pos(i).x, .Pos(i).y, .Pos(i).z)
 NEXT i

 END WITH

 WITH Particles2
 FOR i = 1 TO .CloneNum
     .Pos(i).x = .Pos(i).x + pSpeed * (RND - 0.5)
   IF .Pos(i).x < -CourtX THEN .Pos(i).x = -CourtX
   IF .Pos(i).x >  CourtX THEN .Pos(i).x =  CourtX
     .Pos(i).y = .Pos(i).y + pSpeed * (RND - 0.5)
   IF .Pos(i).y < 0 THEN .Pos(i).y = 0
   IF .Pos(i).y > Ceiling THEN .Pos(i).y = Ceiling
  .Pos(i).z = .Pos(i).z + pSpeed * (RND - 0.5)
   IF .Pos(i).z < -CourtZ THEN .Pos(i).z = -CourtZ
   IF .Pos(i).z >  CourtZ THEN .Pos(i).z =  CourtZ
  .Frame(i).SetPosition(.Pos(i).x, .Pos(i).y, .Pos(i).z)
 NEXT i
 END WITH
END SUB


MainForm.ShowModal                                          'get the program running