 A BB4W Compendium freeman69@gmx.com

IDIC  Point Of Light

In previous programs we've created graphics by using lines to join points, where the points (or 'nodes') are rotated prior to drawing. Basic 3D wireframe and solid graphics can be drawn using similar techniques. Wireframe uses lines, whereas solid graphics are based on filled triangles.

When using triangles, it's possible to simulate a point of light by using brighter shades of colour when a triangle (or plane) faces the light to a greater degree. Conversely, triangles facing away from the light are drawn using darker shades. Brightness can be determined by finding the angle between the 'vector pointing to a distant light source' and 'the normal of a plane'. (The normal is perpendicular to the plane).

10 MODE 9:OFF

20 ORIGIN 640,512

30 *REFRESH OFF

40 DIM nd{(18) xt,yt,zt,xr,yr,zr,x2,y2}:REM Nodes

60 DIM td{(1) np,lp,nc,lc}:REM Net pointers

70 FOR a=0 TO 18

90 NEXT

100 FOR a=0 TO 25

120 NEXT

130 FOR a=0 TO 1

150 NEXT

160 REM Create a unit vector pointing towards a light source

170 lightx=100:lighty=100:lightz=10

180 lm=SQR(lightx^2+lighty^2+lightz^2)

190 lightx/=lm:lighty/=lm:lightz/=lm

200

210 TIME=0:za=0:xa=0:ya=0

220 REPEAT

230   T=TIME:CLS

240   net=(TIME DIV 800)MOD 2

250   PROCdraw(net,0,0,-40,za,xa,ya)

260   za-=2.6:xa+=1:ya+=3.4

270   *REFRESH

280   WAIT 4-(TIME-T)

290 UNTIL 0

300 END

310

320 DEF PROCdraw(net,x,y,z,za,xa,ya)

330 LOCAL f,abort,col,a,n1,n2,n3,v1x,v1y,v1z,v2x,v2y,v2z,nx,ny,nz,nm,v

340 f=1280:abort=FALSE:col=7:GCOL 0,15

350 FOR a=td{(net)}.np TO td{(net)}.np+td{(net)}.nc-1

360   PROCrotzxy(a,x,y,z,za,xa,ya):REM Rotate node & add offset

370   IF nd{(a)}.zr>-1 THEN

380     abort=TRUE:REM Node behind viewer

390   ELSE

400     nd{(a)}.x2=f*nd{(a)}.xr/-nd{(a)}.zr:REM 3D to 2D

410     nd{(a)}.y2=f*nd{(a)}.yr/-nd{(a)}.zr

420   ENDIF

430 NEXT

440 IF abort ENDPROC

450 FOR a=td{(net)}.lp TO td{(net)}.lp+td{(net)}.lc-1

480     WHEN 7

520       REM Calc two vectors from 3 co-ords (triangle): b-a and c-a

530       v1x=nd{(n2)}.xr-nd{(n1)}.xr

540       v1y=nd{(n2)}.yr-nd{(n1)}.yr

550       v1z=nd{(n2)}.zr-nd{(n1)}.zr

560       v2x=nd{(n3)}.xr-nd{(n1)}.xr

570       v2y=nd{(n3)}.yr-nd{(n1)}.yr

580       v2z=nd{(n3)}.zr-nd{(n1)}.zr

590       REM Find the 'normal' of a triangle, (cross product of two vectors)

600       nx=v1y*v2z-v1z*v2y

610       ny=v1z*v2x-v1x*v2z

620       nz=v1x*v2y-v1y*v2x

630       REM Vector towards viewer

640       v1x=0-nd{(n1)}.xr

650       v1y=0-nd{(n1)}.yr

660       v1z=0-nd{(n1)}.zr

670       REM Triangle faces viewer (normal points towards viewer)?

680       IF (nx*v1x+ny*v1y+nz*v1z)>0 THEN

690         nm=SQR(nx^2+ny^2+nz^2)

700         REM Calc triangle shade from: A.B / |A||B|

710         v=(nx*lightx+ny*lighty+nz*lightz)/nm

720         if v<-1 v=0 else if v>1 v=255 else v=255-acs(v)/pi*255

730         COLOUR 15,v*(col AND 1),v*((col AND 2)/2),v*((col AND 4)/4)

740         MOVE nd{(n1)}.x2,nd{(n1)}.y2

750         MOVE nd{(n2)}.x2,nd{(n2)}.y2

760         PLOT 85,nd{(n3)}.x2,nd{(n3)}.y2

770       ENDIF

780   ENDCASE

790 NEXT

800 ENDPROC

810

820 REM Rotate a node about 3 axes and add any offset

830 DEF PROCrotzxy(n,x,y,z,za,xa,ya)

840 LOCAL ca,sa,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4

850 x1=nd{(n)}.xt:y1=nd{(n)}.yt:z1=nd{(n)}.zt

890 nd{(n)}.xr=x4+x:nd{(n)}.yr=y4+y:nd{(n)}.zr=z4+z

900 ENDPROC

910

920 REM Ship K nodes (x,y,z)

930 DATA 0,4,-8,0,-4,-8,8,0,-4,-8,0,-4,0,0,8,6,4,-8

940 DATA 6,-4,-8,-6,-4,-8,-6,4,-8,8,0,8,-8,0,8,8,0,-8,-8,0,-8

950 REM Ship S

960 DATA -3,0,8,3,0,8,8,0,-8,-8,0,-8,0,4,-4,0,-4,-4

970 REM Ship K links (type,n1,n2,n3: type 1=colour, 7=triangle)

980 DATA 1,1,0,0,7,7,12,10,7,12,8,10,7,11,6,9,7,9,5,11,1,7,0,0

990 DATA 7,0,4,2,7,0,3,4,7,4,3,1,7,1,2,4,7,2,1,0,7,1,3,0

1000 DATA 1,1,0,0,7,7,10,12,7,12,10,8,7,9,6,11,7,9,11,5

1010 REM Ship S

1020 DATA 1,6,0,0,7,4,2,5,7,4,5,3,7,4,0,1,7,4,3,0,7,2,4,1

1030 DATA 7,5,1,0,7,5,2,1,7,5,0,3

1040 REM Ship pointers

1050 DATA 0,0,13,17

1060 DATA 13,17,6,9 Point Of Light: Code explained...

40 DIM nd{(18) xt,yt,zt,xr,yr,zr,x2,y2}:REM Nodes

60 DIM td{(1) np,lp,nc,lc}:REM Net pointers

'xt', 'yt' & 'zt' hold the template nodes.

'xr', 'yr' & 'zr' will hold the transformed (rotated) template nodes.

'x2', 'y2' will hold the 2D co-ordinates of 3D rotated nodes projected onto the flat plane of the window.

The 'link' array holds the instructions for drawing the 3D shape. This program uses two types of instruction: 'type'=1 for changing the current colour and 'type'=7 to draw a triangle. The nodes (corners of the triangle) are pointed to by 'n1', 'n2' & 'n3'

'td' holds information on two separate 'nets'. A 'net' is the umbrella term for the template of a 3D object. This demonstration switches between 2 different net designs every 8 seconds.

160 REM Create a unit vector pointing towards a light source

170 lightx=100:lighty=100:lightz=10

180 lm=SQR(lightx^2+lighty^2+lightz^2)

190 lightx/=lm:lighty/=lm:lightz/=lm

To calculate the shade of each triangle, we need a light source and a vector pointing towards it. By making the vector a unit vector, we simplify the calculation slightly. (A unit vector has a magnitude of 1.)

320 DEF PROCdraw(net,x,y,z,za,xa,ya)

330 LOCAL f,abort,col,a,n1,n2,n3,v1x,v1y,v1z,v2x,v2y,v2z,nx,ny,nz,nm,v

340 f=1280:abort=FALSE:col=7:GCOL 0,15

350 FOR a=td{(net)}.np TO td{(net)}.np+td{(net)}.nc-1

360   PROCrotzxy(a,x,y,z,za,xa,ya):REM Rotate node & add offset

370   IF nd{(a)}.zr>-1 THEN

380     abort=TRUE:REM Node behind viewer

390   ELSE

400     nd{(a)}.x2=f*nd{(a)}.xr/-nd{(a)}.zr:REM 3D to 2D

410     nd{(a)}.y2=f*nd{(a)}.yr/-nd{(a)}.zr

420   ENDIF

430 NEXT

440 IF abort ENDPROC

When drawing a net, every node must be rotated so the object possesses the orientation we require. In 2D we only rotate about the Z-axis i.e. in the XY plane. In 3D there are 3 axes to rotate around. The order of rotation affects the final orientation.

This routine simplifies drawing by aborting if any of the nodes fall on the wrong side of the window i.e. behind the viewer. Otherwise each node is converted to 2 dimensions by projecting it onto the window.

450 FOR a=td{(net)}.lp TO td{(net)}.lp+td{(net)}.lc-1

The next stage involves reading the drawing instructions (or links). These instructions control the current colour and indicate which nodes are to be used to draw each triangle.

The design of the 3D net is extremely important. Nodes must be listed in the correct order i.e. anticlockwise, to face the viewer. With complex shapes, the order in which the triangles are drawn also makes a significant difference.

480     WHEN 7

'n1', 'n2' & 'n3' point to the specific row in the 'nd' (node) array.

520       REM Calc two vectors from 3 co-ords (triangle): b-a and c-a

530       v1x=nd{(n2)}.xr-nd{(n1)}.xr

540       v1y=nd{(n2)}.yr-nd{(n1)}.yr

550       v1z=nd{(n2)}.zr-nd{(n1)}.zr

560       v2x=nd{(n3)}.xr-nd{(n1)}.xr

570       v2y=nd{(n3)}.yr-nd{(n1)}.yr

580       v2z=nd{(n3)}.zr-nd{(n1)}.zr

590       REM Find the 'normal' of a triangle, (cross product of two vectors)

600       nx=v1y*v2z-v1z*v2y

610       ny=v1z*v2x-v1x*v2z

620       nz=v1x*v2y-v1y*v2x

To calculate the shade of a triangle, we need to find the 'normal' of the plane, which involves finding the 'cross product' of two vectors within the plane.

630       REM Vector towards viewer

640       v1x=0-nd{(n1)}.xr

650       v1y=0-nd{(n1)}.yr

660       v1z=0-nd{(n1)}.zr

670       REM Triangle faces viewer (normal points towards viewer)?

680       IF (nx*v1x+ny*v1y+nz*v1z)>0 THEN

By finding the 'normal' of each triangle, we can determine whether the triangle is facing the viewer or not: Is there less than 90 degrees difference between the 'normal' and 'a vector from the triangle towards the viewer'?

Note that our routine could be made more efficient, preventing these calculations from being repeated for flat polygons comprised of multiple triangles.

690         nm=SQR(nx^2+ny^2+nz^2)

700         REM Calc triangle shade from: A.B / |A||B|

710         v=(nx*lightx+ny*lighty+nz*lightz)/nm

720         v=255-ABS(ACS(v))/PI*255

730         COLOUR 15,v*(col AND 1),v*((col AND 2)/2),v*((col AND 4)/4)

740         MOVE nd{(n1)}.x2,nd{(n1)}.y2

750         MOVE nd{(n2)}.x2,nd{(n2)}.y2

760         PLOT 85,nd{(n3)}.x2,nd{(n3)}.y2

770       ENDIF

780   ENDCASE

790 NEXT

800 ENDPROC

The angle of the triangle to the light, between zero and 180 degrees, will be converted to a value between zero and 255, representing the full range of shades of the basic palette.

820 REM Rotate a node about 3 axes and add any offset

830 DEF PROCrotzxy(n,x,y,z,za,xa,ya)

840 LOCAL ca,sa,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4

850 x1=nd{(n)}.xt:y1=nd{(n)}.yt:z1=nd{(n)}.zt   