3d without openGL

User projects written in or related to FreeBASIC.
BasicCoder2
Posts: 3349
Joined: Jan 01, 2009 7:03

3d without openGL

For some unknown reason I have for the time being taken an interest again in doing some 3D stuff using only the FreeBasic's graphic commands.

I have been scouring the internet for a simple explanation of a rotation problem I encountered while playing with some simple code that I recently posted and so far I have come up with it being the need to rotate around an arbitrary axis. It appears that unlike the 2d world none of this 3d world math is simple. The explanations are given in mathematical notation that I am not familiar with so it would be a very big learning curve for me to take on.

I think I need to write the code to perform these actions:
Rotation around arbitrary axis:
1. Translate the P0 (x0, y0, z0) axis point to the origin of the coordinate system.
2. Perform appropriate rotations to make the axis of rotation coincident with z-coordinate axis.
3. Rotate about the z-axis by the angle θ.
4. Perform the inverse of the combined rotation transformation.
5. Perform the inverse of the translation.

Which I am assuming is illustrated here.
Last edited by BasicCoder2 on Oct 08, 2017 5:52, edited 2 times in total.
paul doe
Posts: 913
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Hi BasicCoder2

I'm preparing something for you. When I finish it, I will post it here or on a separate thread, if you prefer. The code is relatively complicated (all 3D stuff is), but we can review it, step by step. Just not right now, as I'm too drunk. But as soon as I finish, I will let you know, ok? Until then, see ya.
dafhi
Posts: 1238
Joined: Jun 04, 2005 9:51

Re: 3d without openGL

i'll let paul doe do his thing and just say a few words. rotation about an arbitrary axis is not an easy thing. mathoma on youtube gives a great video about rodrigues rotation. vector arithmetic is rock-simple however, and vectors are the building blocks of "deeper" 3d, including rodrigues rotation

I also recommend mathoma's video on complex number multiplication
BasicCoder2
Posts: 3349
Joined: Jan 01, 2009 7:03

Re: 3d without openGL

This is where I am at so far.
The rocket seems to rotate around the x axis where ever it is due I think to the rotation routine dealing with that axis first?

Code: Select all

`'some useful definesConst Pi = 4 * Atn(1)Dim Shared As Double TwoPi = 8 * Atn(1)Dim Shared As Double RtoD = 180 / Pi   ' radians * RtoD = degreesDim Shared As Double DtoR = Pi / 180   ' degrees * DtoR = radiansconst SCRW = 800const SCRH = 600screenres SCRW,SCRH,32color rgb(0,0,0),rgb(255,255,255):clsdim shared as integer midX,midY  'origin position(0,0,0) position on screenmidX = SCRW\2midY = SCRH\2const PointCount = 44const LineCount  = 73type POINT3D    as single x    'absolute position    as single y    as single z    as single rx   'position after rotation    as single ry    as single rz    as ulong  c    'color of pixelend typetype GROUP    as POINT3D pt(0 to pointCount)  'set of 32 points    as single cx    'point of rotation in absolute space    as single cy    as single cz    as single ax   'rotation around axis cx,cy    as single ay    as single az    as ulong  c     'color of group's points    as single dx    'direction of movement along x and y coordinates    as single dy    as single dzend typetype ALINE    as integer pt1    as integer pt2    as ulong   cend typedim shared as GROUP rocket  'group of dots called rocket'initialize rocket pointsfor i as integer = 0 to pointCount    read rocket.pt(i).x    read rocket.pt(i).y    read rocket.pt(i).znext idim shared as ALINE aLine1(0 to lineCount)for i as integer = 0 to lineCount    read aLine1(i).pt1,aLine1(i).pt2  'read start and end points of line    aLine1(i).c = rgb(0,0,0)          'color the lines blacknext i'recolor axes linesaLine1(70).c = rgb(255,0,0)aLine1(71).c = rgb(0,255,0)aLine1(72).c = rgb(0,0,255)rocket.cx = 150rocket.cy = 150rocket.cz = 0rocket.ax  = 0  'rotation around axesrocket.ay  = 0rocket.az  = 0rocket.c  = rgb(200,100,25)  'color of grouprocket.dx = 1rocket.dy = 0sub drawAxes()    line (0,100)-(800,500),rgb(255,0,0) 'y axis    line (0,500)-(800,100),rgb(0,255,0) 'x axis    line (400,300)-(400,200),rgb(0,0,255) 'z axis    line (400,200)-(380,220),rgb(0,0,255) 'up arrow    line (400,200)-(420,220),rgb(0,0,255)end subsub rotatePoints()        dim as single cosAngleX,sinAngleX,angleX    dim as single cosAngleY,sinAngleY,angleY    dim as single cosAngleZ,sinAngleZ,angleZ        angleX    = rocket.ax*DtoR        cosAngleX = cos(angleX)    sinAngleX = sin(angleX)        angleY    = rocket.ay*DtoR        cosAngleY = cos(angleY)    sinAngleY = sin(angleY)        angleZ    = rocket.az*DtoR        cosAngleZ = cos(angleZ)    sinAngleZ = sin(angleZ)        '=========================================        dim as single newX,newY,newZ,x,y,z        for i as integer = 0 to pointCount                x = rocket.pt(i).x        y = rocket.pt(i).y        z = rocket.pt(i).z                '***Rotation on the X-axis        NewY = y*cosAngleX - z*sinAngleX        NewZ = z*cosAngleX + y*sinAngleX        y = NewY        z = NewZ        '***Rotation on the Y-axis        NewZ = z*cosAngleY - x*sinAngleY        NewX = x*cosAngleY + z*sinAngleY        x = NewX                '***Rotation on the Z-axis        NewX = x*cosAngleZ - y*sinAngleZ        NewY = y*cosAngleZ + x*sinAngleZ        rocket.pt(i).rx = NewX        rocket.pt(i).ry = NewY        rocket.pt(i).rz = NewZ        rocket.pt(i).c = rocket.pt(i).c            next i    end subsub drawScene()    dim as single x,y,z,x1,y1,z1,x2,y2,z2    z = 0    rotatePoints()    screenlock    cls    drawAxes()    locate 2,2    print " use the arrow keys and the Z or X key to rotate"    print " use WASD keys to translate along x and y axes"    print " hit space key to reset position"            x = rocket.cx + midX   'screen coordinates        y = rocket.cy + midY        circle ( x - y + 300, (x + y)/2 - z - 50),5,rocket.c,,,0.4,f                for i as integer = 0 to pointCount  'draw each point in each group                        'get x,y positions            x = rocket.pt(i).rx + rocket.cx + midX            y = rocket.pt(i).ry + rocket.cy + midY            z = rocket.pt(i).rz            'draw rotation point in isometric display            circle ( x - y + 300, (x + y)/2 - z - 50),2,rocket.pt(i).c,,,0.4,f                     next i        'draw lines        for i as integer = 0 to lineCount  'number of lines            x1 = rocket.pt(aLine1(i).pt1).rx + rocket.cx + midX            y1 = rocket.pt(aLine1(i).pt1).ry + rocket.cy + midY            z1 = rocket.pt(aLine1(i).pt1).rz            x2 = rocket.pt(aLine1(i).pt2).rx + rocket.cx + midX            y2 = rocket.pt(aLine1(i).pt2).ry + rocket.cy + midY            z2 = rocket.pt(aLine1(i).pt2).rz             line ( x1 - y1 + 300, (x1 + y1)/2 - z1 - 50)-( x2 - y2 + 300, (x2 + y2)/2 - z2 - 50)  ,aLine1(i).c        next i            screenunlockend subdim as string keydo    drawScene            rotatePoints()                if multikey(&H39) then  'space key to reset rocket variables            rocket.ax = 0            rocket.ay = 0            rocket.az = 0            rocket.cx = 150            rocket.cy = 150            rocket.cz = 0            while multikey(&H39):wend        end if                if multikey(&H1E) then            rocket.cx = rocket.cx - 1        end if                if multikey(&H20) then            rocket.cx = rocket.cx + 1        end if                if multikey(&H1F) then            rocket.cy = rocket.cy - 1        end if                if multikey(&H11) then             rocket.cy = rocket.cy + 1        end if                'rotate around x axis        if multikey(&H48) then            rocket.ax = rocket.ax + 1            if rocket.ax = 360 then rocket.ax = 0        end if        if multikey(&H50) then            rocket.ax = rocket.ax - 1            if rocket.ax < 0 then rocket.ax = 359        end if                'rotate around y axis        if multikey(&H4B) then            rocket.ay = rocket.ay + 1            if rocket.ay = 360 then rocket.ay = 0        end if        if multikey(&H4D) then            rocket.ay = rocket.ay - 1            if rocket.ay < 0 then rocket.ay = 359        end if                'rotate around z axis        if multikey(&H2C) then   'Z KEY            rocket.az = rocket.az + 1            if rocket.az = 360 then rocket.az = 0        end if        if multikey(&H2D) then   'X KEY            rocket.az = rocket.az - 1            if rocket.az < 0 then rocket.az = 359        end if'=======================================    sleep 2loop until multikey(&H01)'=========================== 3D POINT DATA ===================================' points for rocket cylinderdata   0,-24,-50,  10,-22,-50,  17,-17,-50,  22,-10,-50data  24,  0,-50,  22, 10,-50,  17, 17,-50,  10, 22,-50data   0, 24,-50, -10, 22,-50, -17, 17,-50, -22, 10,-50data -24,  0,-50, -22,-10,-50, -17,-17,-50, -10,-22,-50data   0,-24, 50,  10,-22, 50,  17,-17, 50,  22,-10, 50data  24,  0, 50,  22, 10, 50,  17, 17, 50,  10, 22, 50data   0, 24, 50, -10, 22, 50, -17, 17, 50, -22, 10, 50data -24,  0, 50, -22,-10, 50, -17,-17, 50, -10,-22, 50data 0,0,80  'nose cone point'rocket finsdata -44,0,-50, -24,0,-50, -24,0,-30data  44,0,-50,  24,0,-50,  24,0,-30'points for relative axisdata 100,0,0, -100,0,0  'x axisdata 0,100,0, 0,-100,0  'y axisdata 0,0,100, 0,0,-100  'z axis'=============================  LINE DATA ==================================' 16 lines joining pointsdata  0,16, 1,17,  2,18,  3,19,  4,20,  5,21,  6,22,  7,23data  8,24, 9,25, 10,26, 11,27, 12,28, 13,29, 14,30, 15,31' lines to cone tipdata  32,16, 32,17, 32,18, 32,19, 32,20, 32,21, 32,22, 32,23data  32,24, 32,25, 32,26, 32,27, 32,28, 32,29, 32,30, 32,31' lines for finsdata  33,34, 34,35, 35,33data  36,37, 37,38, 38,36'lines to draw circumference of top and bottom circledata 0 , 1,  1, 2,  2, 3,  3, 4,  4, 5,  5, 6,  6, 7,  7, 8,  8, 9,  9,10, 10,11, 11,12, 12,13, 13,14, 14,15, 15, 0data 16,17, 17,18, 18,19, 19,20, 20,21, 21,22, 22,23, 23,24, 24,25, 25,26, 26,27, 27,28, 28,29, 29,30, 30,31, 31,16    'axis linesdata 39,40, 41,42, 43,44`
dafhi
Posts: 1238
Joined: Jun 04, 2005 9:51

Re: 3d without openGL

i'm kinda busy, but check this out

Code: Select all

`type p3d    as single x,y,zEnd Typeoperator *(l as single,r as p3d) as p3d: return type(r.x*l,r.y*l,r.z*l): end operatoroperator *(l as p3d,r as single) as p3d: return type(l.x*r,l.y*r,l.z*r): end operatoroperator *(l as p3d,r as p3d) as p3d: return type(l.x*r.x,l.y*r.y,l.z*r.z): end operatoroperator -(l as p3d,r as p3d) as p3d: return type(l.x-r.x,l.y-r.y,l.z-r.z): end operatoroperator +(l as p3d,r as p3d) as p3d: return type(l.x+r.x,l.y+r.y,l.z+r.z): end operatortype POINT3D    union      as p3d    ori      type      as single x    'absolute position      as single y      as single z      end type    end union    union      as p3d    ori_r      type      as single rx   'position after rotation      as single ry      as single rz      end type    end union    as ulong  c    'color of pixelend typevar w = 800, midx = w/2var h = 600, midy = h/2screenres w,hdim as point3d  pp.ori += type(midx, midy)pset (p.x, p.y)sleep`
fatman2021
Posts: 135
Joined: Dec 14, 2013 0:43

Re: 3d without openGL

BasicCoder2 wrote:This is where I am at so far.
The rocket seems to rotate around the x axis where ever it is due I think to the rotation routine dealing with that axis first?

Code: Select all

`'some useful definesConst Pi = 4 * Atn(1)Dim Shared As Double TwoPi = 8 * Atn(1)Dim Shared As Double RtoD = 180 / Pi   ' radians * RtoD = degreesDim Shared As Double DtoR = Pi / 180   ' degrees * DtoR = radiansconst SCRW = 800const SCRH = 600screenres SCRW,SCRH,32color rgb(0,0,0),rgb(255,255,255):clsdim shared as integer midX,midY  'origin position(0,0,0) position on screenmidX = SCRW\2midY = SCRH\2const PointCount = 44const LineCount  = 73type POINT3D    as single x    'absolute position    as single y    as single z    as single rx   'position after rotation    as single ry    as single rz    as ulong  c    'color of pixelend typetype GROUP    as POINT3D pt(0 to pointCount)  'set of 32 points    as single cx    'point of rotation in absolute space    as single cy    as single cz    as single ax   'rotation around axis cx,cy    as single ay    as single az    as ulong  c     'color of group's points    as single dx    'direction of movement along x and y coordinates    as single dy    as single dzend typetype ALINE    as integer pt1    as integer pt2    as ulong   cend typedim shared as GROUP rocket  'group of dots called rocket'initialize rocket pointsfor i as integer = 0 to pointCount    read rocket.pt(i).x    read rocket.pt(i).y    read rocket.pt(i).znext idim shared as ALINE aLine1(0 to lineCount)for i as integer = 0 to lineCount    read aLine1(i).pt1,aLine1(i).pt2  'read start and end points of line    aLine1(i).c = rgb(0,0,0)          'color the lines blacknext i'recolor axes linesaLine1(70).c = rgb(255,0,0)aLine1(71).c = rgb(0,255,0)aLine1(72).c = rgb(0,0,255)rocket.cx = 150rocket.cy = 150rocket.cz = 0rocket.ax  = 0  'rotation around axesrocket.ay  = 0rocket.az  = 0rocket.c  = rgb(200,100,25)  'color of grouprocket.dx = 1rocket.dy = 0sub drawAxes()    line (0,100)-(800,500),rgb(255,0,0) 'y axis    line (0,500)-(800,100),rgb(0,255,0) 'x axis    line (400,300)-(400,200),rgb(0,0,255) 'z axis    line (400,200)-(380,220),rgb(0,0,255) 'up arrow    line (400,200)-(420,220),rgb(0,0,255)end subsub rotatePoints()        dim as single cosAngleX,sinAngleX,angleX    dim as single cosAngleY,sinAngleY,angleY    dim as single cosAngleZ,sinAngleZ,angleZ        angleX    = rocket.ax*DtoR        cosAngleX = cos(angleX)    sinAngleX = sin(angleX)        angleY    = rocket.ay*DtoR        cosAngleY = cos(angleY)    sinAngleY = sin(angleY)        angleZ    = rocket.az*DtoR        cosAngleZ = cos(angleZ)    sinAngleZ = sin(angleZ)        '=========================================        dim as single newX,newY,newZ,x,y,z        for i as integer = 0 to pointCount                x = rocket.pt(i).x        y = rocket.pt(i).y        z = rocket.pt(i).z                '***Rotation on the X-axis        NewY = y*cosAngleX - z*sinAngleX        NewZ = z*cosAngleX + y*sinAngleX        y = NewY        z = NewZ        '***Rotation on the Y-axis        NewZ = z*cosAngleY - x*sinAngleY        NewX = x*cosAngleY + z*sinAngleY        x = NewX                '***Rotation on the Z-axis        NewX = x*cosAngleZ - y*sinAngleZ        NewY = y*cosAngleZ + x*sinAngleZ        rocket.pt(i).rx = NewX        rocket.pt(i).ry = NewY        rocket.pt(i).rz = NewZ        rocket.pt(i).c = rocket.pt(i).c            next i    end subsub drawScene()    dim as single x,y,z,x1,y1,z1,x2,y2,z2    z = 0    rotatePoints()    screenlock    cls    drawAxes()    locate 2,2    print " use the arrow keys and the Z or X key to rotate"    print " use WASD keys to translate along x and y axes"    print " hit space key to reset position"            x = rocket.cx + midX   'screen coordinates        y = rocket.cy + midY        circle ( x - y + 300, (x + y)/2 - z - 50),5,rocket.c,,,0.4,f                for i as integer = 0 to pointCount  'draw each point in each group                        'get x,y positions            x = rocket.pt(i).rx + rocket.cx + midX            y = rocket.pt(i).ry + rocket.cy + midY            z = rocket.pt(i).rz            'draw rotation point in isometric display            circle ( x - y + 300, (x + y)/2 - z - 50),2,rocket.pt(i).c,,,0.4,f                     next i        'draw lines        for i as integer = 0 to lineCount  'number of lines            x1 = rocket.pt(aLine1(i).pt1).rx + rocket.cx + midX            y1 = rocket.pt(aLine1(i).pt1).ry + rocket.cy + midY            z1 = rocket.pt(aLine1(i).pt1).rz            x2 = rocket.pt(aLine1(i).pt2).rx + rocket.cx + midX            y2 = rocket.pt(aLine1(i).pt2).ry + rocket.cy + midY            z2 = rocket.pt(aLine1(i).pt2).rz             line ( x1 - y1 + 300, (x1 + y1)/2 - z1 - 50)-( x2 - y2 + 300, (x2 + y2)/2 - z2 - 50)  ,aLine1(i).c        next i            screenunlockend subdim as string keydo    drawScene            rotatePoints()                if multikey(&H39) then  'space key to reset rocket variables            rocket.ax = 0            rocket.ay = 0            rocket.az = 0            rocket.cx = 150            rocket.cy = 150            rocket.cz = 0            while multikey(&H39):wend        end if                if multikey(&H1E) then            rocket.cx = rocket.cx - 1        end if                if multikey(&H20) then            rocket.cx = rocket.cx + 1        end if                if multikey(&H1F) then            rocket.cy = rocket.cy - 1        end if                if multikey(&H11) then             rocket.cy = rocket.cy + 1        end if                'rotate around x axis        if multikey(&H48) then            rocket.ax = rocket.ax + 1            if rocket.ax = 360 then rocket.ax = 0        end if        if multikey(&H50) then            rocket.ax = rocket.ax - 1            if rocket.ax < 0 then rocket.ax = 359        end if                'rotate around y axis        if multikey(&H4B) then            rocket.ay = rocket.ay + 1            if rocket.ay = 360 then rocket.ay = 0        end if        if multikey(&H4D) then            rocket.ay = rocket.ay - 1            if rocket.ay < 0 then rocket.ay = 359        end if                'rotate around z axis        if multikey(&H2C) then   'Z KEY            rocket.az = rocket.az + 1            if rocket.az = 360 then rocket.az = 0        end if        if multikey(&H2D) then   'X KEY            rocket.az = rocket.az - 1            if rocket.az < 0 then rocket.az = 359        end if'=======================================    sleep 2loop until multikey(&H01)'=========================== 3D POINT DATA ===================================' points for rocket cylinderdata   0,-24,-50,  10,-22,-50,  17,-17,-50,  22,-10,-50data  24,  0,-50,  22, 10,-50,  17, 17,-50,  10, 22,-50data   0, 24,-50, -10, 22,-50, -17, 17,-50, -22, 10,-50data -24,  0,-50, -22,-10,-50, -17,-17,-50, -10,-22,-50data   0,-24, 50,  10,-22, 50,  17,-17, 50,  22,-10, 50data  24,  0, 50,  22, 10, 50,  17, 17, 50,  10, 22, 50data   0, 24, 50, -10, 22, 50, -17, 17, 50, -22, 10, 50data -24,  0, 50, -22,-10, 50, -17,-17, 50, -10,-22, 50data 0,0,80  'nose cone point'rocket finsdata -44,0,-50, -24,0,-50, -24,0,-30data  44,0,-50,  24,0,-50,  24,0,-30'points for relative axisdata 100,0,0, -100,0,0  'x axisdata 0,100,0, 0,-100,0  'y axisdata 0,0,100, 0,0,-100  'z axis'=============================  LINE DATA ==================================' 16 lines joining pointsdata  0,16, 1,17,  2,18,  3,19,  4,20,  5,21,  6,22,  7,23data  8,24, 9,25, 10,26, 11,27, 12,28, 13,29, 14,30, 15,31' lines to cone tipdata  32,16, 32,17, 32,18, 32,19, 32,20, 32,21, 32,22, 32,23data  32,24, 32,25, 32,26, 32,27, 32,28, 32,29, 32,30, 32,31' lines for finsdata  33,34, 34,35, 35,33data  36,37, 37,38, 38,36'lines to draw circumference of top and bottom circledata 0 , 1,  1, 2,  2, 3,  3, 4,  4, 5,  5, 6,  6, 7,  7, 8,  8, 9,  9,10, 10,11, 11,12, 12,13, 13,14, 14,15, 15, 0data 16,17, 17,18, 18,19, 19,20, 20,21, 21,22, 22,23, 23,24, 24,25, 25,26, 26,27, 27,28, 28,29, 29,30, 30,31, 31,16    'axis linesdata 39,40, 41,42, 43,44`

Compiles and runs in DOSBOX until I press the X or Z keys 2 or 3 times. Then DOSBOX crashes(Segmentation fault (core dumped)).
BasicCoder2
Posts: 3349
Joined: Jan 01, 2009 7:03

Re: 3d without openGL

fatman2021 wrote:Compiles and runs in DOSBOX until I press the X or Z keys 2 or 3 times. Then DOSBOX crashes(Segmentation fault (core dumped)).

Sorry I can't help there as I know nothing about running code in DOSBOX.
BasicCoder2
Posts: 3349
Joined: Jan 01, 2009 7:03

Re: 3d without openGL

Slow progress but have been working my way through some 3D tutorials trying to resolve the 3D rotation about an arbitrary axis coding problem when all I have found is complex math only explanations I can't translate into actual FreeBasic code.

There are some good qbasic 3d tutorials but the code is in very old qbasic code that needs translating to FreeBasic.
http://www.petesqbsite.com/sections/tut ... hics.shtml

Code: Select all

`'''Code supplement for the rotation article'''This is supposed to show you how to use wireframes instead of points'''The filled cube is far from perfect as it used PAINT so I had to'''check a lower number for the z component of our projected coord.'''Pls be sure to check the code out as I will be explaing it in detail'''in the next issue of article. :*)'''SetvideoSeg by Plasma357'''Relsoft 2004'''Rel.Betterwebber.com'http://www.petesqbsite.com/sections/tutorials/tuts/relsoft3d/Chapter3/Chapter3.htm'http://www.petesqbsite.com/sections/tutorials/tuts/relsoft3d/Chapter3/Files/3DWIRE.BAS'Tweaked to compile with FreeBASIC by BasicCoder2TYPE Point3d        x       AS SINGLE                   'Normal 3d coords        y       AS SINGLE        z       AS SINGLE        xr      AS SINGLE                   'Rotated  3d coords        yr      AS SINGLE        zr      AS SINGLE        scrx    AS INTEGER                  'Translated and projected        scry    AS INTEGER                  '2d CoordsEND TYPETYPE PolyType        p1      AS INTEGER                  'vertex 1        p2      AS INTEGER        p3      AS INTEGER        clr     AS INTEGER                  'color        idx     AS INTEGER                  'index for later useEND TYPECONST LENS = 256                            'ZCONST XCENTER = 160                         '??CONST YCENTER = 100                         '??CONST PI = 3.14151693'Polyhedra stuffREDIM SHARED Model(1) AS Point3d               '3d  CoordsREDIM SHARED CubePoly(1) AS PolyType           '12 faces/PolysDIM   SHARED as single Thetax, Thetay, Thetaz           'Angle of rotationDIM   SHARED as single camx, camy, camz              'cameraDECLARE SUB DrawModelHIDEFace (Model() AS ANY, Tri() AS ANY)DECLARE SUB DrawModelFill (Model() AS ANY, Tri() AS ANY)DECLARE SUB DrawModel (Model() AS ANY, Tri() AS ANY)DECLARE SUB RotateAndProject (Model() AS ANY, AngleX as single, AngleY as single, AngleZ as single)DECLARE SUB LoadCube (Model() AS ANY, Tri() AS ANY, Scale as single)CLSSCREEN 0WIDTH 80'Initialize 3d modelLoadCube Model(), CubePoly(), 1dim as integer RendMode = 0LOCATE 1, 1PRINT "Choose Render Type:"PRINTPRINT "1. WireFrame"PRINT "2. WireFrame + BackFace Culling"PRINT "3. Filled"dim as string KK = INPUT(1)SELECT CASE ASC(K)        CASE 49            RendMode = 0        'Wireframe        CASE 50            RendMode = 1        'Back face culled        CASE 51            RendMode = 2        'Filled        CASE ELSE            RendMode = 0END SELECTCLSSCREEN 13RANDOMIZE TIMERcamx = 0camy = 0camz = 0Thetax = INT(RND * 360)Thetay = INT(RND * 360)Thetaz = INT(RND * 360)dim as integer Finished = 0DO     SELECT CASE UCASE(K)             'Camera control        CASE "A"            camz = camz + 1        CASE "Z"            camz = camz - 1        CASE "S"            camy = camy + 1        CASE "X"            camy = camy - 1        CASE "D"            camx = camx + 1        CASE "C"            camx = camx - 1    END SELECT                 Thetax = (Thetax + 1) MOD 360          'rotate our angles     Thetay = (Thetay + 1) MOD 360     Thetaz = (Thetaz + 1) MOD 360     RotateAndProject Model(), Thetax, Thetay, Thetaz  '12 muls     LINE (0, 0)-(319, 199), 0, BF  'CLS     SELECT CASE RendMode        CASE 0            DrawModel Model(), CubePoly()      'WireFrame        CASE 1            DrawModelHIDEFace Model(), CubePoly()      'Wire with BF culling        CASE 2            DrawModelFill Model(), CubePoly()      'Filled        CASE ELSE     END SELECT     SLEEPLOOP UNTIL MULTIKEY(&H01)'vertices of CubeCUBEDATA:DATA  50, 50, 50            : 'x,y,zDATA -50, 50, 50DATA -50,-50, 50DATA  50,-50, 50DATA  50, 50,-50DATA -50, 50,-50DATA -50,-50,-50DATA  50,-50,-50'Faces/Poly in 4 point formCUBECONNECT:DATA 1, 2, 3, 4DATA 2, 6, 7, 3DATA 6, 5, 8, 7DATA 5, 1, 4, 8DATA 5, 6, 2, 1DATA 4, 3, 7, 8SUB DrawModel (Model() AS Point3d, Tri() AS PolyType) STATICdim j as integerdim as single x1,x2,x3,y1,y2,y3FOR i as integer = 1 TO UBOUND(Tri)    j  = Tri(i).idx    x1 = Model(Tri(j).p1).scrx       'Get triangles from "projected"    x2 = Model(Tri(j).p2).scrx       'X and Y coords since Znormal    X3 = Model(Tri(j).p3).scrx       'Does not require a Z coord    y1 = Model(Tri(j).p1).scry       'V1= Point1 connected to V2 then    y2 = Model(Tri(j).p2).scry       'V2 to V3 and so on...    Y3 = Model(Tri(j).p3).scry    LINE (x1, y1)-(x2, y2), Tri(j).clr    LINE -(X3, Y3), Tri(j).clr    LINE -(x1, y1), Tri(j).clrNEXT iEND SUBSUB DrawModelFill (Model() AS Point3d, Tri() AS PolyType) STATICdim as integer jdim as single x1,x2,x3,y1,y2,y3,Znormal,xcen,ycen''Draws our cube hiding the facesFOR i as integer = 1 TO UBOUND(Tri)    j = Tri(i).idx    x1 = Model(Tri(j).p1).scrx       'Get triangles from "projected"    x2 = Model(Tri(j).p2).scrx       'X and Y coords since Znormal    X3 = Model(Tri(j).p3).scrx       'Does not require a Z coord    y1 = Model(Tri(j).p1).scry       'V1= Point1 connected to V2 then    y2 = Model(Tri(j).p2).scry       'V2 to V3 and so on...    Y3 = Model(Tri(j).p3).scry    Znormal = (x2 - x1) * (y1 - Y3) - (y2 - y1) * (x1 - X3)    IF (Znormal > 256) THEN              '<-256 so vector facing us                                        'cuz our z component is not                                        'normalized and paint would sometimes                                        'fail ;*)        LINE (x1, y1)-(x2, y2), Tri(j).clr        LINE -(X3, Y3), Tri(j).clr        LINE -(x1, y1), Tri(j).clr        xcen = (x1 + x2 + X3) \ 3        ycen = (y1 + y2 + Y3) \ 3        PAINT (xcen, ycen), Tri(j).clr    END IFNEXT iEND SUBSUB DrawModelHIDEFace (Model() AS Point3d, Tri() AS PolyType) STATIC''Draws our cube hiding the facesdim as integer jdim as single x1,x2,x3,y1,y2,y3,Znormal,xcen,ycenFOR i as integer = 1 TO UBOUND(Tri)    j  = Tri(i).idx    x1 = Model(Tri(j).p1).scrx       'Get triangles from "projected"    x2 = Model(Tri(j).p2).scrx       'X and Y coords since Znormal    X3 = Model(Tri(j).p3).scrx       'Does not require a Z coord    y1 = Model(Tri(j).p1).scry       'V1= Point1 connected to V2 then    y2 = Model(Tri(j).p2).scry       'V2 to V3 and so on...    Y3 = Model(Tri(j).p3).scry    Znormal = (x2 - x1) * (y1 - Y3) - (y2 - y1) * (x1 - X3)    IF (Znormal > 0) THEN               '<0 so vector facing us        LINE (x1, y1)-(x2, y2), Tri(j).clr        LINE -(X3, Y3), Tri(j).clr        LINE -(x1, y1), Tri(j).clr    END IFNEXT iEND SUBSUB LoadCube (Model() AS Point3d, Tri() AS PolyType, Scale as single)REDIM Model(1 TO 8) AS Point3d   '8 verticesRESTORE CUBEDATAdim as single x,y,zFOR i as integer = 1 TO 8    READ x, y, z    Model(i).x = x * Scale     'scale it according to    Model(i).y = y * Scale     'the parameter    Model(i).z = z * ScaleNEXT iREDIM Tri(1 TO 12) AS PolyTypedim as integer jdim as single p1,p2,p3,p4j = 1FOR i as integer = 1 TO 6    READ p1, p2, p3, p4    Tri(j).p1 = p1    Tri(j).p2 = p2    Tri(j).p3 = p4    Tri(j).idx = j    Tri(j).clr = 20 + INT(RND * 128)    j = j + 1    Tri(j).p1 = p2    Tri(j).p2 = p3    Tri(j).p3 = p4    Tri(j).idx = j    Tri(j).clr = 20 + INT(RND * 128)    j = j + 1NEXT iEND SUBSUB RotateAndProject (Model() AS Point3d, AngleX as single, AngleY as single, AngleZ as single) STATIC''Right handed system''when camera components increase:''x=goes left''y=goes down''z=goes into the screen' so when increased:'x =right'y=up'z=into you'          y'         |'         |'         |'         0-- -- -- '        /        x'      /'    /  z'  \/'''rotation: counter-clockwise of each axis''ei.  make yourself perpenicular to the axis''wave your hand from the center of your body to the left.''That's how it rotates. ;*)dim as single ax,ay,az,cx,sx,cy,sy,cz,sz'convert degrees to radiansax = AngleX * PI / 180ay = AngleY * PI / 180az = AngleZ * PI / 180'Precalculate the SIN and COS of each anglecx = COS(ax)sx = SIN(ax)cy = COS(ay)sy = SIN(ay)cz = COS(az)sz = SIN(az)'''After2 hours of work, I was able to weed out the constants from'''Rotate and project N to reduce my muls to 9 instead of 12. wootdim as single xx,xy,xz,yx,yy,yz,zx,zy,zzxx = cy * czxy = sx * sy * cz - cx * szxz = cx * sy * cz + sx * szyx = cy * szyy = cx * cz + sx * sy * szyz = -sx * cz + cx * sy * szzx = -syzy = sx * cyzz = cx * cyFOR i as integer = LBOUND(Model) TO UBOUND(Model)    dim as single x,y,z,RotX,RotY,RotZ,distance        x = Model(i).x        y = Model(i).y        z = Model(i).z        RotX = (x * xx + y * xy + z * xz) - camx        RotY = (x * yx + y * yy + z * yz) - camy        RotZ = (x * zx + y * zy + z * zz) - camz        Model(i).xr = RotX        Model(i).yr = RotY        Model(i).zr = RotZ        'Project        Distance = (LENS - RotZ)        IF Distance > 0 THEN            Model(i).scrx = XCENTER + (LENS * RotX / Distance)            Model(i).scry = YCENTER - (LENS * RotY / Distance)        ELSE        END IFNEXT iEND SUB`
paul doe
Posts: 913
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Hi, BasicCoder2
Slow progress but have been working my way through some 3D tutorials trying to resolve the 3D rotation about an arbitrary axis coding problem when all I have found is complex math only explanations I can't translate into actual FreeBasic code.

The problem you're having has to do with the way you are representing rotations. You are representing them in Euler angles (aka Yaw, Pitch and Roll), and with Euler you can only rotate about the origin of an absolute coordinate system (as can be seen in you example). For rotating about an arbitrary axis, you need to use vectors and matrices. This resource explains it very nicely:

http://www.codinglabs.net/article_world_view_projection_matrix.aspx

Also, you can check this slideshow, which compares different ways of representing rotations:

https://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf

The math can be daunting at first sight, but don't panic. We'll review it, and I'll show you how you can implement it in FreeBasic. I'm almost done. Until then, try to grasp at least those very basic concepts (vectors and matrices) so you'll have a better understanding of the code that I'm about to unleash ;-)

Regards,
Paul
paul doe
Posts: 913
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Say, I think that this little bit of code (in 2D) may help you understand the different coordinate spaces that you need to manage. The demo is a very simple shape, that rotates around an arbitrary point, and shows you how the coordinate spaces relate to each other. As much of the code will be used in the 3D demo anyway, here it is. Read the comments on the code and compare the matrices and transformations with the resources that I gave you. Perhaps it will make everything more clear.

platform.bi

Code: Select all

`#ifndef __platform__   #define __platform__      /'      platform specific definitions   '/   #ifdef __fb_64bit__      /'         64-bit definitions      '/      type int32 as long      type int64 as integer      type uint32 as ulong      type uint64 as integer      const as string fbVersion = "FreeBasic 64-bit"   #else      /'         32-bit definitions      '/      type int32 as integer      type int64 as longint      type uint32 as uinteger      type uint64 as ulongint      const as string fbVersion = "FreeBasic 32-bit"   #endif#endif`

core.bi

Code: Select all

`#ifndef __core__   #define __core__      #include once "platform.bi"      /'      as FB doesn't implements the class keyword, this is used to distinguish      between a class (object) and a type (data)   '/   #ifdef class      #undef class      #define class type   #endif      '' null for objects   #ifdef nothing      #undef nothing      #define nothing 0   #else      #define nothing 0   #endif      /'      again, FB doesn't have the 'inherits' keyword to differentiate interface      inheritance and implementation inheritance. This define is useful to remember      that            syntactically, they are the same. Conceptually, they are very different   '/   #ifdef inherits      #undef inherits      #define inherits extends   #else      #define inherits extends   #endif   /'      FB also don't distinguish between an interface class and a class implementation      this keyword is used for that sole purpose   '/   #ifdef interface      #undef interface      #define interface type   #else      #define interface type   #endif#endif`

math.bi

Code: Select all

`#ifndef __math__   #define __math__      /'      math, geometry and other useful numerical stuff            10/7/2017: removed all OpenGL-related stuff and the x86 assembler implementations of         floating-point modulus operator, and moved them to their own files (again).   '/         '' some convenience macros   #define max( a, b )                  iif( a > b, a, b )   #define min( a, b )                  iif( a < b, a, b )   #define clamp( mn, mx, v )      iif( v < mn, mn, iif( v > mx, mx, v ) )   #define wrap( wrapValue, v )   ( ( v ) + wrapValue ) mod wrapValue      '' useful constants   const as double pi = 4 * atn( 1 )   const as double twoPi = 2 * pi   const as double halfPi = pi / 2      const as double degToRad = pi / 180   const as double radToDeg = 180 / pi   const as double epsilon = 0.00000001      '' used to express angles in another unit   #define radians( ang )   ang * degToRad   #define degrees( ang )   ang * radToDeg      '' functions to return a delimited random value (uses FB implementation which   '' is a Mersenne Twister, can't remember the classification   declare function rndRange overload( byval as integer, byval as integer ) as integer   declare function rndRange( byval as single, byval as single ) as single   declare function rndRange( byval as double, byval as double ) as double      public function rndRange( byval mn as integer, byval mx as integer ) as integer     return int( rnd() * ( mx + 1 - mn ) + mn )   end function      public function rndRange( byval mn as single, byval mx as single ) as single     return rnd() * ( mx - mn ) + mn   end function   public function rndRange( byval mn as double, byval mx as double ) as double     return rnd() * ( mx - mn ) + mn   end function   #endif`

shape.bi

Code: Select all

`#ifndef __shape__   #define __shape__   /'      shapeEdge type definition            this type simply holds two vertices that define an edge of the shape      fairly straightforward   '/      #include once "core.bi"   #include once "vec2h.bi"   #include once "mat3.bi"   #include once "arrayList.bi"      type shapeEdge      public:         as vec2h ptr v1 = any         as vec2h ptr v2 = any            declare constructor()      declare constructor( byval p1 as vec2h ptr, byval p2 as vec2h ptr )      declare constructor( byref rhs as shapeEdge )            declare operator let( byref rhs as shapeEdge )    end type      constructor shapeEdge()      v1 = 0      v2 = 0   end constructor      constructor shapeEdge( byval p1 as vec2h ptr, byval p2 as vec2h ptr )      v1 = p1      v2 = p2   end constructor      constructor shapeEdge( byref e as shapeEdge )      v1 = e.v1      v2 = e.v2   end constructor      operator shapeEdge.let( byref rhs as shapeEdge )      v1 = rhs.v1      v2 = rhs.v2   end operator      /'      shape class            responsibility: this class represents a shape, defined by a collection of vertices and         the edges that connect them      mutability: mutable   '/   class shape      public:         declare constructor()         declare destructor()                  declare property vertexCount() as integer         declare property edgeCount() as integer                  declare function vertex( byval index as integer ) as vec2h ptr         declare function edge( byval index as integer ) as shapeEdge ptr                   declare sub addVertex( byval v as vec2h ptr )         declare sub addEdge( byval e as shapeEdge ptr )                  declare function transform() as mat3         declare sub transform( byval M as mat3 )                  declare sub render( byval c as uint32 = rgba( 255, 255, 255, 255 ) )                  declare sub computeBoundingBox()            private:         as arrayList   m_vertices '' list of vertices         as arraylist   m_edges '' list of edges         as mat3            m_transform = any '' the transformation matrix         as arrayList   m_boundingBox '' the list of vertices for the bounding box         as arrayList   m_boundingBoxEdges '' the list of edges for the bounding box   end class      constructor shape()      '' load an identity matrix in the transformation matrix when constructed      m_transform = matrices.identity2h()   end constructor      destructor shape()      /'         this step is necessary because my data structures (like the list)         don't do garbage collection      '/      '' delete vertices      for i as integer = 0 to m_vertices.count - 1         dim as vec2h ptr v = m_vertices.get( i )                  delete( v )      next            '' delete edges      for i as integer = 0 to m_edges.count - 1         dim as shapeEdge ptr e = m_edges.get( i )                  delete( e )      next i            '' delete bounding box      for i as integer = 0 to m_boundingBox.count - 1         dim as vec2h ptr e = m_boundingBox.get( i )                  delete( e )      next i   end destructor      property shape.vertexCount() as integer      return( m_vertices.count )   end property      property shape.edgeCount() as integer      return( m_edges.count )   end property      function shape.vertex( byval index as integer ) as vec2h ptr      return( m_vertices.get( index ) )   end function      function shape.edge( byval index as integer ) as shapeEdge ptr      return( m_edges.get( index ) )   end function      sub shape.addVertex( byval v as vec2h ptr )      m_vertices.add( v )   end sub      sub shape.addEdge( byval e as shapeEdge ptr )      m_edges.add( e )   end sub      function shape.transform() as mat3      return( m_transform )   end function      sub shape.transform( byval M as mat3 )      m_transform = M   end sub      sub shape.computeBoundingBox()      '' minimum and maximum positions of the bounding box      dim as vec2h tl = vec2h( -100000.0, -100000.0 )      dim as vec2h rb = vec2h( 100000.0, 100000.0 )            for i as integer = 0 to m_edges.count - 1         '' iterate through every edge         dim as shapeEdge ptr e = m_edges.get( i )                  dim as vec2h v1 = *( e->v1 )         dim as vec2h v2 = *( e->v2 )                  if( v1.x > tl.x ) then tl.x = v1.x         if( v1.y > tl.y ) then tl.y = v1.y                  if( v2.x < rb.x ) then rb.x = v2.x         if( v2.y < rb.y ) then rb.y = v2.y      next            '' create the 4 corners of the bounding box...      m_boundingBox.add( new vec2h( tl.x, tl.y ) )      m_boundingBox.add( new vec2h( tl.x, rb.y ) )      m_boundingBox.add( new vec2h( rb.x, rb.y ) )      m_boundingBox.add( new vec2h( rb.x, tl.y ) )            '' ...and connect them with edges      m_boundingBoxEdges.add( new shapeEdge( m_boundingBox.get( 0 ), m_boundingBox.get( 1 ) ) )      m_boundingBoxEdges.add( new shapeEdge( m_boundingBox.get( 1 ), m_boundingBox.get( 2 ) ) )      m_boundingBoxEdges.add( new shapeEdge( m_boundingBox.get( 2 ), m_boundingBox.get( 3 ) ) )      m_boundingBoxEdges.add( new shapeEdge( m_boundingBox.get( 3 ), m_boundingBox.get( 0 ) ) )   end sub      sub shape.render( byval c as uint32 = rgba( 255, 255, 255, 255 ) )      /'         render the shape                  the process is simple: transform each vertex with the provided         matrix (via multiplying), and use that transformed vector to         draw the shape      '/      for i as integer = 0 to m_edges.count - 1         '' transform both edge vectors first         dim as shapeEdge ptr e = m_edges.get( i )                  dim as vec2h v1 = vec2h( m_transform * ( *e->v1 ) )         dim as vec2h v2 = vec2h( m_transform * ( *e->v2 ) )                            '' and then draw the edge         line( v1.x, v1.y ) - ( v2.x, v2.y ), c       next            '' if the bounding box has been computed, draw it      if( m_boundingBox.count ) > 0 then         for i as integer = 0 to m_boundingBoxEdges.count - 1            '' transform both edge vertices first            dim as shapeEdge ptr e = m_boundingBoxEdges.get( i )                        dim as vec2h v1 = vec2h( m_transform * ( *e->v1 ) )            dim as vec2h v2 = vec2h( m_transform * ( *e->v2 ) )                                  '' and then draw the edge            line( v1.x, v1.y ) - ( v2.x, v2.y ), rgba( 128, 128, 128, 255 )          next      end if   end sub#endif`

arrayList.bi

Code: Select all

`#ifndef __arrayList__   #define __arrayList__      #include once "platform.bi"   #include once "core.bi"   #include once "math.bi"   #include once "crt.bi" '' needed for memcpy      /'      simple java-like ArrayList class            responsibility: represents a collection of objects, accessed and indexed         as an array      mutability: immutable            note that these classes aren't meant to do garbage collection,         merely to conveniently index and traverse a collection of objects      also the classes aren't optimized, as they are meant to be         used as a reference implementation (there's almost no error handling)   '/   class ArrayList extends object 'inherits ICollection      '' public interface      public:         declare constructor()                  declare function get( byval index as uint64 ) as any ptr         declare function count() as uint64         declare sub add( byval e as any ptr )         declare sub insert( byval e as any ptr, byval before as uint64 )         declare function remove( byval index as uint64 ) as any ptr         declare function remove( byval item as any ptr ) as any ptr         declare function removeLast() as any ptr         declare function removeFirst() as any ptr            '' state members      protected:         as any ptr   m_element( any )         as uint64      m_count = any                     /'         convenience macro for brevity         this macro resizes the m_element array by v elements, either positive or negative            if v is positive, the array expands            if v is negative, the array shrinks                  the code uses +1 and -1 for further clarify the meaning of the expression      '/      #macro resizeElements( v )         m_count += v :         redim preserve m_element( 0 to m_count - 1 )      #endmacro   end class      constructor ArrayList()      m_count = 0   end constructor      function ArrayList.count() as uint64      return( m_count )   end function      function ArrayList.get( byval index as uint64 ) as any ptr      /'         gets the element of the list indicated by index            note that there is no error checking on this one, for this can            mask out of bound errors         if the list tries to access an item out of bounds, the application deserves to         fail miserably      '/      return( m_element( index ) )   end function      sub ArrayList.add( byval e as any ptr )      resizeElements( +1 )            m_element( m_count - 1 ) = e   end sub      sub ArrayList.insert( byval e as any ptr, byval before as uint64 )      '' don't allow insertion out of bounds      before = min( m_count, max( 0, before ) )            '' trivial case, inserting at the end of the list      if( before = m_count - 1 ) then         resizeElements( +1 )                  m_element( m_count - 1 ) = m_element( m_count - 2 )         m_element( m_count - 2 ) = e      else         resizeElements( +1 )                  '' calculate number of elements to move         dim as uint64 elem = m_count - 1 - before         '' and move them to make space for the inserted item         memcpy( @m_element( before + 1 ), @m_element( before ), elem * sizeOf( any ptr ) )                  m_element( before ) = e         end if   end sub      function ArrayList.remove( byval item as any ptr ) as any ptr      '' removes an item from the list by pointer      dim as any ptr ret = 0      dim as int64 elem = -1 '' assume not found            '' search it (inefficiently)      for i as uint64 = 0 to m_count - 1         if( item = m_element( i ) ) then            '' found it            elem = i            exit for         end if      next            '' if found, return it, else return nothing      if( elem <> -1 ) then         return( remove( elem ) )      else         return( nothing )      end if   end function      function ArrayList.remove( byval index as uint64 ) as any ptr      '' removes an item from the list by index      '' don't allow removal out of bounds      index = min( m_count - 1, max( 0, index ) )      dim as any ptr ret = m_element( index )            if( index = m_count - 1 ) then      '' trivial removal, the last item         resizeElements( -1 )                  return( ret )      end if            '' only 2 elements to remove remaining      if( index = 0 and m_count < 3 ) then         m_element( 0 ) = m_element( 1 )                  resizeElements( -1 )                  return( ret )      end if            '' general case (elements > 2)      '' number of elements to move      dim as uint64 elem = m_count - 1 - index      '' move the rest of the elements       memcpy( @m_element( index ), @m_element( index + 1 ), elem * sizeOf( any ptr ) )            resizeElements( -1 )            return( ret )   end function      function ArrayList.removeLast() as any ptr      '' convenience function for removing the last element of the list      dim as any ptr ret = m_element( m_count - 1 )            resizeElements( -1 )            return( ret )   end function      function ArrayList.removeFirst() as any ptr      '' convenience function for removing the first element of the list      dim as any ptr ret = m_element( 0 )            '' there's only one element remaining      if( m_count = 1 ) then         m_count -= 1         redim m_element( 0 )                  return( ret )            end if            '' there's 2 elements remaining      if( m_count = 2 ) then         m_element( 0 ) = m_element( 1 )                  resizeElements( -1 )                  return( ret )      end if            '' general case      '' calculate number of elements to move      dim as uint64 elem = m_count - 1            '' move the remaining elements to their new position      memcpy( @m_element( 0 ), @m_element( 1 ), elem * sizeOf( any ptr ) )            '' and resize the member array      resizeElements( -1 )            return( ret )   end function      #endif/'   '' some code for unit testing   class test extends object      public:         as integer value = any                  declare constructor()         declare constructor( byval v as integer )         declare destructor()   end class      constructor test()      value = 0   end constructor      constructor test( byval v as integer )      value = v   end constructor      destructor test()      print "Destructor "; value; " called."   end destructor   dim as ArrayList al   dim as test ptr t   dim as integer numElements = 10   dim as any ptr what      for i as integer = 1 to numElements      dim as test ptr t = new test( i )      al.add( t )            if i = 5 then         what = t      end if   next      al.insert( new test( 343 ), al.count - 2 )      t = al.remove( 8798 )   delete t      t = al.removeLast()   delete t      t = al.remove( what )   delete t   '' show the elements   for i as integer = 0 to al.count - 1      t = al.get( i )            print t->value   next      '' delete all elements   do while al.count > 0      t = al.removeFirst            delete( t )      t = 0    loop      '' this code should never be executed if everything went ok   for i as integer = 0 to al.count - 1      t = al.get( i )            print t->value   next      sleep()'/`

mat3.bi

Code: Select all

`#ifndef __mat3__      #define __mat3__   #include once "vec2h.bi"   #include once "math.bi"   /'                             | a b c |      3x3 Matrix type      | d e f |                            | g h i |            note that this type also define operators to work with      2D homogeneous vectors            08/28/2017: fixed typo in multiplication of two matrices   '/      type mat3      public:         as double a, b, c         as double d, e, f         as double g, h, i            declare constructor()      declare constructor(_         byval na as double, byval nb as double, byval nc as double, _         byval nd as double, byval ne as double, byval nf as double, _         byval ng as double, byval nh as double, byval ni as double )            declare constructor( byref rhs as mat3 )      declare operator let( byref rhs as mat3 )            declare sub transpose()      declare function determinant() as double      declare sub inverse()      declare sub identity()            declare operator cast() as string   end type            constructor mat3() export      '' The default constructor creates an identity matrix, instead of a matrix of zeros      a = 1.0: b = 0.0: c = 0.0      d = 0.0: e = 1.0: f = 0.0      g = 0.0: h = 0.0: i = 1.0   end constructor      constructor mat3(   byval na as double, byval nb as double, byval nc as double, _                                    byval nd as double, byval ne as double, byval nf as double, _                                    byval ng as double, byval nh as double, byval ni as double ) export                                          a = na: b = nb: c = nc      d = nd: e = ne: f = nf      g = ng: h = nh: i = ni   end constructor      constructor mat3( byref rhs as mat3 ) export      '' copy constructor      a = rhs.a: b = rhs.b: c = rhs.c      d = rhs.d: e = rhs.e: f = rhs.f      g = rhs.g: h = rhs.h: i = rhs.i      end constructor      operator mat3.let( byref rhs as mat3 ) export      '' assignment constructor      a = rhs.a: b = rhs.b: c = rhs.c      d = rhs.d: e = rhs.e: f = rhs.f      g = rhs.g: h = rhs.h: i = rhs.i   end operator   sub mat3.transpose() export      /'         transpose:         | a b c |T    | a d g |         | d e f |  =  | b e h |         | g h i |     | c f i |      '/    b += d: d = b - d: b -= d '' swap b and d    c += g: g = c - g: c -= g '' swap c and g    f += h: h = f - h: f -= h '' swap f and h   end sub      function transpose( byval A as mat3 ) as mat3 export      A.transpose()            return( A )   end function      function mat3.determinant() as double export    '' computes the determinant of the matrix    dim as double det = _          ( a * e * i _          + b * f * g _         + d * h * c _         - g * e * c _         - d * b * i _         - h * f * a )            '' this is, of course, not matematically correct but it saves      '' some comprobations when you're calculating the inverse of      '' the matrix (and to avoid dividing by zero)      if det = 0 then det = 1            return( det )   end function      function determinant( byval A as mat3 ) as double export      return( A.determinant() )   end function      sub mat3.inverse() export      '' inverse of a 3x3 matrix      '' the inverse is the adjoint divided through the determinant      dim as double det = this.determinant()            '' if the determinant is 0, the matrix has no inverse      if det > 0 then         dim as double rDet = 1 / det '' The reciprocal of the determinant                  '' included in these calculations: minor, cofactor (changed signs), transpose          '' and division through determinant (expressed here by multiplying by the reciprocal)         dim as mat3 INV = mat3( _            (  e * i - h * f ) * rDet, ( -b * i + h * c ) * rDet, (  b * f - e * c ) * rDet, _            ( -d * i + g * f ) * rDet, (  a * i - g * c ) * rDet, ( -a * f + d * c ) * rDet, _            (  d * h - g * e ) * rDet, ( -a * h + g * b ) * rDet, (  a * e - d * b ) * rDet )                  '' set this matrix to the computed inverse         this = INV      end if   end sub      function inverse( byval A as mat3 ) as mat3 export      A.inverse()            return( A )   end function      sub mat3.identity() export      '' make the matrix an identity matrix      a = 1.0: b = 0.0: c = 0.0      d = 0.0: e = 1.0: f = 0.0      g = 0.0: h = 0.0: i = 1.0   end sub      operator mat3.cast() as string export      '' this is only to obtain a human readable representation of the matrix      return( _         "[" & trim( str( a ) ) & "][" & trim( str( b ) ) & "][" & trim( str( c ) ) & "]" & chr( 10 ) & chr( 13 ) & _         "[" & trim( str( d ) ) & "][" & trim( str( e ) ) & "][" & trim( str( f ) ) & "]" & chr( 10 ) & chr( 13 ) & _         "[" & trim( str( g ) ) & "][" & trim( str( h ) ) & "][" & trim( str( i ) ) & "]" )   end operator      operator + ( byref A as mat3, byref B as mat3 ) as mat3 export    '' adds two matrices    return( mat3( _         A.a + B.a, A.b + B.b, A.c + B.c, _         A.d + B.d, A.e + B.e, A.f + B.f, _         A.g + B.g, A.h + B.h, A.i + B.i ) )   end operator      operator - ( byref A as mat3, byref B as mat3 ) as mat3 export      '' substracts two matrices    return( mat3( _       A.a - B.a, A.b - B.b, A.c - B.c, _       A.d - B.d, A.e - B.e, A.f - B.f, _       A.g - B.g, A.h - B.h, A.i - B.i ) )   end operator      operator * ( byref A as mat3, byref s as double ) as mat3 export      '' multiply a matrix with a scalar    return( mat3( _         A.a * s, A.b * s, A.c * s, _         A.d * s, A.e * s, A.f * s, _         A.g * s, A.h * s, A.i * s ) )   end operator      operator * ( byref s as double, byref A as mat3 ) as mat3 export      '' multiply a matrix with a scalar        return( mat3( _       A.a * s, A.b * s, A.c * s, _       A.d * s, A.e * s, A.f * s, _       A.g * s, A.h * s, A.i * s ) )   end operator      operator * ( byref A as mat3, byref B as mat3 ) as mat3 export      '' multiply two matrices    return( mat3( _         A.a * B.a + A.b * B.d + A.c * B.g, A.a * B.b + A.b * B.e + A.c * B.h, A.a * B.c + A.b * B.f + A.c * B.i, _         A.d * B.a + A.e * B.d + A.f * B.g, A.d * B.b + A.e * B.e + A.f * B.h, A.d * B.c + A.e * B.f + A.f * B.i, _         A.g * B.a + A.h * B.d + A.i * B.g, A.g * B.b + A.h * B.e + A.i * B.h, A.g * B.c + A.h * B.f + A.i * B.i ) )   end operator      operator * ( byref A as mat3, byref v as vec2h ) as vec2h export      '' multiply a matrix with a column vector, resulting in a column vector      return( vec2h( A.a * v.x + A.b * v.y + A.c * v.w, A.d * v.x + A.e * v.y + A.f * v.w, A.g * v.x + A.h * v.y + A.i * v.w ) )     end operator      operator * ( byref v as vec2h, byref A as mat3 ) as vec2h export      '' multiply a vector with a row matrix, resulting in a row vector      return( vec2h( A.a * v.x + A.d * v.y + A.g * v.w, A.b * v.x + A.e * v.y + A.h * v.w, A.c * v.x + A.f * v.y + A.i * v.w ) )   end operator   operator / ( byref A as mat3, byref s as double ) as mat3 export      '' divide a matrix trough a scalar      return( mat3( _         A.a / s, A.b / s, A.c / s, _          A.d / s, A.e / s, A.f / s, _         A.g / s, A.h / s, A.i / s ) )   end operator      namespace matrices      function identity2h() as mat3         '' returns a 2D homogeneous identity matrix         return( mat3( _            1.0, 0.0, 0.0, _            0.0, 1.0, 0.0, _            0.0, 0.0, 1.0 ) )      end function                  function translation2h( byval tx as double, byval ty as double ) as mat3         '' returns a 2D homogeneous translation matrix         return( mat3( _            1.0, 0.0, tx, _            0.0, 1.0, ty, _            0.0, 0.0, 1.0 ) )      end function            function scaling2h( byval sx as double, byval sy as double ) as mat3         '' returns a 2D scaling matrix         return( mat3( _            sx,      0.0,   0.0, _            0.0,   sy,      0.0, _            0.0,   0.0,   1.0 ) )      end function            function shear2h( byval sx as double, byval sy as double ) as mat3         '' returns a shearig matrix by sx and sy angles in degrees         return( mat3( _            1.0,                            tan( radians( sx ) ), 0.0, _            tan( radians( sy ) ), 1.0,                            0.0, _            0.0,                            0.0,                            1.0 ) )         end function            function rotation2h( byval ang as double ) as mat3         '' returns a 2D rotation matrix around by ang degrees about the origin         dim as double co = cos( radians( ang ) )         dim as double si = sin( radians( ang ) )                  return( mat3( _            co   , -si   ,   0.0, _            si   , co   ,   0.0, _            0.0   ,   0.0   ,   1.0 ) )      end function         function rotationAxis2h( byval axis as vec2h, byval ang as double ) as mat3         '' returns a 2D rotation matrix around an arbitrary axis, by ang degrees         dim as double co = cos( radians( ang ) )         dim as double si = sin( radians( ang ) )         return( mat3( _            co, -si, -axis.x * co + axis.y * si + axis.x, _            si,  co, -axis.x * si - axis.y * co + axis.y, _            0.0, 0.0, 1.0 ) )      end function   end namespace#endif`

vec2h.bi

Code: Select all

`#ifndef __vec2h__   #define __vec2h__   /'      homogeneous 2D vector type            | x |      | y |      | 1 |         '/   #include once "core.bi"   #include once "math.bi"      type vec2h      public:         as double x         as double y         as double w                  declare constructor()         declare constructor( byval nx as double, byval ny as double, byval nw as double = 1.0 )         declare constructor( byref nv as vec2h )         declare operator let( byref rhs as vec2h )                           declare function dot( byref b as vec2h ) as double         declare function cross( byref b as vec2h ) as double         declare function length() as double         declare function squaredLength() as double         declare function unit() as vec2h         declare sub makeHomogeneous()         declare sub normalize()         declare sub turnLeft()         declare sub turnRight()         declare sub rotate( byval rAngle as double )         declare function angle() as double         declare sub setLength( byval l as double )                  declare operator cast() as string   end type      constructor vec2h() export      x = 0.0      y = 0.0      w = 1.0   end constructor      constructor vec2h( byval nx as double, byval ny as double, byval nw as double = 1.0 ) export      x = nx      y = ny      w = nw   end constructor      constructor vec2h( byref nv as vec2h ) export      x = nv.x      y = nv.y      w = nv.w   end constructor      operator vec2h.let( byref rhs as vec2h ) export      x = rhs.x      y = rhs.y      w = rhs.w   end operator      '' basic arithmetic operators   operator + ( byref lhs as vec2h, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs.x + rhs.x, lhs.y + rhs.y ) )   end operator      operator - ( byref lhs as vec2h, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs.x - rhs.x, lhs.y - rhs.y ) )   end operator      operator - ( byref lhs as vec2h ) as vec2h export      return( vec2h( -lhs.x, -lhs.y ) )   end operator      operator * ( byref lhs as vec2h, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs.x * rhs.x, lhs.y * rhs.y ) )   end operator      operator * ( byref lhs as vec2h, byref rhs as double ) as vec2h export      return( vec2h( lhs.x * rhs, lhs.y * rhs ) )   end operator      operator * ( byref lhs as double, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs * rhs.x, lhs * rhs.y ) )   end operator      operator * ( byref lhs as vec2h, byref rhs as integer ) as vec2h export      return( vec2h( lhs.x * rhs, lhs.y * rhs ) )   end operator      operator * ( byref lhs as integer, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs * rhs.x, lhs * rhs.y ) )   end operator      operator / ( byref lhs as vec2h, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs.x / rhs.x, lhs.y / rhs.y ) )   end operator      operator / ( byref lhs as vec2h, byref rhs as double ) as vec2h export      return( vec2h( lhs.x / rhs, lhs.y / rhs ) )   end operator      operator / ( byref lhs as double, byref rhs as vec2h ) as vec2h export      return( vec2h( lhs / rhs.x, lhs / rhs.y ) )   end operator      operator vec2h.cast() as string export      return(   "[" & str( this.x ) & "]" & chr( 10 ) & chr( 13 ) & _                  "[" & str( this.y ) & "]" & chr( 10 ) & chr( 13 ) & _                  "[" & str( w ) & "]" )   end operator      function vec2h.dot( byref b as vec2h ) as double export      '' returns the dot product of this vector with another vector b      return( x * b.x + y * b.y )    end function      function dot( byref v as vec2h, byref w as vec2h ) as double export      return( v.x * w.x + v.y * w.y )   end function      function vec2h.cross( byref b as vec2h ) as double export      /'         the cross product is not defined in 2d, so this function returns the z component         of the cross product of this vector with vector b, augmented to 3d      '/      return( x * b.y - y * b.x )   end function      function vec2h.length() as double export      '' returns the length of this vector      return( sqr( x * x + y * y ) )   end function      function vec2h.squaredLength() as double export      /'         returns the squared length of this vector         useful when one just want to compare two vectors to see which is longest,         as this avoids computing the square root      '/      return( x * x + y * y )   end function      function vec2h.unit() as vec2h export      '' return a normalized copy of this vector, without normalizing the vector itself      return vec2h( x, y ) / sqr( x * x + y * y )    end function      sub vec2h.normalize() export      '' normalizes this vector      dim as double l = sqr( x * x + y * y )            x /= l      y /= l   end sub      function normalize( byval v as vec2h ) as vec2h export      '' for compatibility      v.normalize()            return( v )   end function      /'      same with these two functions      turnLeft and turnRight rotates this vector 90 degrees to the left and right, while the      functions left and right return this vector rotated 90 degrees without actually rotating it   '/   sub vec2h.turnLeft() export      this = vec2h( y, -x )   end sub      sub vec2h.turnRight() export      this = vec2h( -y, x )   end sub      function turnLeft( byref v as vec2h ) as vec2h export      return( vec2h( v.y, -v.x ) )   end function   function turnRight( byref v as vec2h ) as vec2h export      return( vec2h( -v.y, v.x ) )   end function      sub vec2h.rotate( byval rAngle as double ) export      /'         rotates this vector by rAngle radians      '/            dim as double si = sin( rAngle )      dim as double co = cos( rAngle )            this = vec2h( x * co - y * si, x * si + y * co  )   end sub      function rotate( byref v as vec2h, byval rAngle as double ) as vec2h export      /'         returns the vector v, rotated by rAngle radians      '/      dim as double si = sin( rAngle )      dim as double co = cos( rAngle )            return( vec2h( v.x * co - v.y * si, v.x * si + v.y * co ) )             end function      function vec2h.angle() as double export      '' returns the angle that this vector points to, in radians            return( atan2( y, x ) )   end function      function vectorAngle overload( byval v as vec2h ) as double export      '' returns the angle that v vector points to, in radians            return atan2( v.y, v.x )   end function      function vectorAngle( byval v as vec2h, byval w as vec2h ) as double export      '' returns the angle between two vectors v and w            dim as double cosineOfAngle = dot( normalize( v ), normalize( w ) )            return( -acos( clamp( -1.0, 1.0, cosineOfAngle ) ) )   end function      sub vec2h.makeHomogeneous() export      '' homogeneize this vector (make the w component 1.0)            x /= w      y /= w      w /= w   end sub   sub vec2h.setLength( byval l as double )      '' sets the length of this vector      dim va as double = atan2( y, x )            x = l * cos( va )      y = l * sin( va )   end sub      function distance( byval a as vec2h, byval b as vec2h ) as double      '' returns the distance between two vectors      return( ( a - b ).length )   end function      function alignment( byval position as vec2h, byval size as vec2h, byval align as vec2h ) as vec2h      '' returns a vec2h aligned between position and position + size      '' align should be a normalized vector      return( position + size * align )   end function      function interpolate( byval p0 as vec2h, byval p1 as vec2h, byval t as double ) as vec2h      '' returns the interpolation point t (0..1) between p0 and p1       return( ( 1 - t ) * p0 + t * p1 )   end function#endif`

main.bas

Code: Select all

`#include once "platform.bi"#include once "core.bi"#include once "mat3.bi"#include once "vec2h.bi"#include once "shape.bi"#include once "fbgfx.bi" '' needed for the constants used by multiKey()/'   testing the shape and 2D vector and matrix classes      conventions used:      RIGHT HANDED coordinate system      positive angle increment is COUNTER-CLOCKWISE      vertices are specified COUNTER-CLOCKWISE      angles are specified in DEGREES'/dim as integer scrW = 800, scrH = 600screenRes( scrW, scrH, 32 )windowTitle( fbVersion & " - 2D vector and matrix example" )/'   this code defines a very simple 2D shape   note that all points are specified relative to the   ( 0.0, 0.0 ) coordinate.      this is otherwise known as OBJECT SPACE'/dim as shape shwith sh   .addVertex( new vec2h( -20, -10 ) )   .addVertex( new vec2h(  20, -10 ) )   .addVertex( new vec2h(   0,  30 ) )      .addEdge( new shapeEdge( sh.vertex( 0 ), sh.vertex( 1 ) ) )   .addEdge( new shapeEdge( sh.vertex( 1 ), sh.vertex( 2 ) ) )   .addEdge( new shapeEdge( sh.vertex( 2 ), sh.vertex( 0 ) ) )      '' when the object is finished, compute its bounding box   .computeBoundingBox()   end with/'   define the main projection matrix (the PROJECTION SPACE coordinate system)   note that we map the Y axis to increase upwards, and the center of the   coordinate system to the center of the screen'/dim as mat3 P = mat3( _   1.0,  0.0, scrW / 2, _   0.0, -1.0, scrH / 2, _   0.0,  0.0, 1.0 )'' angle of rotation of the shape, in degreesdim as single ang = 0.0'' axis of rotation in object coordinatesdim as vec2h axis = vec2h( 0.0, 100.0 )'' position of the object in world coordinatesdim as vec2h position = vec2h( 50.0, 50.0 )'' begin main loopdo      if multiKey( fb.sc_up ) then      '' up      position.y += 1   end if      if multiKey( fb.sc_down ) then      '' down      position.y -= 1   end if   if multiKey( fb.sc_left ) then      '' left      position.x -= 1   end if   if multiKey( fb.sc_right ) then      '' right      position.x += 1   end if      /'      lets compose some transformations            as a general rule, each transformation matrix must be recomputed every      time it changes      here, the translation and rotations can change depending on which      keys do you press, so we recompose them   '/      '' translation   dim as mat3 T = matrices.translation2h( position.x, position.y )   '' rotation about an arbitrary axis   dim as mat3 RA = matrices.rotationAxis2h( axis, ang )      /'      if you use column vectors and matrices (as I do), you have to specify      the last transformation FIRST, like this            P = projection matrix      T = translation matrix      RA = rotation about an arbitrary axis            and compose them by multiplying. Matrix multiplication is NOT      commutative, so the order IS important!!   '/      '' compose the transformation (first rotate, then translate, then project)   dim as mat3 transformation = P * T * RA      '' and then pass it to the shape   sh.transform( transformation )      '' render the screen   screenLock()      cls()            '' show the center of screen (the center of the coordinate system)       line( scrW / 2, 0 ) - ( scrW / 2, scrH - 1 ), rgba( 64, 64, 64, 255 )      line( 0, scrH / 2 ) - ( scrW - 1, scrH / 2 ), rgba( 64, 64, 64, 255 )            '' render the shape (yellow)      sh.render( rgba( 255, 255, 0, 255 ) )            /'         this bit of code shows you the axis used to rotate the shape,         in OBJECT space      '/      dim as vec2h tAxis = transformation * axis      circle( tAxis.x, tAxis.y ), 3, rgba( 0, 255, 0, 255 ), , , , f      /'         this bit shows you the origin of the shape, again, in OBJECT         space      '/      dim as vec2h tOrigin = transformation * vec2h( 0.0, 0.0 )      circle( tOrigin.x, tOrigin.y ), 3, rgba( 0, 0, 255, 255 ), , , , f            /'         and this bit shows you the position of the shape in WORLD         coordinates. Note that you don't have to transform, only         project them      '/      dim as vec2h tPos = P * position      circle( tPos.x, tPos.y ), 3, rgba( 255, 0, 0, 255 ), , , , f   screenUnlock()      '' increment the angle the object rotates   ang += 0.1   if ang > 360.0 then ang -= 360.0      '' spare some time, always   sleep( 1 )loop until multiKey( fb.sc_escape )`

Simply save each file with it's corresponding name in a folder of your choice and run 'main.bas'. Use the arrow keys to move the shape. Meanwhile, the shape will be rotating around an axis that you can change (see the code). Experiment a little and see how it's done in 2D, before we jump to 3D. It's a little easier to grasp ;-)

See ya
Paul
owen
Posts: 512
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: 3d without openGL

here's what i was working on back in 2013

Code: Select all

`'note: my arrays usually do not use sub script zero'when i dim a(4) i use a(1),a(2),a(3),a(4)... a(0) is not used'good example is a rectangel has for points, point 1,2,3,4 and'i usually refer to these points in my arrays as a(1-4) not a(0-3)'secondly, even though i dim a(4), knowing a(0-5) are allocated in memory'i find it convenient to use a(0) to temporarily store the value of (1-4)'starting in the top left corner cuz we read (left to right)'but in a counter clockwise manner cuz my code figures angles increase this way'a(1) is top left corner'a(2) is bottom left corner'a(3) is bottom right corner'a(4) is top right corner'note:'a(5) would be center of rectangle'a(6) would be width of rectangle'a(7) would be height of rectangle'for a cube'a(1-4) are the front face corners'a(5-8) are the back face corners'a(6) would be center of the cube'a(7-9) would be the width,height,depth'rotation is counter clockwise 0 degrees is at 3 O'clock - 90 is @ 12 - 180 is @ 9 - 270 is @ 6'axis xy from front view'axis xz from bottom view'axis yz from right viewType point2d   x As Double   y As DoubleEnd TypeType point2dgamma   x As Double   y As Double   g As Double'gamma is ratioEnd TypeType point3d   x As Double   y As Double   z As DoubleEnd TypeType cube2dgamma   p(8) As point2dgammaEnd TypeType axes2dgamma   p(6) As point2dgammaEnd TypeType rotation3daxes   xy As Double   xz As Double   yz As DoubleEnd TypeType camera   pos3d      As point3d   rot3d      As rotation3daxes   stationary As Byte   visible    As Byte   lensdepth  As Integer'this adjusts FOVEnd TypeType lines   lstart As point3d   lend As point3dEnd TypeType line2dgamma   p(2) As point2dgammaEnd TypeDeclare Function rotate3dpoint(rotation_order As Integer,pivot As point3d, p As point3d, angle As rotation3daxes) As point3dDeclare Function proj3d(p3d As point3d,cam As camera) As point2dgammaDeclare Function abtp(abtpx1 as Double,abtpy1 as Double,abtpx2 as Double,abtpy2 as Double) as DoubleDeclare Function mymod(n As Double,m As Integer) As DoubleDeclare Sub drawonlens()Const Pi = Atn(1) * 4Dim As Integer _'iteratorsi, _j, _k'Dim Shared As Byte _'logic'TRUE, _'FALSE'TRUE=-1'FALSE=Not TRUEDim Shared As Integer _mousex, _mousey, _mouseb, _mousew, _mousewpDim Shared tempd As DoubleDim Shared world_origin As point3dworld_origin.x=0world_origin.y=0world_origin.z=0Dim pivotpoint3d As point3dpivotpoint3d.x=0pivotpoint3d.y=0pivotpoint3d.z=0Dim Shared vertex3d() As point3dReDim Preserve vertex3d(16)For i = 1 To 14   Read vertex3d(i).x   Read vertex3d(i).y   Read vertex3d(i).zNext'Dim rotaxis As rotation3daxes'rotaxis.xy=90'rotaxis.xz=0'rotaxis.yz=90'4 cameras'cam 1 stationary, front view'cam 2 stationary, right side view'cam 3 stationary, top view'cam 4 can manuver, default isometric view (up/right/front)Dim Shared cam(4) As cameracam(1).lensdepth=256'front viewcam(1).pos3d.x=0cam(1).pos3d.y=0cam(1).pos3d.z=512cam(1).rot3d.xy=0cam(1).rot3d.xz=0cam(1).rot3d.yz=0'right side view'cam(1).pos3d.x=512'cam(1).pos3d.y=0'cam(1).pos3d.z=0'cam(1).rot3d.xy=0'cam(1).rot3d.xz=90'cam(1).rot3d.yz=0'right side view-2'cam(1).pos3d.x=-512'cam(1).pos3d.y=0'cam(1).pos3d.z=0'cam(1).rot3d.xy=0'cam(1).rot3d.xz=270'cam(1).rot3d.yz=0'top view'cam(1).pos3d.x=0'cam(1).pos3d.y=512'cam(1).pos3d.z=0'cam(1).rot3d.xy=0'cam(1).rot3d.xz=0'cam(1).rot3d.yz=270'bottom view'cam(1).pos3d.x=0'cam(1).pos3d.y=-512'cam(1).pos3d.z=0'cam(1).rot3d.xy=0'cam(1).rot3d.xz=0'cam(1).rot3d.yz=90'1 is the top left corner of the lenscam(1).stationary=TRUE'camera can not be repositionedcam(1).visible=TRUE'camer can be seen and displayed by other cameras'mousex=wx1+(wx2-wx1)*(mousex/(drawareax2-drawareax1))Dim Shared As Integer wx1,wy1,wx2,wy2,wsize,scrnresscrnres=256wsize=256wx1=wsize/2*-1wy1=wsize/2*-1wx2=wsize/2wy2=wsize/2ScreenRes scrnres,scrnres,8,2ScreenSet(1,1)View (0,0)-(scrnres-1,scrnres-1)'drawing areaWindow (wx1,wy1)-(wx2,wy2)Dim tempv As point3dDim temppivot As point3dDim tempvup As point3dDim tempvdn As point3dDim tempvlt As point3dDim tempvrt As point3dDim tempvfwd As point3dDim tempvrev As point3dDim tempangle As DoubleDim tempangleup As DoubleDim tempangledn As DoubleDim tempanglelt As DoubleDim tempanglert As DoubleDim tempanglefwd As DoubleDim tempanglerev As DoubleDim temprot As rotation3daxestemprot.xy=0temprot.xz=0temprot.yz=0Dim scube As cube2dgamma 'my spinning cubeDim axes As axes2dgamma' my axesDim axesrot As rotation3daxesaxesrot.xy=0axesrot.xz=0axesrot.yz=0Dim As Integer tiDim As String hti=1Dim neg As ByteDim Shared As Integer rotationorderrotationorder=5Dim As Byte spincubexy,spincubexz,spincubeyzDim As Byte spinaxisxy,spinaxisxz,spinaxisyzspincubexy=FALSEspincubexz=FALSEspincubeyz=FALSEspinaxisxy=FALSEspinaxisxz=FALSEspinaxisyz=FALSEDim As Byte userotorderuserotorder=TRUEDim Shared line3d() As linesReDim Preserve line3d(1)Dim lineg As line2dgammaDim shared linecount As Integerlinecount=0Dim Shared As Byte leftmousebdown,rightmousebdownleftmousebdown=FALSErightmousebdown=FALSEDim Shared As Integer mouseclicksmouseclicks=0Dim Shared drawingz As IntegerDim Shared nm As Integernm=256Do   For i=0 To 359      ScreenCopy 0,1      If cam(1).rot3d.xz<0 Then neg=TRUE Else neg = FALSE      cam(1).rot3d.xy=mymod(cam(1).rot3d.xy,360)      cam(1).rot3d.xz=mymod(cam(1).rot3d.xz,360)      cam(1).rot3d.yz=mymod(cam(1).rot3d.yz,360)      If cam(1).rot3d.xy<0 Then cam(1).rot3d.xy=360+cam(1).rot3d.xy      If cam(1).rot3d.xz<0 Then cam(1).rot3d.xz=360+cam(1).rot3d.xz      If cam(1).rot3d.yz<0 Then cam(1).rot3d.yz=360+cam(1).rot3d.yz      Locate 29,1      If userotorder=FALSE Then         Print "simulated axes - 6 rot orders   ";      Else         Print "simulated axes rotation order";rotationorder;      EndIf      Locate 30,1      Print "cam using rotation order";rotationorder;      Locate 1,1      Print "camrot axis xy ";cam(1).rot3d.xy      Print "camrot axis xz ";cam(1).rot3d.xz      Print "camrot axis yz ";cam(1).rot3d.yz      Print "cam x pos ";cam(1).pos3d.x      Print "cam y pos ";cam(1).pos3d.y      Print "cam z pos ";cam(1).pos3d.z      Print "sim axis xy ";axesrot.xy      Print "sim axis xz ";axesrot.xz      Print "sim axis yz ";axesrot.yz      Print "cam lens depth ";cam(1).lensdepth      Print "drawing depth ";drawingz      Print "nm=";nm      drawonlens      If spincubexy=TRUE Then temprot.xy=i Else temprot.xy=0      If spincubexz=TRUE Then temprot.xz=i Else temprot.xz=0      If spincubeyz=TRUE Then temprot.yz=i Else temprot.yz=0      If spinaxisxy=TRUE Then axesrot.xy=i' Else axesrot.xy=0      If spinaxisxz=TRUE Then axesrot.xz=i' Else axesrot.xz=0      If spinaxisyz=TRUE Then axesrot.yz=i' Else axesrot.yz=0      'spin cube      For j = 1 To 8         tempv=vertex3d(j)         tempv=rotate3dpoint(rotationorder,world_origin, tempv, temprot)         scube.p(j)=proj3d(tempv,cam(1))      Next      If scube.p(1).g<>0 Then         Circle (scube.p(1).x,scube.p(1).y),10*scube.p(1).g,15         If scube.p(2).g<>0 Then            Line(scube.p(1).x,scube.p(1).y)-(scube.p(2).x,scube.p(2).y),13         EndIf         If scube.p(4).g<>0 Then            Line(scube.p(1).x,scube.p(1).y)-(scube.p(4).x,scube.p(4).y),13         EndIf         If scube.p(5).g<>0 Then            Line(scube.p(1).x,scube.p(1).y)-(scube.p(5).x,scube.p(5).y),15         EndIf      End If      If scube.p(2).g<>0 Then         If scube.p(3).g<>0 Then            Line(scube.p(2).x,scube.p(2).y)-(scube.p(3).x,scube.p(3).y),13         EndIf         If scube.p(6).g<>0 Then            Line(scube.p(2).x,scube.p(2).y)-(scube.p(6).x,scube.p(6).y),15         EndIf      EndIf      If scube.p(3).g<>0 Then         If scube.p(4).g<>0 Then            Line(scube.p(3).x,scube.p(3).y)-(scube.p(4).x,scube.p(4).y),13         EndIf         If scube.p(7).g<>0 Then            Line(scube.p(3).x,scube.p(3).y)-(scube.p(7).x,scube.p(7).y),15         EndIf      EndIf      If scube.p(4).g<>0 And scube.p(8).g<>0 Then         Line(scube.p(4).x,scube.p(4).y)-(scube.p(8).x,scube.p(8).y),15      EndIf      If scube.p(5).g<>0 Then         If scube.p(6).g<>0 Then            Line(scube.p(5).x,scube.p(5).y)-(scube.p(6).x,scube.p(6).y),14         EndIf         If scube.p(8).g<>0 Then            Line(scube.p(5).x,scube.p(5).y)-(scube.p(8).x,scube.p(8).y),14         EndIf      EndIf      If scube.p(6).g<>0 And scube.p(7).g<>0 Then         Line(scube.p(6).x,scube.p(6).y)-(scube.p(7).x,scube.p(7).y),14      EndIf      If scube.p(7).g<>0 And scube.p(8).g<>0 Then         Line(scube.p(7).x,scube.p(7).y)-(scube.p(8).x,scube.p(8).y),14      EndIf      For j=1 To 6         tempv=vertex3d(j+8)         If userotorder=TRUE Then            tempv=rotate3dpoint(rotationorder,world_origin, tempv, axesrot)         Else            tempv=rotate3dpoint(j,world_origin, tempv, axesrot)         End If         axes.p(j)=proj3d(tempv,cam(1))      Next      For j=2 To 6 Step 2         Circle (axes.p(j).x,axes.p(j).y),10*axes.p(j).g,15      Next      If axes.p(1).g<>0 And axes.p(2).g<>0 Then         Line(axes.p(1).x,axes.p(1).y)-(axes.p(2).x,axes.p(2).y),10      EndIf      If axes.p(3).g<>0 And axes.p(4).g<>0 Then         Line(axes.p(3).x,axes.p(3).y)-(axes.p(4).x,axes.p(4).y),11      EndIf      If axes.p(5).g<>0 And axes.p(6).g<>0 Then         Line(axes.p(5).x,axes.p(5).y)-(axes.p(6).x,axes.p(6).y),12      EndIf      For j = 1 To linecount         tempv=line3d(j).lstart         lineg.p(1)=proj3d(tempv,cam(1))         tempv=line3d(j).lend         lineg.p(2)=proj3d(tempv,cam(1))         If lineg.p(1).g<>0 And lineg.p(2).g<>0 Then            Line(lineg.p(1).x,lineg.p(1).y)-(lineg.p(2).x,lineg.p(2).y)         EndIf      Next                              tempv=cam(1).pos3d      h=InKey      Select Case h         Case Chr(27)            Exit Do         Case "+"            wsize=wsize-4            If wsize=0 Then wsize=4            wx1=wsize/2*-1            wy1=wsize/2*-1            wx2=wsize/2            wy2=wsize/2            Window (wx1,wy1)-(wx2,wy2)         Case "-"            wsize=wsize+4            wx1=wsize/2*-1            wy1=wsize/2*-1            wx2=wsize/2            wy2=wsize/2            Window (wx1,wy1)-(wx2,wy2)         Case "d"            drawingz=drawingz+1         Case "c"            drawingz=drawingz-1            'If drawingz<0 Then drawingz=0         Case "a"            cam(1).lensdepth=cam(1).lensdepth+1         Case "z"            cam(1).lensdepth=cam(1).lensdepth-1            If cam(1).lensdepth< 1 Then cam(1).lensdepth=1         Case "q"            If userotorder=FALSE Then userotorder=TRUE Else userotorder=FALSE         Case "Q"            axesrot.xy=0            axesrot.xz=0            axesrot.yz=0         Case "r","R"            rotationorder=rotationorder+1            If rotationorder=7 Then rotationorder=1         Case "1"            If spincubexy=FALSE Then               spincubexy=TRUE            Else               spincubexy=FALSE            EndIf         Case "2"            If spincubexz=FALSE Then               spincubexz=TRUE            Else               spincubexz=FALSE            EndIf         Case "3"            If spincubeyz=FALSE Then               spincubeyz=TRUE            Else               spincubeyz=FALSE            EndIf         Case "!"            If spinaxisxy=FALSE Then               spinaxisxy=TRUE            Else               spinaxisxy=FALSE            EndIf         Case "@"            If spinaxisxz=FALSE Then               spinaxisxz=TRUE            Else               spinaxisxz=FALSE            EndIf         Case "#"            If spinaxisyz=FALSE Then               spinaxisyz=TRUE            Else               spinaxisyz=FALSE            EndIf         Case Chr(255)+"H"'up            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera up            tempv.y=cam(1).pos3d.y +10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case Chr(255)+"P"'dn            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera down            tempv.y=cam(1).pos3d.y -10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case Chr(255)+"K"'lt            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera left            tempv.x=cam(1).pos3d.x -10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case Chr(255)+"M"'rt            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera right            tempv.x=cam(1).pos3d.x +10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case "w"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera forward            tempv.z=cam(1).pos3d.z +10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case "s"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively moving the camera backwards            tempv.z=cam(1).pos3d.z -10            cam(1).pos3d=rotate3dpoint(rotationorder,cam(1).pos3d, tempv, cam(1).rot3d)         Case "y"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively rolling the camera clock wise            cam(1).rot3d.xy=cam(1).rot3d.xy+1         Case "Y"            axesrot.xy=Int(axesrot.xy)+1         Case "h"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively rolling the camera counter clock wise            cam(1).rot3d.xy=cam(1).rot3d.xy-1         Case "H"            axesrot.xy=Int(axesrot.xy)-1         Case "u"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively yawing the camera's nose to the left            cam(1).rot3d.xz=cam(1).rot3d.xz+1         Case "U"            axesrot.xz=Int(axesrot.xz)+1         Case "j"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively yawing the camera's nose to the right            cam(1).rot3d.xz=cam(1).rot3d.xz-1         Case "J"            axesrot.xz=Int(axesrot.xz)-1         Case "i"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively pitching the camera's nose up            cam(1).rot3d.yz=cam(1).rot3d.yz+1         Case "I"            axesrot.yz=Int(axesrot.yz)+1         Case "k"            'according to camera's current rotation on the 3 axes            'adjust cam.x,y,z effectively pitching the camera's nose down            cam(1).rot3d.yz=cam(1).rot3d.yz-1         Case "K"            axesrot.yz=Int(axesrot.yz)-1         Case Chr(27), Chr(255) + "k"            Exit Do         Case Else      End Select      GetMouse mousex,mousey,mousew,mouseb      Sleep 1      NextLoopEndFunction rotate3dpoint(rotation_order As Integer,pivot As point3d, p As point3d, angle As rotation3daxes) As point3d   Dim As Double _   dx,           _   dy,           _   dz,           _   anglexy,      _   anglexz,      _   angleyz      dx=p.x-pivot.x   dy=p.y-pivot.y   dz=p.z-pivot.z   anglexy=angle.xy*pi/180   anglexz=angle.xz*pi/180   angleyz=angle.yz*pi/180   'starting off from the front or right views and yawing   'rotation order #3 works when rotation on xz axis 0 to 359 (yaw)   'ie. rotating xy axis makes it roll   '    rotating yz axis makes it pitch   'starting off from the top view   'rotation 5 works    'Locate 31,20   Select Case rotation_order      Case 1         'Print "xy - xz - yz";         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)         dx=p.x         dy=p.y         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)         dz=p.z         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)      Case 2         'Print "xy - yz - xz";         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)         dx=p.x         dy=p.y         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)         dz=p.z         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)      Case 3         'Print "xz - xy - yz";         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)         dx=p.x         dz=p.z         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)         dy=p.y         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)      Case 4         'Print "xz - yz - xy";         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)         dx=p.x         dz=p.z         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)         dy=p.y         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)      Case 5         'Print "yz - xy - xz";         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)         dy=p.y         dz=p.z         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)         dx=p.x         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)      Case 6         'Print "yz - xz - xy";         p.y=dy*Cos(angleyz) - dz*Sin(angleyz)         p.z=dz*Cos(angleyz) + dy*Sin(angleyz)         dy=p.y         dz=p.z         p.x=dx*Cos(anglexz) + dz*Sin(anglexz)         p.z=dz*Cos(anglexz) - dx*Sin(anglexz)         dx=p.x         p.x=dx*Cos(anglexy) - dy*Sin(anglexy)         p.y=dy*Cos(anglexy) + dx*Sin(anglexy)   End Select      p.x=p.x+pivot.x   p.y=p.y+pivot.y   p.z=p.z+pivot.z   Return pEnd Function'project_3d_point_to_2d_camera_lens_to_screen_xy_coordinates_with_gammaFunction proj3d(   _   p3d As point3d, _   cam As camera   _   ) As point2dgamma   Dim As Integer camlensdepth   camlensdepth=cam.lensdepth   Dim p2dg As point2dgamma   Dim As Double _   z_distance,   _   z_ratio   Dim As point3d _   tp,            _'temp_point   tc              'temp_camera_position   Dim trot As rotation3daxes   Dim reversero As Integer   Select Case rotationorder      Case 1         reversero=6      Case 2         reversero=4      Case 3         reversero=5      Case 4         reversero=2      Case 5         reversero=3      Case 6         reversero=1   End Select   tp=p3d   trot.xy=cam.rot3d.xy*-1   trot.xz=cam.rot3d.xz*-1   trot.yz=cam.rot3d.yz*-1   tp=rotate3dpoint(reversero,cam.pos3d, p3d, trot)   tc=cam.pos3d   tp.x=tp.x-tc.x   tp.y=tp.y-tc.y   z_distance=tc.z-tp.z   If z_distance=0 Then z_distance=1   tempd=z_distance   z_ratio=camlensdepth/z_distance   p2dg.x=tp.x*z_ratio   p2dg.y=tp.y*z_ratio   If tp.z>=tc.z Then      p2dg.g=0   Else      p2dg.g=z_ratio   EndIf   Return p2dgEnd FunctionSub drawonlens()   Dim As point3d mouse3d, scrnc3d, tempv   Dim As point2dgamma mouse2d, scrnc2d   'scrnc3d.x=0+cam(1).pos3d.x   'scrnc3d.y=0+cam(1).pos3d.y   'scrnc3d.z=cam(1).pos3d.z-256   'scrnc3d=rotate3dpoint(rotationorder,cam(1).pos3d,scrnc3d,cam(1).rot3d)   'Print Int(scrnc3d.x);Int(scrnc3d.y);Int(scrnc3d.z)   mousex=wx1+(wx2-wx1)*(mousex/scrnres)   mousey=(wy1+(wy2-wy1)*(mousey/scrnres))*-1   mouse3d.x=mousex*(256/cam(1).lensdepth)*((256-drawingz)/256)+cam(1).pos3d.x'*(drawingz/128)   mouse3d.y=mousey*(256/cam(1).lensdepth)*((256-drawingz)/256)+cam(1).pos3d.y   mouse3d.z=cam(1).pos3d.z-256+drawingz   mouse3d=rotate3dpoint(rotationorder,cam(1).pos3d,mouse3d,cam(1).rot3d)   Print Int(mouse3d.x);Int(mouse3d.y);Int(mouse3d.z)   'tempv=scrnc3d   'scrnc2d=proj3d(tempv,cam(1))   'tempv=mouse3d   'mouse2d=proj3d(tempv,cam(1))   'Line(scrnc2d.x,scrnc2d.y)-(mouse2d.x,mouse2d.y)   Select Case mouseb      Case 0         If leftmousebdown=TRUE Then            Select Case mouseclicks               Case 0                  mouseclicks=1                  line3d(0).lstart=mouse3d               Case 1                  line3d(0).lend=mouse3d                  linecount=linecount+1                  ReDim Preserve line3d(linecount)                  line3d(linecount)=line3d(0)                  line3d(0).lstart=mouse3d            End Select            'mouse button released            'linecount=linecount+1            'ReDim Preserve line3d(linecount)            'line3d(linecount).lstart=scrnc3d            'line3d(linecount).lend=mouse3d         EndIf         If rightmousebdown=TRUE Then            If mouseclicks=1 Then               'linecount=linecount-1               mouseclicks=0            EndIf         EndIf         leftmousebdown=FALSE         rightmousebdown=FALSE      Case 1         leftmousebdown=TRUE      Case 2         rightmousebdown=TRUE   End Select   Print "line count=";linecount   If mouseclicks=1 Then      tempv=line3d(0).lstart      scrnc2d=proj3d(tempv,cam(1))      tempv=mouse3d      mouse2d=proj3d(tempv,cam(1))      Line(scrnc2d.x,scrnc2d.y)-(mouse2d.x,mouse2d.y)   End IfEnd SubFunction abtp(abtpx1 as Double,abtpy1 as Double,abtpx2 as Double,abtpy2 as Double) as Double   Dim As Double xlength,ylength,angle   xlength=(abtpx1-abtpx2)*-1   ylength=(abtpy1-abtpy2)*-1   angle=atan2(ylength,xlength)/pi*180   If angle<0 then angle=360+angle Mod 360   abtp=angleEnd FunctionFunction mymod(n As Double,m As Integer) As Double   Dim As Integer i   Dim As Byte neg, dec, inf   Select Case n      Case 0         mymod = 0      Case Else         If n<0 Then neg = TRUE Else neg = FALSE         dec=FALSE         inf=FALSE         Dim As String nstring         nstring=LTrim(Str(n))         'If Mid(nstring,1,1)="-" Then neg=TRUE Else neg=FALSE         If neg=TRUE Then nstring= Mid(nstring,2)         For i = 1 To Len(nstring)            Select Case Mid(nstring,i,1)               Case "0" To "9"                  'this is ok               Case "."                  dec=TRUE               Case Else                  'Cls                  'Print nstring                  'Print Mid(nstring,i,1)                  'sleep                  inf=TRUE                  Exit For            End Select         Next         If inf=TRUE Then            mymod=0         Else            If neg=TRUE Then               If dec=TRUE Then                  mymod=Val(Str(val(Mid(nstring,1,InStr(nstring,".")-1)) Mod m)+"."+Mid(nstring,InStr(nstring,".")+1))*-1               Else                  mymod = n Mod m               End If            Else               If dec=TRUE Then                  mymod=Val(Str(val(Mid(nstring,1,InStr(nstring,".")-1)) Mod m)+"."+Mid(nstring,InStr(nstring,".")+1))               Else                  mymod = n Mod m               End If            EndIf         EndIf   End SelectEnd Function'spinning cubeData -100, 100, 100Data -100,-100, 100Data  100,-100, 100Data  100, 100, 100Data -100, 100,-100Data -100,-100,-100Data  100,-100,-100Data  100, 100,-100'axes'x axisData -100,0,0Data  100,0,0'y axisData 0, 100,0Data 0,-100,0'z axisData 0,0, 100Data 0,0,-100''axes''x axis'Data -100,10,10'Data  100,10,10''y axis'Data 10, 100,10'Data 10,-100,10''z axis'Data 10,10, 100'Data 10,10,-100`
BasicCoder2
Posts: 3349
Joined: Jan 01, 2009 7:03

Re: 3d without openGL

paul doe wrote:This resource explains it very nicely:
http://www.codinglabs.net/article_world ... atrix.aspx

Oh no!! Homework :) It may take some time to work through it.
Rotating and moving about a 2D world is something I have done a lot of. Everything from Asteroids to raycasting dungeons (pseudo 3D but looked great with textured floors, walls and ceilings) . It was only when I thought about moving the viewer up into the Z axis I had problems. I call z the up axis as I see the x,y as the floor although I note that many examples have y as the up axis as you might find in a math or physics book.

This is an example of the raycasting 3D . If differs from the way it was done in Wolfenstein 3D. I took advantage of a modern computer's speed and the variation I used enables curved walls. The demo only has simple sprites but it looks better with animated sprites that move around the corridors and better textures. Looking at it now I see I could speed up the drawing code.
viewtopic.php?f=3&t=20594&hilit=raycasting
.
paul doe
Posts: 913
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

BasicCoder2 wrote:Rotating and moving about a 2D world is something I have done a lot of. Everything from Asteroids to raycasting dungeons (pseudo 3D but looked great with textured floors, walls and ceilings) . It was only when I thought about moving the viewer up into the Z axis I had problems. I call z the up axis as I see the x,y as the floor although I note that many examples have y as the up axis as you might find in a math or physics book.

This is an example of the raycasting 3D . If differs from the way it was done in Wolfenstein 3D. I took advantage of a modern computer's speed and the variation I used enables curved walls. The demo only has simple sprites but it looks better with animated sprites that move around the corridors and better textures. Looking at it now I see I could speed up the drawing code.

Looks nice. I have a raycaster somewhere, don't remember where. It was more similar to the one used in Doom2 (with sectors, linedefs and all that). But it seems that you're missing the point.
It is not about what the code does, it's how it does it. While it looks deceptively simple, it isn't, trust me. By the way, didn't you noticed that you can do perfectly good 3D code with the mat3 definitions? As it is a 3x3 matrix, you can't represent encode translations or projections also, but you can do that manually and it still works. In fact, the 3D code that I'm preparing initially used that implementation. To be able to work with OpenGL however, you need 4x4 matrices. Also, it is advisable that you adopt a more common convention for the axes, like the ones used by OpenGL/Direct3D. That way, you will be able to recycle much of the insight you gain by studying all that matrix and vector stuff. The only thing I know that uses the convention you mentioned is Maya.

Let's see if I can make my point across this time. Platform.bi, core.bi, mat3.bi and vec2h.bi are all the same as the previous example:

grid.bi

Code: Select all

`#ifndef __grid__   #define __grid__      #include once "core.bi"   #include once "vec2h.bi"   #include once "mat3.bi"      /'      A bounding box (or bounding rectangle) object            this is useful for clipping of objects, geometry, and collision detection, to      name a few applications.      The grid object derives from here, and also provides spatial partition functionality      besides the bounding functionality   '/   class boundingBox extends object      protected:         as mat3 m_transform = any         as mat3 m_invTransform = any                  as double m_width = any         as double m_height = any               public:         declare constructor()         declare constructor( byval w as double, byval h as double )                        declare property transform() as mat3         declare property transform( byval M as mat3 )                  declare property invTransform() as mat3                  declare virtual property width() as double         declare virtual property width( byval value as double )                  declare virtual property height() as double         declare virtual property height( byval value as double )                  declare function isInside( byval p as vec2h ) as boolean         declare function getPoint( byval p as vec2h ) as vec2h                  declare virtual sub render( byval c as uint32 = rgba( 222, 222, 222, 255 ) )   end class   constructor boundingBox()      m_width = 100.0      m_height = 100.0            m_transform = matrices.identity2h()      m_invTransform = inverse( m_transform )   end constructor      constructor boundingBox( byval w as double, byval h as double )      m_width = iif( w > 0, w, 100.0 )      m_height = iif( h > 0, h, 100.0 )            m_transform = matrices.identity2h()      m_invTransform = inverse( m_transform )         end constructor      property boundingBox.transform() as mat3      return( m_transform )   end property      property boundingBox.transform( byval M as mat3 )      m_transform = M      '' update also the inverse matrix of the transformation      m_invTransform = inverse( m_transform )   end property      property boundingBox.invTransform() as mat3      return( m_invTransform )   end property      property boundingBox.width() as double      return( m_width )   end property      property boundingBox.width( byval value as double )      m_width = value   end property      property boundingBox.height() as double      return( m_height )   end property      property boundingBox.height( byval value as double )      m_height = value   end property      function boundingBox.getPoint( byval p as vec2h ) as vec2h      '' returns the point in bounding box coordinates      return( m_invTransform * p )   end function      function boundingBox.isInside( byval p as vec2h ) as boolean      /'         this function returns true if a point p lies inside the bounding box                        0,0 +-------+              |       |             |   p   |             |       |             +-------+                       m_width, m_height                  it works transforming the point (which must be specified in global coordinates)         to the coordinate system of the bounding box, by multiplying the point with the         inverse transformation matrix of the bounding box.         After the transformation, the test is straightforward.      '/      dim as vec2h tp = getPoint( p )            return( iif( tp.x >= 0 andAlso tp.x <= m_width - 1 andAlso tp.y >= 0 andAlso tp.y <= m_height - 1, true, false ) )    end function      sub boundingBox.render( byval c as uint32 = rgba( 222, 222, 222, 255 ) )      '' renders the bounding box (mostly for debug purposes)            /'            p0 +-----+ p3               |     |               |     |               |     |            p1 +-----+ p2      '/      '' transform the origin and derive the rest of the points from there               dim as vec2h p0 = m_transform * vec2h( 0.0, 0.0 )      dim as vec2h p1 = m_transform * vec2h( 0.0, m_height )      dim as vec2h p2 = m_transform * vec2h( m_width, m_height )      dim as vec2h p3 = m_transform * vec2h( m_width, 0.0 )                  line( p0.x, p0.y ) - ( p1.x, p1.y ), c      line( p1.x, p1.y ) - ( p2.x, p2.y ), c      line( p2.x, p2.y ) - ( p3.x, p3.y ), c      line( p3.x, p3.y ) - ( p0.x, p0.y ), c            dim as vec2h center = vec2h( p0.x + m_width / 2, p0.y + m_height / 2 )            circle( center.x, center.y ), 6, rgba( 255, 0, 255, 255 ), , , , f   end sub   /'      grid object            A grid object that can be used as an acceleration structure      Derives from boundingBox   '/   class grid extends boundingBox      protected:         as double m_cellWidth         as double m_cellHeight         as integer m_gridXSize         as integer m_gridYSize            public:         declare constructor()         declare constructor( byval w as double, byval h as double, byval xSize as integer, byval ySize as integer )         declare destructor()            declare property width() as double         declare property width( byval value as double )                  declare property height() as double         declare property height( byval value as double )                  declare property gridXSize() as integer         declare property gridXSize( byval value as integer )                  declare property gridYSize() as integer         declare property gridYSize( byval value as integer )                  declare property cellWidth() as double         declare property cellHeight() as double            declare function getCell( byval p as vec2h ) as vec2h                  declare sub render( byval c as uint32 = rgba( 200, 200, 200, 255 ) )            end class         constructor grid( byval w as double, byval h as double, byval xSize as integer, byval ySize as integer )            '' call the base constructor to initialize      base.constructor( w, h )               m_gridXSize = iif( xSize > 0, xSize, 1 )      m_gridYSize = iif( ySize > 0, ySize, 1 )            m_cellWidth = m_width / m_gridXSize      m_cellHeight = m_height / m_gridYSize   end constructor      destructor grid()      end destructor      property grid.cellWidth() as double      return( m_cellWidth )   end property      property grid.cellHeight() as double      return( m_cellHeight )   end property      property grid.width() as double      return( m_width )   end property      property grid.width( byval value as double )      m_width = value      m_cellWidth = m_width / m_gridXSize      end property      property grid.height() as double      return( m_height )   end property      property grid.height( byval value as double )      m_height = value      m_cellHeight = m_height / m_gridYSize   end property      property grid.gridXSize() as integer      return( m_gridXSize )   end property      property grid.gridXSize( byval value as integer )      m_gridXSize = value      m_cellWidth = m_width / m_gridXSize      end property      property grid.gridYSize() as integer      return( m_gridYSize )   end property      property grid.gridYSize( byval value as integer )      m_gridYSize = value      m_cellHeight = m_height / m_gridYSize   end property      function grid.getCell( byval p as vec2h ) as vec2h      '' returns the cell in the grid this point lies on      '' assumes the point is already in grid space coordinates      return( vec2h( p.x / m_cellWidth, p.y / m_cellHeight ) )      end function   sub grid.render( byval c as uint32 = rgba( 200, 200, 200, 255 ) )      /'         get the 4 corners of the grid                  p0-----p3         |       |         |       |         p1-----p2      '/            '' transform the grid corners to grid space      dim as vec2h p0 = m_transform * vec2h( 0.0, 0.0 )      dim as vec2h p1 = m_transform * vec2h( 0.0, m_height )      dim as vec2h p2 = m_transform * vec2h( m_width, m_height )      dim as vec2h p3 = m_transform * vec2h( m_width, 0.0 )            '' draw the borders      line( p0.x, p0.y ) - ( p1.x, p1.y ), c      line( p1.x, p1.y ) - ( p2.x, p2.y ), c      line( p2.x, p2.y ) - ( p3.x, p3.y ), c      line( p3.x, p3.y ) - ( p0.x, p0.y ), c            '' horizontal lines      for y as double = 0 to 1 step 1 / m_gridYSize         dim as vec2h st = interpolate( p0, p1, y )         dim as vec2h en = interpolate( p3, p2, y )                  line( st.x, st.y ) - ( en.x, en.y ), c      next            '' vertical lines      for x as double = 0 to 1 step 1 / m_gridXSize         dim as vec2h st = interpolate( p0, p3, x )         dim as vec2h en = interpolate( p1, p2, x )                  line( st.x, st.y ) - ( en.x, en.y ), c      next         end sub#endif`

grid test.bas

Code: Select all

`#include once "platform.bi"#include once "core.bi"#include once "grid.bi"#include once "vec2h.bi"#include once "mat3.bi"/'   grid debug program      conventions: same as the previous code'/dim as integer scrW = 800, scrH = 600screenRes( scrW, scrH, 32 )windowTitle( fbVersion & " - Simple matrix and vector demo" )dim as vec2h mp '' mouse coordinatesdim as vec2h gp '' mouse coordinates in grid space'' the angle of rotationdim as double ang = 0.0'' instantiate a grid ( 400 x 400 px, 10 x 10 cells )dim as grid g = grid( 400, 400, 10, 10 )'' position of the grid (the logical origin of the grid is at the top left corner)dim as vec2h gridPos = vec2h( scrW / 2 - g.width / 2, scrH / 2 - g.height / 2 )'' axis of rotationdim as vec2h axis = vec2h( gridPos.x, gridPos.y )dim as string kdim as integer mx, mydo   getMouse( mx, my )   mp = vec2h( mx, my )      k = lcase( inkey() )         '' translate the grid to its position   dim as mat3 T = matrices.translation2h( gridPos.x, gridPos.y )   '' rotation about the center of the grid   dim as mat3 R = matrices.rotationAxis2h( vec2h( g.width / 2, g.height / 2 ), ang )   '' shear the grid a little to deform it   dim as mat3 S = matrices.shear2h( 25, 12 )      '' transform the grid   '' try to change the order of this, or to provide another matrix and see what happens   g.transform = T * S * R      '' transform the mouse pointer to grid space   gp = g.getPoint( mp )      '' get the mouse coordinates in grid cell space   dim as vec2h cell = g.getCell( g.invTransform * vec2h( mp ) )      screenLock()      cls()            '' render the grid            g.render( rgba( 64, 64, 64, 255 ) )            /'         this is the important bit of code that does the magic                  what it actually does, is computing which cell of the grid the user is pointing         with the mouse. Note, however, that the grid can be grotesquely deformed, yet         the cell is computed correctly. And, as you can see, is in fact treated like it         was a simple, untransformed, rectangle on the screen      '/            '' check if the mouse pointer is inside the grid      if( cell.x >= 0 and cell.x <= g.gridXSize and cell.y >= 0 and cell.y <= g.gridYSize ) then         /'            if it is, draw the cell the mouse is pointing to                        the points are winded like this:                        p0 +--------+ p3               |        |               |        |            p1 +--------+ p2                        which is to say, are computed in counter-clockwise order         '/                  '' top left         dim as vec2h p0 = g.transform * _            vec2h( int( cell.x ) * g.cellWidth, int( cell.y ) * g.cellHeight )                  '' bottom left         dim as vec2h p1 = g.transform * _            vec2h( int( cell.x ) * g.cellWidth, int( cell.y ) * g.cellHeight + g.cellHeight )                  '' bottom right         dim as vec2h p2 = g.transform * _             vec2h( int( cell.x ) * g.cellWidth + g.cellWidth, int( cell.y ) * g.cellHeight + g.cellHeight )                  '' top right         dim as vec2h p3 = g.transform * _            vec2h( int( cell.x ) * g.cellWidth + g.cellWidth, int( cell.y ) * g.cellHeight )                  '' draw the cell edges         line( p0.x, p0.y ) - ( p1.x, p1.y ), rgba( 255, 255, 0, 255 )         line( p1.x, p1.y ) - ( p2.x, p2.y ), rgba( 255, 255, 0, 255 )         line( p2.x, p2.y ) - ( p3.x, p3.y ), rgba( 255, 255, 0, 255 )         line( p3.x, p3.y ) - ( p0.x, p0.y ), rgba( 255, 255, 0, 255 )               end if               screenUnlock()      ang += 0.05      if ang > 360.0 then      ang -= 360.0   end if      sleep( 1 )loop until k = chr( 27 )`

In this more than unimpressive demo, you see a (somewhat deformed) grid that it's rotating. And it detects the cell that you are pointing at with the mouse. Nothing fancy, no? But, take a look at the main loop. Specifically, the part that calculates which cell you are picking. Simple enough, right? The trick here is that it uses coordinate system mapping (via matrix multiplication) to map the mouse coordinates to the coordinate system of the grid, where it is trivial to compute the cell being picked. And all that while the grid is being transformed. If you where to do that in an absolute coordinate system (as with Euler angles), you will be in for a lot of trouble. Wanna try it? I'll like to see ;-)

owen wrote:here's what i was working on back in 2013

Perfect timing. Thanks, owen.

@BasicCoder2: look inside owen's code, specifically the rotate3dPoint function. You'll see that, with Euler angles, you have six different ways in which you can rotate a point, and the axes change depending on which one you rotate first (play with owen's code to see what I mean). And all of them about the absolute coordinates of the world, not that of the object itself. This problem disappears if you use matrices (no matter in which order you compose the rotations; as far as the matrix is concerned, they are multiplied all at once)

The thing to remember here is: matrix * point = point transformed to the matrix coordinate system. InverseMatrix * point = point from the matrix coordinate system to absolute (aka world) coordinates.

Hope this helps with your homework ;-)
Paul
owen
Posts: 512
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: 3d without openGL

@ paul doe

This problem disappears if you use matrices

I couldn't agree with you more.
And not only that, using matrices avoids gimbal lock

in my example you can see it encounter gimbal lock by first rotating 90 degrees on the x axis, then 90 on y axis and then stop at 89 on the z.
when you get to 89 on the z axis and proceed to rotate to 90 look closely and notice a tiny glitch.

what a wonderful experience it was to try and avoid gimbal lock to no avail. well actually i kinda did it but what length i went to.

im glad that my example can serve the perfect example of what not to do. LOL
dodicat
Posts: 5774
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: 3d without openGL

Basiccoder2
I did this a while ago
Windows.
The main point is lines 274/275

preset the z rotation at pi/2

then tilt the y by -pi/8

then rotate about the x axis only.

The rest of the code is the shape and texture to rotate.
(cube + numbers)
But the idea should apply to any 3D rotate about a point.

Code: Select all

`#include Once "windows.bi"#include Once "fbgfx.bi"Const pi=4*Atn(1)Function contrast(c As Ulong) As Ulong    #define Intrange(f,l) Int(Rnd*((l+1)-(f))+(f))    'get the rgb values    Dim As Ubyte r=Cptr(Ubyte Ptr,@c),g=Cptr(Ubyte Ptr,@c),b=Cptr(Ubyte Ptr,@c),r2,g2,b2    Do        r2=Intrange(0,255):g2=IntRange(0,255):b2=IntRange(0,255)        'get at least 120 byte difference    Loop Until Abs(r-r2)>120 Andalso Abs(g-g2)>120 Andalso Abs(b-b2)>120    Return Rgb(r2,g2,b2) End Function#define BOLD  2#define ITALIC 4Sub DrawFont(Byref BUFFER As Any Ptr=0,Byval POSX As Long, Byval POSY As Long, _    Byref FTEXT As String, Byref FNAME As String,Byval FSIZE As Long, _    Byval FCOLOR As Ulong=Rgba(255,255,255,255),Byval FSTYLE As Long=0, _    Byval CHARSET As Long=DEFAULT_CHARSET) Export        Static FINIT As Long    Static As hdc THEDC    Static As hbitmap THEBMP    Static As Any Ptr THEPTR    Static As fb.image Ptr FBBLK    Static As Long TXTSZ,RESU,RESUU    Static As hfont THEFONT    Static As Long FW,FI,TXYY',FCOR    Static DSKWND As hwnd, DSKDC As hdc    Static MYBMPINFO As BITMAPINFO    Static As TEXTMETRIC MYTXINFO    Static As SIZE TXTSIZE    Static As RECT RCT    Static As Ubyte Ptr ubp    ubp=Cptr(Ubyte Ptr,@FCOLOR)    Swap ubp,ubp    Dim As Ubyte alphaval =ubp    ubp=0    #define FontSize(PointSize) - MulDiv(PointSize, GetDeviceCaps(THEDC, LOGPIXELSY), 72)        If FINIT = 0 Then          FINIT = 1          With MYBMPINFO.bmiheader            .biSize = Sizeof(BITMAPINFOHEADER)            .biWidth = 2048            .biHeight = -513            .biPlanes = 1            .biBitCount = 32            .biCompression = BI_RGB        End With         DSKWND = GetDesktopWindow()        DSKDC = GetDC(DSKWND)        THEDC = CreateCompatibleDC(DSKDC)        THEBMP = CreateDIBSection(THEDC,@MYBMPINFO,DIB_RGB_COLORS,@THEPTR,null,null)          ReleaseDC(DSKWND,DSKDC)      End If    If (FSTYLE And 2) Then FW = FW_BOLD Else FW = FW_NORMAL      If (FSTYLE And 4) Then FI = True Else FI = False      THEFONT = CreateFont(FontSize(FSIZE),0,0,0,FW,FI,0,0,CHARSET,0,0,0,0,Cast(Any Ptr,Strptr(FNAME)))      SelectObject(THEDC,THEBMP)    SelectObject(THEDC,THEFONT)    GetTextMetrics(THEDC,@MYTXINFO)    GetTextExtentPoint32(THEDC,Strptr(FTEXT),Len(FTEXT),@TXTSIZE)    TXTSZ = TXTSIZE.CX    TXYY = TXTSIZE.CY    If (FSTYLE And 4) Then        If MYTXINFO.tmOverhang Then            TXTSZ += MYTXINFO.tmOverhang        Else            TXTSZ += 1+(FSIZE/2)        End If        TXYY += 1+(FSIZE/8)    End If    RCT.LEFT = 0    RCT.TOP = 1    RCT.RIGHT = TXTSZ    RCT.BOTTOM = TXYY+1    TXTSZ -= 1    TXYY -= 1    SetBkColor(THEDC,Rgba(255,0,255,0))    SetTextColor(THEDC,FCOLOR)    SystemParametersInfo(SPI_GETFONTSMOOTHING,null,@RESU,null)    If RESU Then SystemParametersInfo(SPI_SETFONTSMOOTHING,False,@RESUU,null)    ExtTextOut(THEDC,0,1,ETO_CLIPPED Or ETO_OPAQUE,@RCT,Strptr(FTEXT),Len(FTEXT),null)    If RESU Then SystemParametersInfo(SPI_SETFONTSMOOTHING,True,@RESUU,null)    FBBLK = THEPTR+(2048*4)-Sizeof(fb.image)    FBBLK->Type = 7    FBBLK->bpp = 4    FBBLK->Width = 2048    FBBLK->height = 512    FBBLK->pitch = 2048*4    Put BUFFER,(POSX,POSY),FBBLK,(0,0)-(TXTSZ-1,TXYY),Alpha,alphaval    DeleteObject(THEFONT)End SubType v3    As Single x,y,z    As Ulong cEnd TypeType _float    As Single x,y,zEnd TypeType sincos 'FLOATS for angles    As Single sx,sy,sz    As Single cx,cy,cz    Declare Static Function construct(As Single,As Single,As Single) As sincosEnd TypeFunction sincos.construct(x As Single,y As Single,z As Single) As sincos    Return   Type <sincos>(Sin(x),Sin(y),Sin(z), _    Cos(x),Cos(y),Cos(z))End FunctionDim Shared As v3 eyepointSub load(im As Any Ptr,w() As V3,Byref ctr As V3,T As Long)    Dim As Ulong c,bc=Rgb(255,0,255)    Dim As Integer pitch    Dim As Any Ptr row    Dim As Ulong Ptr pixel    Dim As Integer ddx,ddy,count    Imageinfo im,ddx,ddy,,pitch,row    For y As Long=0 To ddy-1        For x As Long=0 To ddx-1            pixel=row+pitch*(y)+(x) Shl 2            (c)=*pixel             if c<>bc then            count+=1            Redim Preserve w(1 To count)            Select Case As Const T            Case 1 :w(count)=Type(x,y,0,c) 'front            Case 2 :w(count)=Type(y,0,x,c)'top            Case 3 :w(count)=Type(y,x,ddy,c)'back             Case 4 :w(count)=Type(x,ddy,y,c)'base            Case 5 :w(count)=Type(ddx,y,x,c)'r side            Case 6 :w(count)=Type(0,x,y,c)'l side            End Select            end if            If x=ddx\2 Andalso y=ddy\2 Then ctr=w(count):ctr.c=count        Next x    Next yEnd SubFunction Rotate(c As V3,p As V3,a As sincos,scale As _float=Type<_float>(1,1,1)) As V3    Dim As Single dx=p.x-c.x,dy=p.y-c.y,dz=p.z-c.z    Return Type<V3>((scale.x)*((a.cy*a.cz)*dx+(-a.cx*a.sz+a.sx*a.sy*a.cz)*dy+(a.sx*a.sz+a.cx*a.sy*a.cz)*dz)+c.x,_    (scale.y)*((a.cy*a.sz)*dx+(a.cx*a.cz+a.sx*a.sy*a.sz)*dy+(-a.sx*a.cz+a.cx*a.sy*a.sz)*dz)+c.y,_    (scale.z)*((-a.sy)*dx+(a.sx*a.cy)*dy+(a.cx*a.cy)*dz)+c.z,p.c)End Function Sub RotateImage(wa() As V3,angle As _float,Byref centroid As V3,ctr As V3,sc As Single=1.75,nflag As Long,n() As V3)    #define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)    Var s=sincos.construct(angle.x,angle.y,angle.z)    Var g=Rotate(Type(0,0,0),n(nflag),s)    Var cp=map(-1,1,g.z,.2,1)'shader factor    Dim As Ubyte rd,gr,bl    Dim  As Single dx,dy,dz,w    Dim As Single SinAX=Sin(angle.x)    Dim As Single SinAY=Sin(angle.y)    Dim As Single SinAZ=Sin(angle.z)    Dim As Single CosAX=Cos(angle.x)    Dim As Single CosAY=Cos(angle.y)    Dim As Single CosAZ=Cos(angle.z)    Dim As V3 centre=Type(100,100,100) 'the centre of rotation (fulcrum)    Dim As V3 result    Dim As Single dp=.6*sc        For z As Long=Lbound(wa) To Ubound(wa)                dx=wa(z).x-centre.x        dy=wa(z).y-centre.y        dz=wa(z).z-centre.z                Result.x=sc*((Cosay*Cosaz)*dx+(-Cosax*Sinaz+Sinax*Sinay*Cosaz)*dy+(Sinax*Sinaz+Cosax*Sinay*Cosaz)*dz)+centre.x        result.y=sc*((Cosay*Sinaz)*dx+(Cosax*Cosaz+Sinax*Sinay*Sinaz)*dy+(-Sinax*Cosaz+Cosax*Sinay*Sinaz)*dz)+centre.y        result.z=sc*((-Sinay)*dx+(Sinax*Cosay)*dy+(Cosax*Cosay)*dz)+centre.z        If z=ctr.c Then  centroid=result':If flag=0 Then Exit Sub        'If flag Then            'this bit is to add perspective ================            w = 1 + (result.z/eyepoint.z)            result.x = (result.x-eyepoint.x)/w+eyepoint.x +300            result.y = (result.y-eyepoint.y)/w+eyepoint.y +200            result.z = (result.z-eyepoint.z)/w+eyepoint.z            rd= Cptr(Ubyte Ptr,@wa(z).c) 'shaders            gr= Cptr(Ubyte Ptr,@wa(z).c)             bl= Cptr(Ubyte Ptr,@wa(z).c)            rd=cp*rd:gr=cp*gr:bl=cp*bl            Line(result.x-dp,result.y-dp)-(result.x+dp,result.y+dp),Rgb(rd,gr,bl),bf       ' End If    Next zEnd Sub'sort each image by .z centroid distanceSub sort(array() As V3,painter() As Long)    For p1 As Long  = 1 To Ubound(array,1) - 1        For p2 As Long  = p1 + 1 To Ubound(array,1)              If array(p1).z<array(p2).z Then Swap painter(p1),painter(p2):Swap array(p1),array(p2)        Next p2    Next p1End SubFunction Regulate(Byval MyFps As Long,Byref fps As Long) As Long    Static As Double timervalue,_lastsleeptime,t3,frames    Var t=Timer    frames+=1    If (t-t3)>=1 Then t3=t:fps=frames:frames=0    Var sleeptime=_lastsleeptime+((1/myfps)-T+timervalue)*1000    If sleeptime<1 Then sleeptime=1    _lastsleeptime=sleeptime    timervalue=T    Return sleeptimeEnd Function'==========================================================================Screen 19,32,2color ,rgb(0,0,50)Dim As Any Ptr image(1 To 6)Randomize 2For n As Long=1 To 6    image(n)=Imagecreate(200,200)Next n'normals to each cube faceDim As V3 norm(1 To 6)={(0,0,1),(0,1,0),(0,0,-1),(0,-1,0),(-1,0,0),(1,0,0)}eyepoint=Type(100,100,800) 'behind image centres'fill the imagesDim As String fnt="arial"drawfont(image(1),60,20,"1",fnt,100,contrast(Rgb(0,255,255)))drawfont(image(2),60,20,"3",fnt,100,contrast(Rgb(55,255,0)))drawfont(image(3),60,20,"6",fnt,100,contrast(Rgb(155,100,255)))drawfont(image(4),60,20,"4",fnt,100,contrast(Rgb(0,255,0)))drawfont(image(5),60,20,"5",fnt,100,contrast(Rgb(255,00,0)))drawfont(image(6),60,20,"2",fnt,100,contrast(Rgb(0,0,255)))Dim As Ulong ccFor n As Long=1 To 6'borders    Select Case n    Case 1:cc=Rgb(0,255,255)    Case 2:cc=Rgb(255,255,0)    Case 3:cc=Rgb(255,255,255)    Case 4:cc=Rgb(0,255,0)    Case 5:cc=Rgb(255,00,0)    Case 6:cc=Rgb(0,0,255)    End Select    'frames    For k As Long=0 To 5        Line image(n),(0+k,0+k)-(199-k,199-k),cc,b    Next kNext nRedim As V3 w1(),w2(),w3(),w4(),w5(),w6()'main arraysDim As V3 centroid(1 To 6),ctr(1 To 6)load(image(1),w1(),ctr(1),1)load(image(2),w2(),ctr(2),2)load(image(3),w3(),ctr(3),3)load(image(4),w4(),ctr(4),4)load(image(5),w5(),ctr(5),5)load(image(6),w6(),ctr(6),6)Dim As _float angleDim As Long fpsDim As Long painter(1 To 6)For n As Long=1 To 6:painter(n)=n:Next n    Screenset 1,0        angle.z=pi/2    angle.y=-pi/8        Do        'rotate        angle.x+=.05  :If angle.x>=2*pi Then angle.x=0        'angle.y+=.025 :If angle.y>=2*pi Then angle.y=0        'angle.z+=.015 :If angle.z>=2*pi Then angle.z=0                'sort by .z to reset painter and centroids        sort(centroid(),painter())                Cls        Drawfont(,20,20,"Framerate " &fps,"arial",20,Rgb(200,100,0),italic Or bold)        'draw in 3D order        For n As Long=1 To 6            Select Case As Const painter(n)            Case 1:RotateImage(w1(),angle,centroid(1),ctr(1),,1,norm())            Case 2:RotateImage(w2(),angle,centroid(2),ctr(2),,2,norm())            Case 3:RotateImage(w3(),angle,centroid(3),ctr(3),,3,norm())            Case 4:RotateImage(w4(),angle,centroid(4),ctr(4),,4,norm())            Case 5:RotateImage(w5(),angle,centroid(5),ctr(5),,5,norm())            Case 6:RotateImage(w6(),angle,centroid(6),ctr(6),,6,norm())            End Select        Next n                Flip        For n As Long=1 To 6:painter(n)=n:Next n 'reset the painter            Sleep regulate(30,fps),1        Loop Until Len(Inkey)        Sleep        For n As Long=1 To 6            Imagedestroy image(n)        Next n                           `