3D Cube - Filled Polygon & Wire frame Polygons
This PlayBASIC program draws a spinning 3D cube on the screen. It shows two versions of the cube side-by-side — one filled with solid-colored faces (flat-shaded) and one drawn as a wireframe.
Here's how it works:
Setup: It creates a random color palette for the cube's faces and sets up arrays to store the cube's 3D points (vertices) and the faces (which points connect to make each side).
Scaling: The cube's points are scaled to a larger size using the SizeVerts function.
Rotation: Every frame, the cube's points are rotated around the X, Y, and Z axes using a rotation matrix inside the RotateVerts function. This gives the cube its spinning effect.
Projection: After rotation, the 3D points are projected onto the 2D screen so they can be drawn.
Drawing: The DrawCube function draws each face twice:
On the left side, it draws a filled, solid-colored polygon.
On the right side, it draws just the wireframe (outline) of the face.
Backface Culling: The code uses the cross product to check whether each face is pointing towards the viewer and only draws the visible ones.
Animation: The cube keeps rotating smoothly because the tilt, turn, and roll angles are slightly increased every frame.
Controls: Pressing the up arrow zooms the camera closer, and down arrow zooms out.
[pbcode]
/*
This PlayBASIC program draws a spinning 3D cube using random colors. It scales the cube, rotates its points in 3D space, projects them onto the 2D screen, and draws each face as a filled shape on the left and a wireframe on the right. The cube rotates automatically and the camera distance can be adjusted with the up/down keys.
*/
// Colour palette for the cube faces
dim Palette(16)
for lp =0 to 16
Palette(lp) = rndrgb()
next
Rem create arrays to hold the points (vertex)
rem and face data for our cube.
Dim Points#(8,5)
Dim Faces(6,5)
Rem read in the vertex data
For p = 1 To 8
points#(p,1) = ReadData()
points#(p,2) = ReadData()
points#(p,3) = ReadData()
Next p
Rem Read in the face Data
For f = 1 To 6
faces(f,1) = ReadData()
faces(f,2) = ReadData()
faces(f,3) = ReadData()
faces(f,4) = ReadData()
Next f
Rem some space To put the rotated points
Dim rotated#(255,3)
Rem variables which control the size of the cube
x_scale# = 200.0
y_scale# = 200.0
z_scale# = 200.0
Rem set the initial rotation angles To 0
tilt#=0.0
turn#=0.0
roll#=0.0
SizeVerts(Points#(),x_scale#,y_scale#,z_scale#)
// distance from the viewer
Zdepth#=1000
Do
// clear the screen to rgb colour
cls $334455
// Rotate vertex
RotateVerts(points#(),Rotated#(),8,Tilt#,turn#,roll#,Zdepth#)
// Draw the cube projected to the screen
DrawCube()
Rem animate the tilt, turn And roll values
tilt# = tilt#+1.31
turn# = turn#+0.42
roll# = roll#+0.53
if upkey() and zdepth#>400 then Zdepth#=Zdepth#-20
if downkey() then Zdepth#=Zdepth#+20
// Flip the buffer to show user the frame and start rendering
// the new frame
Sync
loop
Function RotateVerts(Pts#(),RotatedPts#(),NumbOfVerts,Tilt#,turn#,roll#,ObjectDistance#)
ProjectionX#=400
ProjectionY#=400
cx=getscreenwidth()/2
cy=getscreenheight()/2
Rem prepare the rotation matrix
A#=Cos(tilt#):B#=Sin(tilt#)
C#=Cos(turn#):D#=Sin(turn#)
E#=Cos(roll#):F#=Sin(roll#)
AD#=A#*D#
BD#=B#*D#
; Calc Rotation Matrix
m11#=C#*E#
m21#=-1*C#*F#
m31#=D#
m12#=BD#*E#+A#*F#
m22#=-1*BD#*F#+A#*E#
m32#=-1*B#*C#
m13#=-1*AD#*E#+B#*F#
m23#=AD#*F#+B#*E#
m33#=A#*C#
Rem rotate all the points using the matrix
For p=1 To NumbOfVerts
pointx#=pts#(p,1)
pointy#=pts#(p,2)
pointz#=pts#(p,3)
rotatedPts#(p,1) = (m11# * pointx#) + (m12# * pointy#) + (m13# * pointz#)
rotatedPts#(p,2) = (m21# * pointx#) + (m22# * pointy#) + (m23# * pointz#)
rotatedPts#(p,3) = (m31# * pointx#) + (m32# * pointy#) + (m33# * pointz#)
Rem Now Do the perspective calculation
z# = rotatedPts#(p,3) + ObjectDistance#
rotatedPts#(p,1) = cx+ ((rotatedPts#(p,1)*ProjectionX# ) / z#)
rotatedPts#(p,2) = cy+ ((rotatedpts#(p,2)*ProjectionY# )/ z#)
Next p
Endfunction
Function DrawCube()
lockbuffer
For f=1 To 6
Rem p1 -> p4 are the points On the face
p1 = faces(f,1)
p2 = faces(f,2)
p3 = faces(f,3)
p4 = faces(f,4)
vx1#=rotated#(p1,1)
vy1#=rotated#(p1,2)
vx2#=rotated#(p2,1)
vy2#=rotated#(p2,2)
vx3#=rotated#(p3,1)
vy3#=rotated#(p3,2)
; Use Cross product to check if the face is pointing towards the camera
If (((vx2#-vx1#)*(vy3#-vy1#))-((vx3#-vx1#)*(vy2#-vy1#)))>0
vx4#=rotated#(p4,1)
vy4#=rotated#(p4,2)
pad=200
// draw this face was wire frame
vx1#-=pad
vx2#-=pad
vx3#-=pad
vx4#-=pad
quadc vx1#,vy1#,vx2#,vy2#,vx3#,vy3#,vx4#,vy4#,Palette(f)
pad*=2
// draw this face was wire frame
vx1#+=pad
vx2#+=pad
vx3#+=pad
vx4#+=pad
ThisRGB =Palette(f)
linec vx1#,vy1#,vx2#,vy2#,ThisRGB
linec vx2#,vy2#,vx3#,vy3#,ThisRGB
linec vx3#,vy3#,vx4#,vy4#,ThisRGB
linec vx4#,vy4#,vx1#,vy1#,ThisRGB
EndIf
Next f
unlockbuffer
EndFunction
Function SizeVerts(Pts#(),x_scale#,y_scale#,z_scale#)
Rem rotate all the points using the matrix
For p=1 To getArrayelements(pts#(),1)
pts#(p,1)=pts#(p,1)* x_scale#
pts#(p,2)=pts#(p,2)* Y_scale#
pts#(p,3)=pts#(p,3)* Z_scale#
Next p
Endfunction
Rem vertex Data
Data -1,-1,-1
Data 1,-1,-1
Data 1,1,-1
Data -1,1,-1
Data -1,-1,1
Data 1,-1,1
Data 1,1,1
Data -1,1,1
Rem face Data
;right/left
Data 2,6,7,3
Data 5,1,4,8
Data 1,2,3,4
Data 6,5,8,7
; top /bot
Data 5,6,2,1
Data 4,3,7,8
[/pbcode]