Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Peter's ball demo using MATH calls

Author Message
x4nd
Newbie

Joined: 05/07/2020
Location: United Kingdom
Posts: 11
 Posted: 10:34pm 24 Aug 2020 Copy link to clipboard Print this post

Hi,
Hope you don't mind, Peter, but I did get the MATH quaternions working so thought I should paste the BASIC in here so others can see how to use the functions; specifically how to use the "MATH Q_ROTATE" function, which is the one I struggled with.
Runs at around 45fps.
-Stuart

BallDemo.bas

option explicit
option default none

'use this to set the output to 640x480x256 colours
mode 3,8

'use this if we want to double-buffer the output (slower?)
'page write 1

cls

const edgelength=100 'set the length of the verticies of the ticosahedron
const zlocation=3000 'how far is the center of the ticosahedron away from us
const viewplane=1000 'how far is the viewplane away from us

'these arrays store our object vertices (60 vertices)
'ticos is an array of simple vertices
'nticos is an array of 60 quaternions - 5 values containing: w,x,y,z,magnitude
dim float ticos(59,2), nticos(59,4)
'this is our list of connected vertices; each vertex may have up to 3 connections
dim integer linelist(59,2)

'temporary variables
dim integer i, j, k, k2, found
dim float x,y,z,d,x1,y1
dim float v(4), vout(4)

'use this if we want to display the FPS counter
dim string jout

'
' data for location of verticies for truncated icosahedron of edge length 2
'
dim float phi=(1+sqr(5))/2
data 0,1,3*phi
data 0,1,-3*phi
data 0,-1,3*phi
data 0,-1,-3*phi
data 1,3*phi,0
data 1,-3*phi,0
data -1,3*phi,0
data -1,-3*phi,0
data 3*phi,0,1
data 3*phi,0,-1
data -3*phi,0,1
data -3*phi,0,-1
data 2,(1+2*phi),phi
data 2,(1+2*phi),-phi
data 2,-(1+2*phi),phi
data 2,-(1+2*phi),-phi
data -2,(1+2*phi),phi
data -2,(1+2*phi),-phi
data -2,-(1+2*phi),phi
data -2,-(1+2*phi),-phi
data (1+2*phi),phi,2
data (1+2*phi),phi,-2
data (1+2*phi),-phi,2
data (1+2*phi),-phi,-2
data -(1+2*phi),phi,2
data -(1+2*phi),phi,-2
data -(1+2*phi),-phi,2
data -(1+2*phi),-phi,-2
data phi,2,(1+2*phi)
data phi,2,-(1+2*phi)
data phi,-2,(1+2*phi)
data phi,-2,-(1+2*phi)
data -phi,2,(1+2*phi)
data -phi,2,-(1+2*phi)
data -phi,-2,(1+2*phi)
data -phi,-2,-(1+2*phi)
data 1,(2+phi),2*phi
data 1,(2+phi),-2*phi
data 1,-(2+phi),2*phi
data 1,-(2+phi),-2*phi
data -1,(2+phi),2*phi
data -1,(2+phi),-2*phi
data -1,-(2+phi),2*phi
data -1,-(2+phi),-2*phi
data (2+phi),2*phi,1
data (2+phi),2*phi,-1
data (2+phi),-2*phi,1
data (2+phi),-2*phi,-1
data -(2+phi),2*phi,1
data -(2+phi),2*phi,-1
data -(2+phi),-2*phi,1
data -(2+phi),-2*phi,-1
data 2*phi,1,(2+phi)
data 2*phi,1,-(2+phi)
data 2*phi,-1,(2+phi)
data 2*phi,-1,-(2+phi)
data -2*phi,1,(2+phi)
data -2*phi,1,-(2+phi)
data -2*phi,-1,(2+phi)
data -2*phi,-1,-(2+phi)

'
' read in the x,y,z coordinates of the 60 verticies and scale by edgelength
'
for i=0 to 59
for j=0 to 2
ticos(i,j)=ticos(i,j)*edgelength/2
next j
next i

'Initialise the linelist array (-1 means not used)
for i=0 to 59
for j = 0 to 2
linelist(i,j)=-1
next j
next i

'
'Find coordinate pairs that are 100 pixels apart
'
for i=0 to 59
k=0
for j=0 to 59
if i <> j then 'no line from a vertex to itself
'calculate the distance between vertex i and j
d=sqr((ticos(j,0)-ticos(i,0))^2 + (ticos(j,1)-ticos(i,1))^2 + (ticos(j,2)-ticos(i,2))^2 )
'check if it's an edgelength apart (should be connected)
if abs(d-edgelength)<1 then
found=0
if (j < i) then
for k2=0 to 2
if linelist(j,k2)=i then
found=1
exit for
end if
next k2
end if
'if we didn't find it then its an edge we need
if 1 <> found then
linelist(i,k)=j
k=k+1
end if
end if
end if
next j
next i

'
'convert x,y,z coordinates to normalised form and save as quaternions (w,x,y,z,magnitude)
'
for i=0 to 59
create_vector(ticos(i,0),ticos(i,1),ticos(i,2),v())
nticos(i,0)=v(0): nticos(i,1)=v(1): nticos(i,2)=v(2): nticos(i,3)=v(3): nticos(i,4)=v(4)
next i

'
'create a quarternion to rotate 2 degrees about a chosen axis
'play with the x,y,z vector which is the sxis of rotation
'
dim float rot(4)
MATH Q_CREATE 0.02,1.0,0.5,0.75,rot()

'use this if we want to count the FPS
dim integer frames=0
SETTICK 1000,framecounter

'temp arrays to hold the x,y locations of the line ends to draw
dim integer xs0(89),ys0(89),xe0(89),ye0(89)

'
' MAIN LOOP
'
do
'rotate the object nticos() using rotation vector rot() (60 vertices)
for i=0 to 59
v(1)=nticos(i,1): v(2)=nticos(i,2): v(3)=nticos(i,3): v(4)=nticos(i,4):v(0)=nticos(i,0)
MATH Q_ROTATE rot(),v(),vout()
nticos(i,0)=vout(0):nticos(i,1)=vout(1): nticos(i,2)=vout(2): nticos(i,3)=vout(3): nticos(i,4)=vout(4)
next i

'rasterize - turn the 3d object into a set of 2d lines
j=0
for k=0 to 59
if -1 <> linelist(k,0) then
x=nticos(k,1)*viewplane/(nticos(k,3)+zlocation)*nticos(k,4)+MM.HRES/2
y=nticos(k,2)*viewplane/(nticos(k,3)+zlocation)*nticos(k,4)+MM.VRES/2
for i=0 to 2
if -1<>linelist(k,i) then
x1=nticos(linelist(k,i),1)*viewplane/(nticos(linelist(k,i),3)+zlocation)*nticos(linelist(k,i),4)+MM.HRES/2
y1=nticos(linelist(k,i),2)*viewplane/(nticos(linelist(k,i),3)+zlocation)*nticos(linelist(k,i),4)+MM.VRES/2
xs0(j)=x:ys0(j)=y:xe0(j)=x1:ye0(j)=y1:j=j+1
else
exit for
end if
next i
end if
next k

'clear the display
cls

'draw our array of lines on screen
line xs0(),ys0(),xe0(),ye0()

'use this if we want to display the FPS
text 0,0,jout,L
frames=frames+1

'use this if we want to double-buffer the display (slower probably)
'page copy 1 to 0,d 'i, b, d

'have we tapped 'ESC'?
IF INKEY\$ = CHR\$(27) THEN END
loop

'
' SUBROUTINES
'
'use if we want an on-screen FPS counter
sub framecounter()
jout="FPS="+STR\$(frames):frames=0
end sub

'function to create a new vecotr
sub create_vector(x as float,y as float ,z as float,v() as float)
v(4)=sqr(x*x + y*y + z*z)'magnitude (4th field of qaternion)
v(0)=0'w
v(1)=x/v(4)'x
v(2)=y/v(4)'y
v(3)=z/v(4)'z
end sub
If only there were more hours in the day..

x4nd
Newbie

Joined: 05/07/2020
Location: United Kingdom
Posts: 11
 Posted: 01:21am 25 Aug 2020 Copy link to clipboard Print this post

Couldn't resist putting in a bit more effort to hit 60fps:

BallDemo.bas

option explicit
option default none

'use this to set the output to 640x480x256 colours
mode 3,8

'use this if we want to double-buffer the output (slower?)
page write 1

cls

const edgelength=100 'set the length of the verticies of the ticosahedron
const zlocation=3000 'how far is the center of the ticosahedron away from us
const viewplane=1000 'how far is the viewplane away from us

'these arrays store our object vertices (60 vertices)
'ticos is an array of simple vertices
'nticos is an array of 60 quaternions - 5 values containing: w,x,y,z,magnitude
dim float ticos(59,2), nticos(59,4)
' this holds our 2d vertices
dim integer screen(59,1)
'this is our list of connected vertices; each vertex may have up to 3 connections
dim integer linelist(59,2)

'temporary variables
dim integer i, j, k, k2, found
dim float x,y,z,d,x1,y1
dim float v(4), vout(4)

'use this if we want to display the FPS counter
dim string jout

'
' data for location of verticies for truncated icosahedron of edge length 2
'
dim float phi=(1+sqr(5))/2
data 0,1,3*phi
data 0,1,-3*phi
data 0,-1,3*phi
data 0,-1,-3*phi
data 1,3*phi,0
data 1,-3*phi,0
data -1,3*phi,0
data -1,-3*phi,0
data 3*phi,0,1
data 3*phi,0,-1
data -3*phi,0,1
data -3*phi,0,-1
data 2,(1+2*phi),phi
data 2,(1+2*phi),-phi
data 2,-(1+2*phi),phi
data 2,-(1+2*phi),-phi
data -2,(1+2*phi),phi
data -2,(1+2*phi),-phi
data -2,-(1+2*phi),phi
data -2,-(1+2*phi),-phi
data (1+2*phi),phi,2
data (1+2*phi),phi,-2
data (1+2*phi),-phi,2
data (1+2*phi),-phi,-2
data -(1+2*phi),phi,2
data -(1+2*phi),phi,-2
data -(1+2*phi),-phi,2
data -(1+2*phi),-phi,-2
data phi,2,(1+2*phi)
data phi,2,-(1+2*phi)
data phi,-2,(1+2*phi)
data phi,-2,-(1+2*phi)
data -phi,2,(1+2*phi)
data -phi,2,-(1+2*phi)
data -phi,-2,(1+2*phi)
data -phi,-2,-(1+2*phi)
data 1,(2+phi),2*phi
data 1,(2+phi),-2*phi
data 1,-(2+phi),2*phi
data 1,-(2+phi),-2*phi
data -1,(2+phi),2*phi
data -1,(2+phi),-2*phi
data -1,-(2+phi),2*phi
data -1,-(2+phi),-2*phi
data (2+phi),2*phi,1
data (2+phi),2*phi,-1
data (2+phi),-2*phi,1
data (2+phi),-2*phi,-1
data -(2+phi),2*phi,1
data -(2+phi),2*phi,-1
data -(2+phi),-2*phi,1
data -(2+phi),-2*phi,-1
data 2*phi,1,(2+phi)
data 2*phi,1,-(2+phi)
data 2*phi,-1,(2+phi)
data 2*phi,-1,-(2+phi)
data -2*phi,1,(2+phi)
data -2*phi,1,-(2+phi)
data -2*phi,-1,(2+phi)
data -2*phi,-1,-(2+phi)

'
' read in the x,y,z coordinates of the 60 verticies and scale by edgelength
'
for i=0 to 59
for j=0 to 2
ticos(i,j)=ticos(i,j)*edgelength/2
next j
next i

'Initialise the linelist array (-1 means not used)
for i=0 to 59
for j = 0 to 2
linelist(i,j)=-1
next j
next i

'
'Find coordinate pairs that are 100 pixels apart
'
for i=0 to 59
k=0
for j=0 to 59
if i <> j then 'no line from a vertex to itself
'calculate the distance between vertex i and j
d=sqr((ticos(j,0)-ticos(i,0))^2 + (ticos(j,1)-ticos(i,1))^2 + (ticos(j,2)-ticos(i,2))^2 )
'check if it's an edgelength apart (should be connected)
if abs(d-edgelength)<1 then
found=0
if (j < i) then
for k2=0 to 2
if linelist(j,k2)=i then
found=1
exit for
end if
next k2
end if
'if we didn't find it then its an edge we need
if 1 <> found then
linelist(i,k)=j
k=k+1
end if
end if
end if
next j
next i

'
'convert x,y,z coordinates to normalised form and save as quaternions (w,x,y,z,magnitude)
'
for i=0 to 59
create_vector(ticos(i,0),ticos(i,1),ticos(i,2),v())
nticos(i,0)=v(0): nticos(i,1)=v(1): nticos(i,2)=v(2): nticos(i,3)=v(3): nticos(i,4)=v(4)
next i

'
'create a quarternion to rotate 2 degrees about a chosen axis
'play with the x,y,z vector which is the sxis of rotation
'
dim float rot(4)
MATH Q_CREATE 0.02,1.0,0.5,0.75,rot()

'use this if we want to count the FPS
'dim integer frames=0
'SETTICK 1000,framecounter

'temp arrays to hold the x,y locations of the line ends to draw
dim integer xs0(89),ys0(89),xe0(89),ye0(89)

'find the center of the screen
dim integer hcenter = MM.HRES/2
dim integer vcenter = MM.VRES/2
'temp variable to reduce #calculations
dim float zpos

'
' MAIN LOOP
'
do
'rotate the object nticos() using rotation vector rot() (60 vertices)
for i=0 to 59
v(1)=nticos(i,1): v(2)=nticos(i,2): v(3)=nticos(i,3): v(4)=nticos(i,4)
MATH Q_ROTATE rot(),v(),vout()
nticos(i,1)=vout(1): nticos(i,2)=vout(2): nticos(i,3)=vout(3): nticos(i,4)=vout(4)

'transform this vertex into 2d space and store the position. Just once per vertex.
zpos=viewplane/(nticos(1,3)+zlocation)*nticos(i,4)
screen(i,0)=nticos(i,1)*zpos+hcenter
screen(i,1)=nticos(i,2)*zpos+vcenter
next i

'calculate the lines we want to draw between the vertices
j=0
for k=0 to 59
if -1 <> linelist(k,0) then
for i=0 to 2
if -1<>linelist(k,i) then
xs0(j)=screen(k,0)
ys0(j)=screen(k,1)
xe0(j)=screen(linelist(k,i),0)
ye0(j)=screen(linelist(k,i),1)
j=j+1
else
exit for
end if
next i
end if
next k

'clear the display
cls
'draw our array of lines on screen
line xs0(),ys0(),xe0(),ye0()

'use this if we want to display the FPS
'text 0,0,jout,L
'frames=frames+1

'use this if we want to double-buffer the display (slower probably)
page copy 1 to 0,i 'i, b, d

'have we tapped 'ESC'?
IF INKEY\$ = CHR\$(27) THEN END
loop

'
' SUBROUTINES
'
'use if we want an on-screen FPS counter
sub framecounter()
jout="FPS="+STR\$(frames):frames=0
end sub

'function to create a new vecotr
sub create_vector(x as float,y as float ,z as float,v() as float)
v(4)=sqr(x*x + y*y + z*z)'magnitude (4th field of qaternion)
v(0)=0'w
v(1)=x/v(4)'x
v(2)=y/v(4)'y
v(3)=z/v(4)'z
end sub

Edited 2020-08-26 22:29 by x4nd
If only there were more hours in the day..