## Tile - the perspective way

Game development specific discussions.
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Tile - the perspective way

Hi, still in the continuation of tile studies for gaming. Some development here viewtopic.php?f=15&p=215578#p215578, but the perspective chapter is a step harder and deserves its own treatment.

Basically the questions are 2. So to fill the never-ending plane with tiles of a single rectangular type we ask:
• how the whole tile grid gets transformed by perspective?
• if the tile is an image, how doing good texture distortion within any of the many contours generated by the perspective?

The question of the grid
Unfortunately websites are returning very technical stuff so after having simplified to extreme, here is what I’ve finally been able to get so far. Please if I’m wrong please correct me.

Principle
What is important is that, provided a flat view window (the screen):
• perspective means that there should be a line of horizon somewhere on the screen (or out but still existing);
• any set of parallel lines meets to the horizon line in exactly one point (one point a set),
• two different sets of parallel should meet in general at two different points - but not always if we have things built in a symmetrical way we can meet only one perspective point, and in the most general case 3 points may appear;
• lines that are parallel to the horizon are special and keep parallel, but there interline distance is vanishing as they get closer to the true horizon.

Test program
This leads to a first code attempt to deal with perspective grid. This is based on the simple implementation of the following scheme: Code: Select all

`'case_study-----------------------'horizon dependant perspectivegrid'---------------------------------const as long     imgW          =>  200const as long     imgH          =>  260const as long     screenWidth   =>   800const as long     screenHeight  =>   400const as ulong    imgTransColor => rgb(255,0,255)#include "fbgfx.bi"'type_declaration----------------'---------------------------------type SCREENTEST extends OBJECT    '** global variable container    'to hold/get screen parameter    declare static sub TestScreen()    static as integer  scrW    static as integer  scrHend type 'SCREENTEST <- OBJECTtype MOUSETEST extends SCREENTEST    '** global variable container    'to hold/get mouse parameter    declare static function TestMouse() as long    static as long  gmX    static as long  gmY    static as long  gmBtnend type 'MOUSETEST <- SCREENTEST <- OBJECTtype DRAGGABLEHORIZON extends MOUSETEST   declare constructor()   declare constructor(byval as long)   declare property ControllerY() as long   declare property ControllerY(byval as long)   declare sub TestController()   declare sub DrawController()   declare sub DrawHorizon()      as long         _yMainCoordinate      as double      _yControllerRate      as double      _viewingAngle      as double      _viewingRadius      as boolean      _mouseOver      as boolean      _mouseClick      as boolean      _dragStartedend type 'DRAGGABLEHORIZON'type_implemetation--------------'---------------------------------dim as integer SCREENTEST.scrW       => -1dim as integer SCREENTEST.scrH       => -1sub SCREENTEST.TestScreen()    screenInfo (SCREENTEST.scrW, SCREENTEST.scrH)end sub 'SCREENTEST.TestScreen()dim as long MOUSETEST.gmX       => -1dim as long MOUSETEST.gmY       => -1dim as long MOUSETEST.gmBtn     => -1function MOUSETEST.TestMouse() as long    '---->    return getMouse (MOUSETEST.gmX, _                      MOUSETEST.gmY, _                      , _                      MOUSETEST.gmBtn)end function 'LNG:=MOUSETEST.TestMouse()constructor DRAGGABLEHORIZON()   BASE()   '   with THIS      ._yMainCoordinate   => THIS.scrH\2      ._yControllerRate   => 0.4      ._viewingAngle      => 1 - THIS._yControllerRate      ._viewingRadius      => 200      ._mouseOver         => FALSE      ._mouseClick      => FALSE      ._dragStarted      => FALSE   end with 'THISend constructor 'DRAGGABLEHORIZON()constructor DRAGGABLEHORIZON(byval Y as long)   BASE()   '   with THIS      ._yMainCoordinate   => Y      ._yControllerRate   => Y/THIS.scrH      ._viewingAngle      => 1 - THIS._yControllerRate      ._viewingRadius      => 200      ._mouseOver         => FALSE      ._mouseClick      => FALSE      ._dragStarted      => FALSE   end with 'THISend constructor 'DRAGGABLEHORIZON(valLONG)property DRAGGABLEHORIZON.ControllerY() as long   '---->   return (90 + THIS._yControllerRate*140)end property 'LNG:=DRAGGABLEHORIZON.ControllerYproperty DRAGGABLEHORIZON.ControllerY(byval SetValue as long)   THIS._yControllerRate   = (SetValue - 90)/140end property 'DRAGGABLEHORIZON.ControllerY(valLNG)sub DRAGGABLEHORIZON.TestController()   dim as long      dragGmX   dim as long      dragGmY   dim as long      dragGmBtn    if THIS._dragStarted   then        getMouse dragGmX, dragGmY, , dragGmBtn        if dragGmX<(10 + 4)                  then dragGmX = 10 + 4 + 4        if dragGmX>(26 + 4)                  then dragGmX = 26 + 4 - 4        if dragGmY<(THIS.ControllerY + 28)      then THIS.gmY = THIS.ControllerY + 28 + 4        if dragGmY>(THIS.ControllerY + 8 + 28)   then THIS.gmY = THIS.ControllerY + 28 + 4    end if    '    if dragGmY<30 then dragGmY = 30    if dragGmY>(THIS.scrH - 14) then dragGmY = THIS.scrH - 14    if ( THIS.gmX>=(10 + 4)                           and _        THIS.gmX<=(26 + 4)                           and _        THIS.gmY>=(THIS.ControllerY + 28)               and _        THIS.gmY<=(THIS.ControllerY + 8 + 28) )            or _        THIS._dragStarted                            then       if THIS._mouseOver=FALSE then THIS._mouseOver = TRUE        if THIS.gmBtn=+1 then           if THIS._mouseClick=FALSE then THIS._mouseClick = TRUE            if THIS._dragStarted then               THIS.ControllerY   = dragGmY - 28            else                THIS._dragStarted   = TRUE            end if        else           if THIS._mouseClick=TRUE then THIS._mouseClick = FALSE            THIS._dragStarted   = FALSE        end if    else       if THIS._mouseClick=TRUE then THIS._mouseClick = FALSE        if THIS._mouseOver=TRUE then THIS._mouseOver = FALSE    end ifend sub 'DRAGGABLEHORIZON.TestController()sub DRAGGABLEHORIZON.DrawController()   THIS.TestController()   '   line (12,90)-step(12,140), rgb(160,120,120), b   line (12 + 1,90 + 1)-step(12 - 2,140 - 2), rgb(190,190,190), bf   '   if THIS._mouseOver then      line (10,90 + THIS._yControllerRate*140)-step(16,08), rgb(190,100,100), bf   else      line (10,90 + THIS._yControllerRate*140)-step(16,08), rgb(120,120,120), bf   end if   '   draw string (28,THIS.ControllerY - 20), _             "Angle "& str(1 - THIS._yControllerRate), _             rgb(200,120,120)   '   THIS._viewingAngle = 1 - THIS._yControllerRate   circle (50,THIS.ControllerY - 24), 50, 0,0, -1.57, , F   line (50,THIS.ControllerY - 24)-step(50,0),0   paint (50 + 8,THIS.ControllerY - 24 - 8), rgb(240,230,230), 0   line (50,THIS.ControllerY - 24)-step(80*cos(THIS._viewingAngle),-80*sin(THIS._viewingAngle)), 0   circle (50 + 80*cos(THIS._viewingAngle),THIS.ControllerY - 24 - 80*sin(THIS._viewingAngle)), 5, 0end sub 'DRAGGABLEHORIZON.DrawHorizon()sub DRAGGABLEHORIZON.DrawHorizon()   THIS.DrawController()   '   THIS._yMainCoordinate = _                THIS.scrH - THIS._viewingRadius*tan(THIS._viewingAngle)   '   draw string (28,THIS._yMainCoordinate - 10), "Horizon", rgb(200,120,120)   line (0,THIS._yMainCoordinate)-step(THIS.scrW,0), rgb(240,140,140)   line (0,THIS._yMainCoordinate - 1)-step(THIS.scrW,0), rgb(200,120,120)   line (0,THIS._yMainCoordinate + 1)-step(THIS.scrW,0), rgb(200,120,120)   '   'draw horizon parallel tile border   for n as long = -1 to -150 step -1      dim as single   borderStep   = THIS.scrH\2 + (n - 1/2)*imgH      dim as single   cYn               dim as single   cZn               cYn   = _       THIS._viewingRadius*sin(THIS._viewingAngle)*sin(THIS._viewingAngle)*borderStep/ _                         (THIS._viewingRadius - cos(THIS._viewingAngle)*borderStep)      cZn   = _      - THIS._viewingRadius*cos(THIS._viewingAngle)*sin(THIS._viewingAngle)*borderStep/ _                         (THIS._viewingRadius - cos(THIS._viewingAngle)*borderStep)      line (0,THIS.scrH - sqr(cYn^2 + cZn^2))-step(THIS.scrW,0), rgb(080,170,090)   next nend sub 'DRAGGABLEHORIZON.DrawHorizon()'subroutine_declaration----------'---------------------------------'(ui routine)declare function DrawWindowFrame(byval as fb.IMAGE ptr) as fb.IMAGE ptrdeclare sub DrawExitButton(byval as boolean=FALSE)declare function TestFrameWorkForMouse() as boolean'program_initialization----------'---------------------------------randomize TIMERscreenRes screenWidth, _           screenHeight, _           32, _           1, _           fb.GFX_NO_FRAME'note:'gfx screen should be initialized before imageSCREENTEST.TestScreen()dim as DRAGGABLEHORIZON      horizon'program_main_loop---------------'---------------------------------dim as boolean  quitOrder   => FALSEdo    SCREENTEST.TestScreen()   MOUSETEST.TestMouse()   quitOrder   = TestFrameWorkForMouse()    screenLock       cls       DrawWindowFrame(0)      DrawExitButton(quitOrder)            view (4,28)-(SCREENTEST.scrW - 6,SCREENTEST.scrH - 4)         line (SCREENTEST.scrW\2,0)-step(0,SCREENTEST.scrH),  _               rgb(80,80,180), _               ,_               &b1100000011110000                  for i as long = 0 to 100            line (SCREENTEST.scrW\2,SCREENTEST.scrH - 32)-step(-(i + 1/2)*imgW,0), 0            line step-(SCREENTEST.scrW\2,horizon._yMainCoordinate), 0                        line (SCREENTEST.scrW\2,SCREENTEST.scrH - 32)-step(+(i + 1/2)*imgW,0), 0            line step-(SCREENTEST.scrW\2,horizon._yMainCoordinate), 0               next i                  horizon.DrawHorizon()            view screen   screenUnlock   if quitOrder then         '(quit)      sleep 200      end 0   else        '(standard delay)      sleep 15   end ifloop until inkey=chr(27)     'program_finalization------------'---------------------------------'(explicit clean up)'program_termination-------------'---------------------------------sleepend 0'subroutine_implementation-------'---------------------------------'(ui)function DrawWindowFrame(byval DrawingZone as fb.IMAGE ptr) as fb.IMAGE ptr   line (00,00)-(SCREENTEST.scrW - 1, SCREENTEST.scrH - 1), _          rgb(200,190,220), _          bf    for x as long = 0 to SCREENTEST.scrW\10        line (04 + 4*x,04)-(SCREENTEST.scrW - 1 - 4, 24), _              rgb(190 - x,200,090 + x), _              bf    next x    for x as long = 0 to SCREENTEST.scrW\10        line (04 + 2*x,24)-(SCREENTEST.scrW - 1 - 4 - 2*x, SCREENTEST.scrH - 1 - 4), _              rgb(090 + x,190 + x\2,120 + x), _              bf    next x    line (04,24)-(SCREENTEST.scrW - 1 - 4, 28), _          rgb(200,200,220), _          bf   draw string (08,12), _                 "HORIZON DEPENDANT PERSPECTIVE GRID - FB1.04" & _                 " (note: window not movable not resizable)", _                 rgb(030,050,140)    if DrawingZone<>0 then        put (04,28), DrawingZone, TRANS    else       imageDestroy DrawingZone       'MEMORY LEAK AROUND HERE       'fixed by null image attribute but still weird        DrawingZone = imageCreate(0, _                                   0, _                                   imgTransColor, _                                   32)    end if    '    '---->    return DrawingZoneend function 'IMAGE_PTR:=DrawWindowFrame(valIMAGE_PTR)'sub DrawExitButton(byval QuitOrderSent as boolean=FALSE)   line (SCREENTEST.scrW - 1 - 4 - 18, 5)-(SCREENTEST.scrW - 1 - 5, 22), _          rgb(250,80,80), _          bf   draw string (SCREENTEST.scrW - 1 - 4 - 12,12), "X"   if QuitOrderSent then      line (SCREENTEST.scrW - 1 - 4 - 16, 7)- _              (SCREENTEST.scrW - 1 - 6, 20), _              rgb(140,120,080), _              bf   end ifend sub 'DrawExitButton()'function TestFrameWorkForMouse() _                         as boolean   dim as boolean QuitOrder   if MOUSETEST.gmX>(SCREENTEST.scrW - 1 - 4 - 18) and _      MOUSETEST.gmX<(SCREENTEST.scrW - 1 - 5) and _      MOUSETEST.gmY>5 and _       MOUSETEST.gmY<22 then         if MOUSETEST.gmBtn=+1 then          QuitOrder = TRUE         end if   else      QuitOrder = FALSE   end if   '---->   return QuitOrderend function 'BOOL:=TestFrameWorkForMouse()'[EOF]`

The question of the texture
About texture, my first attempt has lead to a test program where I’m distorting the tile image in various ways. This is not complete or general enough, but it’s a program I think useful for anyone for it's possible to add more testing procedures.

First conclusion anyway: there is no major problem in distorting a texture if this involves only size reduction. Magnification means loss of pixels, and would probably require good color interpolation algorithm that I don’t know at all.

Code
Use the here joined tile ("planet.bmp") to get the program work, or an equivalent dimension bmp tile of your choice. Code: Select all

`'case_study-----------------------'perspective transform of an image'---------------------------------const as string imgFileName => "planet.bmp"const as long     imgW          =>  200const as long     imgH          =>  260const as ulong    imgTransColor => rgb(255,0,255)const as long     screenWidth   => 800const as long     screenHeight  => 400#include "fbgfx.bi"'subroutine_declaration----------'---------------------------------'(perspective routine)'if new routine added -> '          add button to interface'the implementation are at the enddeclare sub ReduceXY(byval Img          as fb.IMAGE ptr, _                     byval XScaleFactor as double=0.5, _                      byval YScaleFactor as double=0.5)declare sub ShearAlongX(byval Img           as fb.IMAGE ptr, _                        byval XscaleFactor  as double=0.5, _                         byval OffsetXatYmax as long=0)declare sub TriangleAlongX(byval Img            as fb.IMAGE ptr, _                           byval BaseAtBottom   as boolean=TRUE, _                           byval XscaleFactor   as double=0.5, _                            byval OffsetXatYmax  as long=0)declare sub TrapezeAlongX(byval Img                 as fb.IMAGE ptr, _                          byval XscaleFactorAtYmin  as double=0.5, _                           byval XscaleFactorAtYmax  as double=1)declare sub LinearXY(byval Img   as fb.IMAGE ptr)'type_declaration----------------'---------------------------------type SCREENTEST extends OBJECT    '** global variable container    'to hold/get screen parameter    declare static sub TestScreen()    static as integer  scrW    static as integer  scrHend type 'SCREENTEST <- OBJECTtype MOUSETEST extends SCREENTEST    '** global variable container    'to hold/get mouse parameter    declare static function TestMouse() as long    static as long  gmX    static as long  gmY    static as long  gmBtnend type 'MOUSETEST <- SCREENTEST <- OBJECTtype BUTTON extends MOUSETEST    '** button object with delay    declare constructor()    declare constructor(byval as long, _                         byval as long, _                         byval as long, _                         byval as long, _                         byval as string)    declare property CenterXForText() as long    declare property CenterYForText() as long    declare property MouseClick() as boolean    declare sub TestButton()    declare sub DrawButton()    static as long      fontWidth    static as long      fontHeight    static as double    clickDelay        as long     _topLeftCornerX        as long     _topLeftCornerY        as long     _width        as long     _height        as string   _text    'private:            as boolean  _mouseOver        as boolean  _mouseClickend type 'BUTTON <- MOUSETEST <- SCREENTEST <- OBJECT'type_implemetation--------------'---------------------------------dim as integer SCREENTEST.scrW       => -1dim as integer SCREENTEST.scrH       => -1sub SCREENTEST.TestScreen()    screenInfo (SCREENTEST.scrW, SCREENTEST.scrH)end sub 'SCREENTEST.TestScreen()dim as long MOUSETEST.gmX       => -1dim as long MOUSETEST.gmY       => -1dim as long MOUSETEST.gmBtn     => -1function MOUSETEST.TestMouse() as long    '---->    return getMouse (MOUSETEST.gmX, _                      MOUSETEST.gmY, _                      , _                      MOUSETEST.gmBtn)end function 'LNG:=MOUSETEST.TestMouse()dim as long     BUTTON.fontWidth   => 8dim as long     BUTTON.fontHeight  => 8dim as double   BUTTON.clickDelay  => 0.8constructor BUTTON()    'note:    'default explicit constructor necessary    'if array of the object to be declared    'the implict constructor would not go    BASE()    '   dim as long scrW, scrH   screenInfo  scrW, scrH    with THIS        ._topLeftCornerX    => scrW\3        ._topLeftCornerY    => scrH\3        ._width             => scrW\3        ._height            => scrH\3        ._text              => left("default button", _                                     len("default button")*BUTTON.fontWidth\8)        ._mouseOver         => FALSE        ._mouseClick        => FALSE    end with 'THISend constructor 'BUTTON()constructor BUTTON(byval TLCX  as long, _                    byval TLCY  as long, _                    byval W     as long, _                    byval H     as long, _                    byval Text  as string)    BASE()    '    with THIS        ._topLeftCornerX    => TLCX        ._topLeftCornerY    => TLCY        ._width             => W        ._height            => H        ._text              => left(Text, _                                     len(Text)*BUTTON.fontWidth\8)        ._mouseOver         => FALSE        ._mouseClick        => FALSE    end with 'THISend constructor 'BUTTON({valLNG}*5,STR)property BUTTON.CenterXForText() as long    '---->    return THIS._topLeftCornerX + _           ( THIS._width - len(THIS._text)*BUTTON.fontWidth )\2end property 'get LNG:=BUTTON.CenterXForTextproperty BUTTON.CenterYForText() as long    '---->    return THIS._topLeftCornerY + _            ( THIS._height - BUTTON.fontHeight )\2end property 'get LNG:=BUTTON.CenterYForTextproperty BUTTON.MouseClick() as boolean    'to trigger an action at mouse click    'one should test this property    '---->    return (THIS._mouseOver and THIS._mouseClick)end property 'BOOL:=BUTTON.MouseClicksub BUTTON.TestButton()    static as double    clickTime    if TIMER<(clickTime + BUTTON.clickDelay) then         if THIS._mouseOver=TRUE then THIS._mouseOver = FALSE        exit sub    else        if THIS._mouseClick=TRUE then THIS._mouseClick = FALSE    end if    '    THIS.TestMouse()    '    if THIS.gmX>=THIS._topLeftCornerX                   and _        THIS.gmX<(THIS._topLeftCornerX + THIS._width)    and _        THIS.gmY>=THIS._topLeftCornerY                   and _        THIS.gmY<(THIS._topLeftCornerY + THIS._height)   then        if THIS._mouseOver=FALSE then THIS._mouseOver = TRUE        if THIS.gmBtn=+1    then            if THIS._mouseClick=FALSE then                 THIS._mouseClick    = TRUE                clickTime           = TIMER            end if        else            if THIS._mouseClick=TRUE then THIS._mouseClick = FALSE        end if    else        if THIS._mouseOver=TRUE then THIS._mouseOver = FALSE        if THIS._mouseClick=TRUE then THIS._mouseClick = FALSE    end if    end sub 'BUTTON.TestButton()sub BUTTON.DrawButton()    THIS.TestButton()    '    dim as long x   = THIS._topLeftCornerX    dim as long y   = THIS._topLeftCornerY    dim as long w   = THIS._width    dim as long h   = THIS._height    dim as string t = THIS._text    '    dim as ulong bckgColor    if  THIS._mouseClick    then        bckgColor  = rgb(100,190,140)    elseif THIS._mouseOver  then        bckgColor  = rgb(100,100,160)    else        bckgColor  = rgb(100,100,100)    end if        '    line (x,y)-step(w,h), , b    line (x + 1,y + 1)-step(w - 1,h - 1), bckgColor, bf    draw string (THIS.CenterXForText,THIS.CenterYForText), tend sub 'BUTTON.DrawButton()'subroutine_declaration----------'---------------------------------'(ui routine)declare function DrawWindowFrame(byval as fb.IMAGE ptr) as fb.IMAGE ptrdeclare sub DrawExitButton(byval as boolean=FALSE)declare function TestFrameWorkForMouse() as boolean'program_initialization----------'---------------------------------randomize TIMERscreenRes screenWidth, _           screenHeight, _           32, _           1, _           fb.GFX_NO_FRAME'note:'gfx screen should be initialized before imageSCREENTEST.TestScreen()dim as fb.IMAGE ptr drawingImgdim as fb.IMAGE ptr clearDrawingImgdrawingImg      => DrawWindowFrame(drawingImg)clearDrawingImg => DrawWindowFrame(clearDrawingImg)dim as long drawingZoneWdim as long drawingZoneHimageInfo drawingImg, drawingZoneW, drawingZoneHdim as fb.IMAGE ptr     tileImgtileImg =>  imageCreate(imgW, imgH, imgTransColor, 32)bLoad imgFileName, tileImgdim as BUTTON   resetBtn    => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 320, _                                      120, _                                       12, _                                       "Reset")dim as BUTTON   reduceXYBtn => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 300, _                                      120, _                                       12, _                                       "ReduceXY")dim as BUTTON   ShearXBtn   => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 280, _                                      120, _                                       12, _                                       "ShearAlongX")dim as BUTTON   TrgleXBtn   => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 260, _                                      120, _                                       12, _                                       "TriangleAlongX")dim as BUTTON   TrpzeXBtn   => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 240, _                                      120, _                                       12, _                                       "TrapezeAlongX")dim as BUTTON   LinearXYBtn  => BUTTON(SCREENTEST.scrW - 480, _                                       SCREENTEST.scrH - 200, _                                      120, _                                       12, _                                       "LinearXY")'program_main_loop---------------'---------------------------------dim as double   startTimedim as double   endTimedim as boolean  quitOrder   => FALSEdo    SCREENTEST.TestScreen()    MOUSETEST.TestMouse()   quitOrder   = TestFrameWorkForMouse()    screenLock       cls      imageInfo DrawWindowFrame(drawingImg), _                   drawingZoneW, _                   drawingZoneH      DrawExitButton(quitOrder)                put (060, 120), tileImg, TRANS                resetBtn.DrawButton()        if resetBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)                startTime   = TIMER                put (060, 120), tileImg, TRANS                endTime     = TIMER            draw string (10,40), "op. time="& str(cSng(endTime - startTime))            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                reduceXYBtn.DrawButton()        if reduceXYBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)            draw string (10,54), "pixel transfer.."                startTime   = TIMER                ReduceXY(tileImg, 0.5, 0.5)                endTime     = TIMER            draw string (10,40),  "op. time="& str(cSng(endTime - startTime))            if (endTime - startTime)>0.01 then                 draw string (140,54), "slow"            else                draw string (140,54), "fast"            end if            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                ShearXBtn.DrawButton()        if ShearXBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)            draw string (10,54), "pixel transfer.."                startTime   = TIMER                ShearAlongX(tileImg, 0.5, 0)                endTime     = TIMER            draw string (10,40),  "op. time="& str(cSng(endTime - startTime))            if (endTime - startTime)>0.01 then                 draw string (140,54), "slow"            else                draw string (140,54), "fast"            end if            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                TrgleXBtn.DrawButton()        if TrgleXBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)            draw string (10,54), "pixel transfer.."                startTime   = TIMER                TriangleAlongX(tileImg, TRUE, 0.5, 0)                endTime     = TIMER            draw string (10,40),  "op. time="& str(cSng(endTime - startTime))            if (endTime - startTime)>0.01 then                 draw string (140,54), "slow"            else                draw string (140,54), "fast"            end if            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                TrpzeXBtn.DrawButton()        if TrpzeXBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)            draw string (10,54), "pixel transfer.."                startTime   = TIMER                TrapezeAlongX(tileImg, 0.5, 1)                endTime     = TIMER            draw string (10,40),  "op. time="& str(cSng(endTime - startTime))            if (endTime - startTime)>0.01 then                 draw string (140,54), "slow"            else                draw string (140,54), "fast"            end if            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                        LinearXYBtn.DrawButton()        if LinearXYBtn.MouseClick then            DrawWindowFrame(clearDrawingImg)            draw string (10,54), "pixel transfer.."                startTime   = TIMER                LinearXY(tileImg)                endTime     = TIMER            draw string (10,40),  "op. time="& str(cSng(endTime - startTime))            if (endTime - startTime)>0.01 then                 draw string (140,54), "slow"            else                draw string (140,54), "fast"            end if            get (04,28)-step(drawingZoneW - 1,drawingZoneH -1), drawingImg        end if                   screenUnlock   if quitOrder then         '(quit)        imageDestroy tileImg      imageDestroy drawingImg      sleep 200      end 0   else        '(standard delay)      sleep 15   end ifloop until inkey=chr(27)    'program_finalization------------'---------------------------------'(explicit clean up)imageDestroy tileImgimageDestroy drawingImg'program_termination-------------'---------------------------------sleepend 0'subroutine_implementation-------'---------------------------------'(ui)function DrawWindowFrame(byval DrawingZone as fb.IMAGE ptr) as fb.IMAGE ptr   line (00,00)-(SCREENTEST.scrW - 1, SCREENTEST.scrH - 1), _          rgb(200,190,220), _          bf    for x as long = 0 to SCREENTEST.scrW\10        line (04 + 4*x,04)-(SCREENTEST.scrW - 1 - 4, 24), _              rgb(190 - x,200,090 + x), _              bf    next x    for x as long = 0 to SCREENTEST.scrW\10        line (04 + 2*x,24)-(SCREENTEST.scrW - 1 - 4 - 2*x, SCREENTEST.scrH - 1 - 4), _              rgb(100 + x,100 + x\2,120 + x), _              bf    next x    line (04,24)-(SCREENTEST.scrW - 1 - 4, 28), _          rgb(200,200,220), _          bf   draw string (08,12), _                 "TEST FRAMEWORK FOR PERSPECTIVE - FB1.04" & _                 " (note: window not movable not resizable)", _                 rgb(030,050,140)    if DrawingZone<>0 then        put (04,28), DrawingZone, TRANS    else        DrawingZone = imageCreate(SCREENTEST.scrW - 8, _                                   SCREENTEST.scrH - 32, _                                   imgTransColor, _                                   32)    end if    '    '---->    return DrawingZoneend function 'IMAGE_PTR:=DrawWindowFrame(valIMAGE_PTR)'sub DrawExitButton(byval QuitOrderSent as boolean=FALSE)   line (SCREENTEST.scrW - 1 - 4 - 18, 5)-(SCREENTEST.scrW - 1 - 5, 22), _          rgb(250,80,80), _          bf   draw string (SCREENTEST.scrW - 1 - 4 - 12,12), "X"   if QuitOrderSent then      line (SCREENTEST.scrW - 1 - 4 - 16, 7)- _              (SCREENTEST.scrW - 1 - 6, 20), _              rgb(140,120,080), _              bf   end ifend sub 'DrawExitButton()'function TestFrameWorkForMouse() _                         as boolean   dim as boolean QuitOrder   if MOUSETEST.gmX>(SCREENTEST.scrW - 1 - 4 - 18) and _      MOUSETEST.gmX<(SCREENTEST.scrW - 1 - 5) and _      MOUSETEST.gmY>5 and _       MOUSETEST.gmY<22 then         if MOUSETEST.gmBtn=+1 then          QuitOrder = TRUE         end if   else      QuitOrder = FALSE   end if   '---->   return QuitOrderend function 'BOOL:=TestFrameWorkForMouse()'(perspective routine)******************************************************************sub ReduceXY(byval Img as fb.IMAGE ptr, _             byval XScaleFactor as double=0.5, _              byval YScaleFactor as double=0.5)    for x as long = 0 to (imgW - 1)        for y as long = 0 to (imgH - 1)            pSet (500 + x*XScaleFactor, 120 + y*YScaleFactor), point(x,y,Img)        next y            next xend sub 'ReduceXY(valIMAGE_PTR,valDBL[0.5],valDBL[0.5])'sub ShearAlongX(byval Img as fb.IMAGE ptr, _                byval XscaleFactor as double=0.5, _                 byval OffsetXatYmax as long=0)    for x as long = 0 to (imgW - 1)        for y as long = 0 to (imgH - 1)            pSet (450 + x + y*XscaleFactor, 120 + y), point(x,y,Img)        next y            next xend sub 'ShearAlongX(valIMAGE_PTR,valDBL[0.5],valLNG)'sub TriangleAlongX(byval Img as fb.IMAGE ptr, _                   byval BaseAtBottom as boolean=TRUE, _                   byval XscaleFactor as double=0.5, _                    byval OffsetXatYmax as long=0)    for x as long = 0 to (imgW - 1)        for y as long = 0 to (imgH - 1)            pSet (500 + x*((imgH - 1) - y)/(2*(imgW - 1)), 120 + y), _                  point(x,y,Img)        next y            next xend sub 'TriangleAlongX(valIMAGE_PTR,valBOOL[-1],valDBL[0.5],valLNG)'sub TrapezeAlongX(byval Img as fb.IMAGE ptr, _                  byval XscaleFactorAtYmin as double=0.5, _                   byval XscaleFactorAtYmax as double=1)    for x as long = 0 to (imgW - 1)        for y as long = 0 to (imgH - 1)            pSet (550 + (x/2 - y*(imgW -1)/(4*(imgH - 1))) + 3*x*y/(8*(imgW - 1)), _                  120 + y), _                  point(x,y,Img)        next y            next xend sub 'TrapezeAlongX(valIMAGE_PTR,valDBL[0.5],valDBL)sub LinearXY(byval Img   as fb.IMAGE ptr)   dim as double   a   = 0.5   dim as double   b   = 100   dim as double   c   =  1/(2*(imgH - 1))   dim as double   d   = 1   dim as double   e   = 0.8   dim as double   f   = 50   dim as double   g   = 1/(4*(imgH - 1))   dim as double   h   = 1    for x as long = 0 to (imgW - 1)        for y as long = 0 to (imgH - 1)            pSet (550 + sqr(2)/2*cos(1)*x - sin(1)*y , _                   120 + 0.5*cos(1)*y + 1*sqr(2)/2*sin(1)*x ), _                  point(x,y,Img)                 '        next y            next xend sub 'LinearXY(valIMAGE_PTR)'[EOF]`

Conclusion
My final goal right now is, once determined a perspective grid, to build the set of distorted tiles that match the grid, and apply them!

Any help would be welcome. Feedback, ideas, improvements, or theory explanations from people that knows about those affairs, all welcome.

Solving this problem would possibly mean crafting perspective game very easily from a tile basis. First example we can think of is something like Doom of course, but not only, it's very open field...

(last thing: the programs posted above have compiled and worked for me (xp32 1.04). The grid program may have a memory leak, but not for an essential part, and it's fixed. What I mean is that this should run normally, though not in an optimized way. So if this is not working, this is an issue for itself, please give some feedback if you meet problems.)
Last edited by Tourist Trap on Feb 03, 2016 14:46, edited 1 time in total.
fxm
Posts: 9558
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

### Re: Tile - the perspective way

Two small remarks on coding:
- To be also compatible with 64-bit, all numeric parameters for 'ScrenInfo()' must be Integer.
- What is the usage of 'as OBJECT _fieldPlaceHolder' in types.
Posts: 2001
Joined: May 24, 2007 22:10
Location: The Netherlands

### Re: Tile - the perspective way

A few remarks:

1) Performance can be improved a lot, e.g. I see in the loop: pSet (550 + sqr(2)/2*cos(1)*x - sin(1)*y , 120 + 0.5*cos(1)*y + 1*sqr(2)/2*sin(1)*x ), point(x,y,Img) all these sines, conines and squares can be outside the loop.

2) Linear color-interpolation is not that hard, but additional calculation per pixel is then required. I'll try to post something later...

3) Is it not easier to use a 3d library and let the library and/or graphics card do all this work? 3d examples can be found on this forum.
Posts: 2001
Joined: May 24, 2007 22:10
Location: The Netherlands

### Re: Tile - the perspective way

Bilinear interpolation example:

Code: Select all

`screenres 800, 600, 32const as integer numTerrainTypes = 7const as integer xMapSize = 24\2, yMapSize = 18\2const as integer xTileSize = 32*2, yTileSize = 32*2dim as integer terrainColor(numTerrainTypes-1) = _   {&h0023719B, &h0047BD31, &h00DDEFEE, &h00766258, &h002F5319, &h00CBBC7F, &h001D3E57}type rbg_type   r as ubyte   g as ubyte   b as ubyte   a as ubyteend typeunion rgb_union   value as integer   comp as rbg_typeend unionfunction limit(value as integer, minimum as integer, maximum as integer) as integer   dim as integer result = value   if (result > maximum) then result = maximum   if (result < minimum) then result = minimum   return resultend functionsub drawBlock(x0 as integer, y0 as integer, pC as rgb_union ptr, noise as integer)   dim as integer x, y   dim as rgb_union cT, cB, c   dim as single xFactor1, xFactor2   dim as single yFactor1, yFactor2   for x = 0 to xTileSize-1      for y = 0 to yTileSize-1         xFactor1 = x / xTileSize         xFactor2 = 1 - xFactor1         cT.comp.r = int(xFactor2 * pC.comp.r + xFactor1 * pC.comp.r)         cT.comp.g = int(xFactor2 * pC.comp.g + xFactor1 * pC.comp.g)         cT.comp.b = int(xFactor2 * pC.comp.b + xFactor1 * pC.comp.b)         cB.comp.r = int(xFactor2 * pC.comp.r + xFactor1 * pC.comp.r)         cB.comp.g = int(xFactor2 * pC.comp.g + xFactor1 * pC.comp.g)         cB.comp.b = int(xFactor2 * pC.comp.b + xFactor1 * pC.comp.b)         yFactor1 = y / yTileSize         yFactor2 = 1 - yFactor1         c.comp.r = limit(int(yFactor2 * cT.comp.r + yFactor1 * cB.comp.r + (rnd - 0.5) * noise), 0, 255)         c.comp.g = limit(int(yFactor2 * cT.comp.g + yFactor1 * cB.comp.g + (rnd - 0.5) * noise), 0, 255)         c.comp.b = limit(int(yFactor2 * cT.comp.b + yFactor1 * cB.comp.b + (rnd - 0.5) * noise), 0, 255)         pset (x0 + x, y0 + y), c.value      next   nextend subdim as integer xMap, yMapdim as integer xTile, yTiledim as rgb_union c(4-1)dim as rgb_union cMap(xMapSize, yMapSize) 'not minus 1c(0).value = terrainColor(0) 'top leftc(1).value = terrainColor(3) 'top rightc(2).value = terrainColor(5) 'bottom leftc(3).value = terrainColor(1) 'bottom rightrandomize timerdim as integer itl, itr, ibl, ibrdim as string text, filenamedim as any ptr pImage = imagecreate(xTileSize, yTileSize)for xMap = 0 to xMapSize 'not minus 1   for yMap = 0 to yMapSize 'not minus 1      cMap(xMap, yMap).value = terrainColor(int(rnd*numTerrainTypes))   nextnextfor xMap = 0 to xMapSize-1   for yMap = 0 to yMapSize-1      c(0) = cMap(xMap, yMap)      c(1) = cMap(xMap + 1, yMap)      c(2) = cMap(xMap, yMap + 1)      c(3) = cMap(xMap + 1, yMap + 1)      drawBlock(xMap * (xTileSize + 1), yMap * (yTileSize + 1), @c(0), 20)   nextnext   sleepimagedestroy pImage`

Bilinear interpolation which is Linear interpolation twice (in 2 directions).
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Re: Tile - the perspective way

fxm wrote:- To be also compatible with 64-bit, all numeric parameters for 'ScrenInfo()' must be Integer.
- What is the usage of 'as OBJECT _fieldPlaceHolder' in types.

Thanks for the remarks. The second is unuseful place holder. About screeninfo(), I didn't know that. Not in the documentation anyway. Thanks again.

Thanks for the code illustration. Of course requires adaptation for the present case. I'm not sure that for tilling, trying magnification is not simply too risky since there are other problems than just interpolation, like conservation of contrast and so on.Taken as a whole, seems really not easy. Reduction is fortunately simpler.

badidea wrote:1) Performance can be improved a lot, e.g. I see in the loop: pSet (550 + sqr(2)/2*cos(1)*x - sin(1)*y , 120 + 0.5*cos(1)*y + 1*sqr(2)/2*sin(1)*x ), point(x,y,Img) all these sines, conines and squares can be outside the loop.

Fortunately this function is just a sandbox. In fact I was trying to find something linear in the common meaning of this term, but I was wrong. Perspective in general is not linear, it's homography, and it's linear in a very special meaning in projective spaces. Quite interesting to know indeed.

3) Is it not easier to use a 3d library and let the library and/or graphics card do all this work? 3d examples can be found on this forum.[/quote]
I'm affraid not. OpenGl is not that easy. It may also finally require the help of the gpu. And I dont want learn a third-party library and get bounded to hardware for so basic stuff. Of course I'm not against a tutorial on implementing a infinitely textured tiled plane in OpenGl if someone provided it!

New example game based on previous developments: Code: Select all

`'LION KING........................'---------------------------------'  a simple perspective grid game '---------------------------------'animated charconst as string      animatedCharacter = "tigerwood.bmp"const as long     animaW          =>  491const as long     animaH          =>  149'tileconst as long     imgW          =>  200const as long     imgH          =>  260const as ulong    imgTransColor => rgb(255,0,255)'screenconst as long     screenWidth   =>   800const as long     screenHeight  =>   400#include "fbgfx.bi"'---------------------------------type SCREENTEST extends OBJECT    'global variable container    'storing screen parameter    declare static sub TestScreen()    static as integer  scrW    static as integer  scrHend type 'SCREENTEST <- OBJECTdim as integer SCREENTEST.scrW       => -1dim as integer SCREENTEST.scrH       => -1sub SCREENTEST.TestScreen()    screenInfo (SCREENTEST.scrW, SCREENTEST.scrH)end sub 'SCREENTEST.TestScreen()type INTERACTIONTEST extends SCREENTEST    'global variable container    'storing mouse/keyboard interaction    declare static function TestMouse() as long    declare static function TestKeyboard() as long    static as long  gmX    static as long  gmY    static as long  gmBtn    static as long   scanCodeend type 'INTERACTIONTEST <- SCREENTEST <- OBJECTdim as long INTERACTIONTEST.gmX         => -1dim as long INTERACTIONTEST.gmY         => -1dim as long INTERACTIONTEST.gmBtn      => -1dim as long INTERACTIONTEST.scanCode   => -1function INTERACTIONTEST.TestMouse() as long    '---->    return getMouse (INTERACTIONTEST.gmX, _                      INTERACTIONTEST.gmY, _                      , _                      INTERACTIONTEST.gmBtn)end function 'LNG:=INTERACTIONTEST.TestMouse()function INTERACTIONTEST.TestKeyboard() as long   dim as long      scanCodeResult   = -1   if   multiKey(fb.SC_BACKSPACE)         then      scanCodeResult   =   fb.SC_BACKSPACE   elseif   multiKey(fb.SC_SPACE)         then      scanCodeResult   =    fb.SC_SPACE   elseif   multiKey(fb.SC_LEFT)   and multiKey(fb.SC_UP)      then      scanCodeResult   =    fb.SC_LEFT + fb.SC_UP   elseif   multiKey(fb.SC_LEFT)   and multiKey(fb.SC_DOWN)   then      scanCodeResult   =    fb.SC_LEFT + fb.SC_DOWN   elseif   multiKey(fb.SC_RIGHT)   and multiKey(fb.SC_UP)      then      scanCodeResult   =    fb.SC_RIGHT + fb.SC_UP   elseif   multiKey(fb.SC_RIGHT)   and multiKey(fb.SC_DOWN)   then      scanCodeResult   =    fb.SC_RIGHT + fb.SC_DOWN   elseif   multiKey(fb.SC_LEFT)         then      scanCodeResult   =    fb.SC_LEFT   elseif   multiKey(fb.SC_RIGHT)         then      scanCodeResult   =    fb.SC_RIGHT   elseif   multiKey(fb.SC_DOWN)         then      scanCodeResult   =   fb.SC_DOWN   elseif   multiKey(fb.SC_UP)            then      scanCodeResult   =   fb.SC_UP   end if   while inkey<>"" : /'clean keyboard buffer'/ :wend    '---->    return scanCodeResultend function 'LNG:=INTERACTIONTEST.TestMouse()'---------------------------------screenRes screenWidth, _           screenHeight, _           32color rgb(0,200,0)SCREENTEST.TestScreen()'load tiger frame!dim as fb.IMAGE ptr      tiger(4)for frameNum as long = 0 to 4   tiger(frameNum) => imageCreate(animaW\5 - 1, animaH - 1, imgTransColor, 32)next frameNumscope   dim as fb.IMAGE ptr      fullCharacterImage   fullCharacterImage => _          imageCreate(animaW, animaH, imgTransColor, 32)   bLoad animatedCharacter, fullCharacterImage   for frameNum as long = 0 to 4      get fullCharacterImage, _           (frameNum*(animaW\5),0)-step((animaW\5) - 1,animaH - 1), _           tiger(frameNum)   next frameNumend scopedim as double   viewAngle   = 1.0dim as long      viewRadius   = 200'---------------------------------dim as long      animationStep   => 0dim as long      animationSlower   => 0dim as long      sideStep      => 0dim as long      depthStep      => 0dim as long      jumpStep      => 0dim as boolean   jumpStarted      => FALSEdo   '------------------interaction   if jumpStarted then      animationStep = 4      if jumpStep<40 then          jumpStep   += 1         if jumpStep<20 then             viewAngle += 0.01            depthStep   += +12         else            viewAngle -= 0.01            depthStep   += +1         end if      else          viewAngle   = 1.0         jumpStep   = 0         jumpStarted   = FALSE      end if   else      if animationStep=4 then          animationStep   = 1         animationSlower   = 9      end if      animationSlower += 1      if animationSlower>10 then         animationSlower   = 0         animationStep   += 1         if    animationStep>=4 then    animationStep = 0      end if   end if   select case INTERACTIONTEST.TestKeyboard()      case fb.SC_SPACE         if not jumpStarted then jumpStarted = TRUE      case fb.SC_LEFT + fb.SC_UP         sideStep   += -2         depthStep   += +2      case fb.SC_LEFT + fb.SC_DOWN         sideStep   += -2         depthStep   += -2      case fb.SC_RIGHT + fb.SC_UP         sideStep   += +2         depthStep   += +2      case fb.SC_RIGHT + fb.SC_DOWN         sideStep   += +2         depthStep   += -2      case fb.SC_LEFT         sideStep   += -2      case fb.SC_RIGHT         sideStep   += +2      case fb.SC_DOWN         depthStep   += -2         case fb.SC_UP         depthStep   += +2      case else         animationSlower -= 1         sideStep   += 0         depthStep   += 0         end select 'INTERACTIONTEST.TestKeyboard()   '----------------------drawing   screenLock   cls   ? "Help the lion king to reach the great city of kings"   ? "<ARROW KEY> move .. <SPACE> jump .. <ESC> leave out"      'draw horizon-incident tile border   for i as long = 0 to 100      line (sideStep + SCREENTEST.scrW\2 - (i + 1/2)*imgW,SCREENTEST.scrH)- _       (SCREENTEST.scrW\2,SCREENTEST.scrH - viewRadius*tan(viewAngle)), rgb(080,170 - 4*i,090)      line (sideStep + SCREENTEST.scrW\2 + (i + 1/2)*imgW,SCREENTEST.scrH)- _       (SCREENTEST.scrW\2,SCREENTEST.scrH - viewRadius*tan(viewAngle)), rgb(080,170 - 4*i,090)         next i   'draw horizon-parallel tile border   for n as long = -1 to -150 step -1      dim as single   borderStep   = SCREENTEST.scrH\2 + (n - 1/2)*imgH + depthStep mod imgH      dim as single   cYn               dim as single   cZn               cYn   = + viewRadius*sin(viewAngle)*sin(viewAngle)*borderStep/ _                               (viewRadius - cos(viewAngle)*borderStep)      cZn   = - viewRadius*cos(viewAngle)*sin(viewAngle)*borderStep/ _                               (viewRadius - cos(viewAngle)*borderStep)      line (0,SCREENTEST.scrH - sqr(cYn^2 + cZn^2))-step(SCREENTEST.scrW,0), rgb(080,170 - n,090)   next n      put (SCREENTEST.scrW\2,SCREENTEST.scrH - animaH - animationStep*5), tiger(animationStep), TRANS   screenUnlock   '   sleep 15loop until inkey=chr(27)'---------------------------------sleepend 0'[end of file]`
Last edited by Tourist Trap on Feb 04, 2016 9:35, edited 1 time in total.
fxm
Posts: 9558
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

### Re: Tile - the perspective way

1.
Only non-derived types must have at least a non-static member variable.
Any type which extends another type (including 'Extends Object') can be empty of any own member variable.
=> you can suppress all 'as OBJECT _fieldPlaceHolder'.

2.
ScreenInfo declaration in documentation:
Declare Sub ScreenInfo ( ByRef w As Integer = 0, ByRef h As Integer = 0, ByRef depth As Integer = 0, ByRef bpp As Integer = 0, ByRef pitch As Integer = 0, ByRef rate As Integer = 0, ByRef driver As String = "" )
MrSwiss
Posts: 3445
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: Tile - the perspective way

Sorry to but in on ScreenInfo ...

Just because ScreenInfo wants Integer, and a lot of WIN programmers prefer Long instead,
I've coded the "getScrInfo Sub()" which wraps ScreenInfo and returns LONG(s), NOT Integer!!
Search Tips&Tricks for the Name given above ...
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Re: Tile - the perspective way

fxm wrote:1.
Only non-derived types must have at least a non-static member variable.
Any type which extends another type (including 'Extends Object') can be empty of any own member variable.
=> you can suppress all 'as OBJECT _fieldPlaceHolder'.
2.
ScreenInfo declaration in documentation:
Declare Sub ScreenInfo ( ByRef w As Integer = 0, ByRef h As Integer = 0, ByRef depth As Integer = 0, ByRef bpp As Integer = 0, ByRef pitch As Integer = 0, ByRef rate As Integer = 0, ByRef driver As String = "" )

This is fixed. But are you sure that not using Integer for ScreenInfo() parameters would crash a 64 bits OS? Shouldn't it be casted anyway? This not pointers, just simple numeric parameters here.
fxm
Posts: 9558
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

### Re: Tile - the perspective way

Tourist Trap wrote:This is fixed. But are you sure that not using Integer for ScreenInfo() parameters would crash a 64 bits OS?
Yes, it induces a compiler error for fbc 64-bit (checked).

Tourist Trap wrote:Shouldn't it be cast anyway? This not pointers, just simple numeric parameters here.
The problem occurs only when passing parameters by reference:
- No problem when passing by value, because compiler can do an implicit conversion before passing.
- When passing by reference, in fact a pointer is passed and value is written by dereferencing the pointer.
(not any possibility of casting)

In your 2nd code, other variables to be modified:
- dim as integer scrW, scrH ' in constructor BUTTON()
- dim as integer drawingZoneW ' in main code
- dim as integer drawingZoneH ' in main code
Last edited by fxm on Sep 02, 2017 20:15, edited 1 time in total.
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Re: Tile - the perspective way

fxm wrote:The problem occurs only when passing parameters by reference:
- No problem when passing by value, because compiler can do an implicit conversion before passing.
- When passing by reference, in fact a pointer is passed and value is written by dereferencing the pointer.
(not any possibility of casting)

I'm not sure I understand what is done exactly here. However thanks for the remark. I'm still convinced that the documentation is not insisting enough on the fact that there won't be any cast, and pointers are involved, so Integer type is mandatory. It could lead to many compatibility issues. Thanks for the corrections and remarks anyway.

Whatever!
I've made some progress with the perspective grid calculation. Here the new demo code. Have a try, any comment is welcome. Ah yes I need to post something about macros also. A rather annoying issue (fixed here).

Code: Select all

`'---------------------------------'rotation of a plane grid network 'provided 3D space eye parameter  '---------------------------------const as double _pi          => atn(1) * 4const as ulong   _lightGrey      => rgb(205,205,205)'tile constantconst as long     imgW          =>  12const as long     imgH          =>  15const as ulong    imgTransColor => rgb(255,0,255)'screen constantconst as long     screenWidth   =>   800const as long     screenHeight  =>   480#include "fbgfx.bi"#macro _ExitforOnEscapeKeyPressed    if inkey=chr(27) then exit for#endMacro'---------------------------------type SCREENTEST extends OBJECT    'global variable container    'storing screen parameter    declare static sub TestScreen()    static as integer  scrW    static as integer  scrHend type 'SCREENTEST <- OBJECTdim as integer SCREENTEST.scrW       => -1dim as integer SCREENTEST.scrH       => -1sub SCREENTEST.TestScreen()    screenInfo (SCREENTEST.scrW, SCREENTEST.scrH)end sub 'SCREENTEST.TestScreen()type INTERACTIONTEST extends SCREENTEST    'global variable container    'storing mouse/keyboard interaction    declare static function TestMouse() as long    declare static function TestKeyboard() as long    static as long  gmX    static as long  gmY    static as long  gmBtn    static as long   scanCodeend type 'INTERACTIONTEST <- SCREENTEST <- OBJECTdim as long INTERACTIONTEST.gmX         => -1dim as long INTERACTIONTEST.gmY         => -1dim as long INTERACTIONTEST.gmBtn      => -1dim as long INTERACTIONTEST.scanCode   => -1function INTERACTIONTEST.TestMouse() as long    '---->    return getMouse (INTERACTIONTEST.gmX, _                      INTERACTIONTEST.gmY, _                      , _                      INTERACTIONTEST.gmBtn)end function 'LNG:=INTERACTIONTEST.TestMouse()function INTERACTIONTEST.TestKeyboard() as long   dim as long      scanCodeResult   = -1   if   multiKey(fb.SC_BACKSPACE)         then      scanCodeResult   =   fb.SC_BACKSPACE   elseif   multiKey(fb.SC_SPACE)         then      scanCodeResult   =    fb.SC_SPACE   elseif   multiKey(fb.SC_LEFT)   and multiKey(fb.SC_UP)      then      scanCodeResult   =    fb.SC_LEFT + fb.SC_UP   elseif   multiKey(fb.SC_LEFT)   and multiKey(fb.SC_DOWN)   then      scanCodeResult   =    fb.SC_LEFT + fb.SC_DOWN   elseif   multiKey(fb.SC_RIGHT)   and multiKey(fb.SC_UP)      then      scanCodeResult   =    fb.SC_RIGHT + fb.SC_UP   elseif   multiKey(fb.SC_RIGHT)   and multiKey(fb.SC_DOWN)   then      scanCodeResult   =    fb.SC_RIGHT + fb.SC_DOWN   elseif   multiKey(fb.SC_LEFT)         then      scanCodeResult   =    fb.SC_LEFT   elseif   multiKey(fb.SC_RIGHT)         then      scanCodeResult   =    fb.SC_RIGHT   elseif   multiKey(fb.SC_DOWN)         then      scanCodeResult   =   fb.SC_DOWN   elseif   multiKey(fb.SC_UP)            then      scanCodeResult   =   fb.SC_UP   end if   while inkey<>"" : /'clean keyboard buffer'/ :wend    '---->    return scanCodeResultend function 'LNG:=INTERACTIONTEST.TestMouse()type POINT2D extends SCREENTEST   declare constructor()   declare constructor(byval as double, _                   byval as double, _                   byval as string, _                   byval as ulong=rgb(205,205,205))   declare operator Let(byval as long)   declare property OutOfScreen() as boolean   declare property DistanceToOrigin() as double   declare sub DrawPoint2D()   static as long      constructionCount      as double      _x      as double      _y      as string      _id      as ulong      _colorend type 'POINT2D <- SCREENTEST <- OBJECTdim as long      POINT2D.constructionCountconstructor POINT2D()   BASE()   POINT2D.constructionCount   += 1   THIS.TestScreen()   '   with THIS      ._x      => SCREENTEST.scrW\2      ._y      => SCREENTEST.scrH\2      ._id   => "point" & str(POINT2D.constructionCount)      ._color   => rgb(205,205,205)   end with 'THISend constructor 'POINT2D default explicit constructorconstructor POINT2D(byval X      as double, _                byval Y      as double, _                byval Id   as string, _                byval C      as ulong=rgb(205,205,205))   BASE()   '   with THIS      ._x      => X      ._y      => Y      ._id   => Id      ._color   => C   end with 'THISend constructor 'POINT2D(valDBL,valDBL,valSTR,valULNG[rgb(205,205,205)])operator POINT2D.Let(byval SameForAllCoordinate as long)   with THIS      ._x      =   SameForAllCoordinate      ._y      =   SameForAllCoordinate   end with 'THISend operator 'POINT2D:=valLNGproperty POINT2D.OutOfScreen() as boolean   THIS.TestScreen()   '   if not ( THIS._x>=0   and THIS._x<=THIS.scrW      and _           THIS._y>=0   and THIS._y<=THIS.scrH )   then      '---->      return TRUE   else      '---->      return FALSE   end if end property 'get BOOL:=POINT2D.OutOfScreenproperty POINT2D.DistanceToOrigin() as double      '---->      return sqr(THIS._x*THIS._x +  THIS._y*THIS._y)end property 'get DBL:=POINT2D.DistanceToOriginsub POINT2D.DrawPoint2D()   THIS.TestScreen()   '   if THIS._x>=0 and  THIS._x<=THIS.scrW and _       THIS._y>=0 and  THIS._y<=THIS.scrH then      circle (THIS._x, THIS._y), 4, THIS._color      circle (THIS._x, THIS._y), 2, THIS._color      draw string (THIS._x - 4, THIS._y - 12), THIS._id   end if end sub 'POINT2D.DrawPoint2D()type SCREENBIPOINTLINE extends SCREENTEST   declare constructor()   declare constructor(byval as POINT2D, _                   byval as POINT2D=type<POINT2D>(0,0,"o"), _                   byval as ulong=rgb(205,205,205))   declare sub DrawLine()   declare sub DrawLineWithPoint()      as POINT2D      _p1      as POINT2D      _p2      as ulong      _lineColorend type 'SCREENBIPOINTLINE <- SCREENTEST <- OBJECTconstructor SCREENBIPOINTLINE()   BASE()   THIS.TestScreen()   '   with THIS      ._p1      => 0      ._p2      => type<POINT2D>(THIS.scrW,THIS.scrH,"Corner")      ._lineColor   => rgb(205,205,205)   end with 'THISend constructor 'SCREENBIPOINTLINE default explicit constructorconstructor SCREENBIPOINTLINE(byval P1   as POINT2D, _                        byval P2   as POINT2D=type<POINT2D>(0,0,"o"), _                        byval LC   as ulong=rgb(205,205,205))   with THIS      ._p1      => P1      ._p2      => P2      ._lineColor   => LC   end with 'THISend constructor 'SCREENBIPOINTLINE(valPOINT2D,valPOINT2D[type<POINT2D>(0,0,"o"),valULNG[rgb(205,205,205)])sub SCREENBIPOINTLINE.DrawLine()   THIS.TestScreen()   '   dim as boolean   case1   => FALSE   dim as boolean   case2   => FALSE   dim as boolean   case3   => FALSE   dim as boolean   case4   => FALSE   if ( (THIS._p1)._x=(THIS._p2)._x and (THIS._p1)._y=(THIS._p2)._y ) then      case1   = TRUE      'no line to draw      exit sub   elseif _       ( (THIS._p1)._x=(THIS._p2)._x and (THIS._p1)._y<>(THIS._p2)._y ) then      case2   = TRUE      if (THIS._p1)._x>=0 and (THIS._p1)._x<=THIS.scrW then         dim as POINT2D   i(1 to 2)         i(1)   => POINT2D((THIS._p1)._x, 0, "y=0")         i(2)   => POINT2D((THIS._p1)._x, THIS.scrH, "y=scrH")         line (i(1)._x, i(1)._y)- _              (i(2)._x, i(2)._y), _              THIS._lineColor      else         'no line to draw         exit sub      end if   elseif _       ( (THIS._p1)._x<>(THIS._p2)._x and (THIS._p1)._y=(THIS._p2)._y ) then      case3   = TRUE      if (THIS._p1)._y>=0 and (THIS._p1)._y<=THIS.scrH then         dim as POINT2D   i(1 to 2)         i(1)   => POINT2D(0, (THIS._p1)._y, "x=0")         i(2)   => POINT2D(THIS.scrW, (THIS._p1)._y, "x=scrW")         line (i(1)._x, i(1)._y)- _              (i(2)._x, i(2)._y), _              THIS._lineColor      else         'no line to draw         exit sub      end if     elseif _       ( (THIS._p1)._x<>(THIS._p2)._x and (THIS._p1)._y<>(THIS._p2)._y ) then      case4   = TRUE      dim as double   x1   => (THIS._p1)._x      dim as double   x2   => (THIS._p2)._x      dim as double   y1   => (THIS._p1)._y      dim as double   y2   => (THIS._p2)._y      'compute intersection with screen border      dim as POINT2D   i(1 to 4)      i(1)   => POINT2D(x1 - y1*(x2 - x1)/(y2 - y1), 0, "y=0")      i(2)   => POINT2D(x1 + (THIS.scrH - y1)*(x2 - x1)/(y2 - y1), THIS.scrH, "y=scrH")      i(3)   => POINT2D(0, y1 - x1*(y2 - y1)/(x2 - x1), "x=0")      i(4)   => POINT2D(THIS.scrW, y1 + (THIS.scrW - x1)*(y2 - y1)/(x2 - x1), "x=scrW")      'decide what couple of point to join      dim as long   j(1 to 2)      for index as long = 1 to 4            j(1) => index            exit for      next index      for index as long = 1 to 4         if i( j(1) )._x<>i(index)._x or i( j(1) )._y<>i(index)._y then               j(2) => index               exit for         end if      next index      'draw line      line (i( j(1) )._x, i( j(1) )._y)- _           (i( j(2) )._x, i( j(2) )._y), _           THIS._lineColor   end ifend sub 'SCREENBIPOINTLINE.DrawLine()sub SCREENBIPOINTLINE.DrawLineWithPoint()   (THIS._p1).DrawPoint2D()   (THIS._p2).DrawPoint2D()   '   THIS.DrawLine()end sub 'SCREENBIPOINTLINE.DrawLineWithPoint()'---------------------------------screenRes screenWidth, _           screenHeight, _           32color rgb(0,200,0)SCREENTEST.TestScreen()'---------------------------------'initial plane griddim as SCREENBIPOINTLINE   arrayOfVerticalLine(9)/'for index as long = 0 to uBound(arrayOfVerticalLine)   dim as long   n => index - (uBound(arrayOfVerticalLine) + 1)\2   arrayOfVerticalLine(index)   => _    SCREENBIPOINTLINE(type<POINT2D>(SCREENTEST.scrW\2 + imgW*(1/2 - sgn(n)*n)*sgn(n), 0, "!"), _                  type<POINT2D>(SCREENTEST.scrW\2 + imgW*(1/2 - sgn(n)*n)*sgn(n), 1, "!"), _                  iif(n<>0,rgb(90,70,150),rgb(90,70,190)) )   arrayOfVerticalLine(index).DrawLine()next index'/dim as SCREENBIPOINTLINE   arrayOfHorizontalLine(9)/'for index as long = 0 to uBound(arrayOfHorizontalLine)   dim as long   n => index - (uBound(arrayOfHorizontalLine) + 1)\2   arrayOfHorizontalLine(index)   => _    SCREENBIPOINTLINE(type<POINT2D>(0, SCREENTEST.scrH/2 + sgn(n)*imgH*(1/2 - sgn(n)*n), "!"), _                  type<POINT2D>(1, SCREENTEST.scrH/2 + sgn(n)*imgH*(1/2 - sgn(n)*n), "!"), _                  iif(n<>0,rgb(150,70,90),rgb(190,70,90)) )   arrayOfHorizontalLine(index).DrawLine()next index'/'---------------------------------'observation point parameterdim as double   rE   => 100.dim as double   aE   => 55*_pi/180'source plane grid orientationdim as double   rA   => 100dim as double   aA   => 35.*_pi/180'---------------------------------'---------------------------------'---------------------------------dim as double      angleStep => .01do   aA   += angleStep   if aA>(_pi/2)   then angleStep *= -1   if aA<00      then angleStep *= -1      screenLock   cls   'parameter of intersection with the screen   ' *AE inter screen_plane* = IA   ' *BE inter screen_plane* = IB   dim as double   pA   => -rE*1/(rA*sin(aA)*cos(aE) + rE)   dim as double   pB   => -rE*1/(rA*cos(aA)*cos(aE) + rE)      dim as double   xIA => 0000000000 - pA*( rA*cos(aA) )   dim as double   yIA => rE*cos(aE) - pA*( - rA*sin(aA) - rE*cos(aE))   dim as double   zIA => rE*sin(aE)*(1 + pA)      dim as double   xIB => 000000000 - pB*( -rA*sin(aA) )   dim as double   yIB => rE*cos(aE) - pB*( -rA*cos(aA) - rE*cos(aE))   dim as double   zIB => rE*sin(aE)*(1 + pB)      'screen plane grid main X,Y axis after rotation   dim as double   alfa   => acos( xIA/(sqr( xIA^2 + yIA^2 + zIA^2 )) )   dim as double   beta   => acos( xIB/(sqr( xIB^2 + yIB^2 + zIB^2 )) )      dim as double   xHA      => rE*tan(aE)/tan(alfa)   dim as double   yHA      => -rE*tan(aE)      dim as double   xHB      => rE*tan(aE)/tan(beta)   dim as double   yHB      => -rE*tan(aE)      dim as SCREENBIPOINTLINE   horizon   horizon   => SCREENBIPOINTLINE(type<POINT2D>(0,yHA + SCREENTEST.scrH\2,"h0"), _                          type<POINT2D>(SCREENTEST.scrW,yHA + SCREENTEST.scrH\2,"h1"), _                          rgb(100,190,120) )      'draw horizon line      horizon.DrawLineWithPoint()      dim as SCREENBIPOINTLINE   xAxis   xAxis   => SCREENBIPOINTLINE(type<POINT2D>(0 + SCREENTEST.scrW\2,0 + SCREENTEST.scrH\2,"o"), _                          type<POINT2D>(xHA + SCREENTEST.scrW\2,yHA + SCREENTEST.scrH\2,"HA", rgb(180,100,100)))      'draw X axis rotated      'xAxis.DrawLineWithPoint()      dim as SCREENBIPOINTLINE   yAxis   yAxis   => SCREENBIPOINTLINE(type<POINT2D>(0 + SCREENTEST.scrW\2,0 + SCREENTEST.scrH\2,"o"), _                          type<POINT2D>(xHB + SCREENTEST.scrW\2,yHB + SCREENTEST.scrH\2,"HB", rgb(100,100,180)))      'draw Y axis rotated      'yAxis.DrawLineWithPoint()      'rotated grid line in the original grid plane      'vertical grid line equation      #macro _X(n)         (sgn(n)*imgW*(1/2 - sgn(n)*n) + SCREENTEST.scrW\2)      #endMacro      'horizontal grid line equation      #macro _Y(n)         (sgn(n)*imgH*(1/2 - sgn(n)*n) + SCREENTEST.scrH\2)      #endMacro      'rotation of vertical grid line      #macro _XRV(n,t)         ((_X(n) - SCREENTEST.scrW\2)*cos(_pi - aA) - (t)*sin(_pi - aA) + SCREENTEST.scrW\2)      #endMacro      #macro _YRV(n,t)         ((_X(n) - SCREENTEST.scrW\2)*sin(_pi - aA) + (t)*cos(_pi - aA) + SCREENTEST.scrH\2)      #endMacro      'rotation of horizontal grid line      #macro _XRH(n,t)         ((t)*cos(-aA) - (_Y(n) - SCREENTEST.scrH\2)*sin(-aA)  + SCREENTEST.scrW\2)      #endMacro      #macro _YRH(n,t)         ((t)*sin(-aA) + (_Y(n) - SCREENTEST.scrH\2)*cos(-aA) + SCREENTEST.scrH\2)      #endMacro            'display vertical point for n=0, t=100      '(type<POINT2D>(_XRV(0,100), _YRV(0,100), "vert0,1", rgb(0,0,120))).DrawPoint2D()      'display vertical point for n=0, t=200      (type<POINT2D>(_XRV(0,200), _YRV(0,200), "vert0,2", rgb(0,0,120))).DrawPoint2D()      'display vertical line      '(type<SCREENBIPOINTLINE> _       '(type<POINT2D>(_XRV(0,-100), _YRV(0,-100),""),type<POINT2D>(_XRV(0,200), _YRV(0,200),""), rgb(0,0,120))).DrawLine()         'display horizontal point for n=0, t=100      '(type<POINT2D>(_XRH(0,100), _YRH(0,100), "hori0,1", rgb(190,0,0))).DrawPoint2D()      'display horizontal point for n=0, t=200      (type<POINT2D>(_XRH(0,200), _YRH(0,200), "hori0,2", rgb(190,0,0))).DrawPoint2D()      'display horizontal line      '(type<SCREENBIPOINTLINE> _       '(type<POINT2D>(_XRH(0,-100), _YRH(0,-100),""),type<POINT2D>(_XRH(0,200), _YRH(0,200),""), rgb(150,0,0))).DrawLine()      /'      for n as long = -10 to 10         'display vertical line         (type<SCREENBIPOINTLINE> _          (type<POINT2D> _          (_XRV(n,-100), _YRV(n,-100),""),type<POINT2D>(_XRV(n,200), _YRV(n,200),""), rgb(0,0,120))).DrawLine()         'display horizontal line         (type<SCREENBIPOINTLINE> _          (type<POINT2D>_          (_XRH(n,-100), _YRH(n,-100),""),type<POINT2D>(_XRH(n,200), _YRH(n,200),""), rgb(150,0,0))).DrawLine()            next n   '/         'angle check      #macro _BIPOINTDIST(p1P2D,p2P2D)         sqr( (p2P2D._x - p1P2D._x)^2 + (p2P2D._y - p1P2D._y)^2 )      #endMacro      #macro _VECTANGLE(centerP2D,p1P2D,p2P2D)         acos( ( (p1P2D._x - centerP2D._x)*(p2P2D._x - centerP2D._x) + _                 (p1P2D._y - centerP2D._y)*(p2P2D._y - centerP2D._y) ) / _                 (_BIPOINTDIST(centerP2D,p1P2D)*_BIPOINTDIST(centerP2D,p2P2D)) )      #endMacro      dim as POINT2D   scrCenter      scrCenter._x   => SCREENTEST.scrW\2      scrCenter._y   => SCREENTEST.scrH\2      dim as POINT2D   vert00      vert00._x      => _XRV(0,100)      vert00._y      => _YRV(0,100)      dim as POINT2D   hori00      hori00._x      => _XRH(0,100)      hori00._y      => _YRH(0,100)      dim as POINT2D   xAxisUnit      xAxisUnit._x   => SCREENTEST.scrW\2 + 1      xAxisUnit._y   => SCREENTEST.scrH\2            ? _VECTANGLE(scrCenter,xAxisUnit,hori00) *180/_pi         ? _VECTANGLE(scrCenter,xAxisUnit,vert00) *180/_pi         'compute and store intersection of rotated grid line with X axis         #macro _RHX(n)      ( _XRH(n,0) + ( SCREENTEST.scrH\2 - _YRH(n,0)) * ( _XRH(n,1) - _XRH(n,0) )/( _YRH(n,1) - _YRH(n,0) ) )   #endMacro   #macro _RVX(n)      ( _XRV(n,0) + ( SCREENTEST.scrH\2 - _YRV(n,0)) * ( _XRV(n,1) - _XRV(n,0) )/( _YRV(n,1) - _YRV(n,0) ) )   #endMacro      /'   for n as long = -5 to 5      'horizontal case      (type<SCREENBIPOINTLINE> _       (type<POINT2D>(_XRH(n,0), _YRH(n,0),""),type<POINT2D>(_XRH(n,1), _YRH(n,1),""), rgb(200,150,0))).DrawLine()      (type<POINT2D>(_RHX(n) , SCREENTEST.scrH\2, "h"& str(n), rgb(220,100,100))).DrawPoint2D()            'vertical case      (type<SCREENBIPOINTLINE> _       (type<POINT2D>(_XRV(n,0), _YRV(n,0),""),type<POINT2D>(_XRV(n,1), _YRV(n,1),""), rgb(0,150,200))).DrawLine()      (type<POINT2D>(_RVX(n) , SCREENTEST.scrH\2, "v"& str(n), rgb(100,100,220))).DrawPoint2D()   next n   '/       'draw perspective grid   dim as POINT2D   xAxisHorizonPoint   xAxisHorizonPoint._x   => xHA  + SCREENTEST.scrW\2   xAxisHorizonPoint._y   => yHA  + SCREENTEST.scrH\2      dim as POINT2D   yAxisHorizonPoint   yAxisHorizonPoint._x   => xHB  + SCREENTEST.scrW\2   yAxisHorizonPoint._y   => yHB  + SCREENTEST.scrH\2      for n as long = -45 to 45      if n=0 then continue for            'horizontal line      (type<SCREENBIPOINTLINE> _       (type<POINT2D>(_RHX(n) , SCREENTEST.scrH\2,""),xAxisHorizonPoint, rgb(200,150,200))).DrawLine()            'vertical case      (type<SCREENBIPOINTLINE> _       (type<POINT2D>(_RVX(n) , SCREENTEST.scrH\2,""),yAxisHorizonPoint, rgb(200,150,200))).DrawLine()   next n      'draw sky box      line (0,0)-(SCREENTEST.scrW, yHA  + (100/95)*SCREENTEST.scrH\2), rgb(140,150,180), bf      'draw view center      circle(SCREENTEST.scrW\2, SCREENTEST.scrH\2), 8, rgb(220,140,155), , , , f      /'   ? sqr( xIA^2 + (yIA - rE*cos(aE))^2 + (zIA - rE*sin(aE))^2 )   ? "EA="; sqr( (rA*cos(aA))^2 + (-rA*sin(aA) - rE*cos(aE))^2 + ( -rE*sin(aE))^2 )   ? sqr( xIB^2 + (yIB - rE*cos(aE))^2 + (zIB - rE*sin(aE))^2 )   ? "EB="; sqr( (-rA*sin(aA))^2 + (-rA*cos(aA) - rE*cos(aE))^2 + ( -rE*sin(aE))^2 )   '/   screenUnlock      '   sleep 75loop until inkey=chr(27)sleepend 0`
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

### Re: Tile - the perspective way

very interesting! keep going, Mr. Trap
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Re: Tile - the perspective way

leopardpm wrote:very interesting! keep going, Mr. Trap

Hi leopardpm,

Yes that's funny.

I will try continue this as soon as possible. However I've not found yet the way to transform a rectangle tile to any quadrangle. I'm sure I'm missing a tiny trick, but not found yet. And for the grid projection I will need to rewrite it from start now that I can see what it looks like. There are one or two weak points that I need to get reinforced in all those intersections formulas.

So it's potentially long, and I'm right now fighting with files to make a communication system between two programs Passing messages to a unique file on disk. Doing that clean (checking file possible errors) is not that easy so far...
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

### Re: Tile - the perspective way

Tourist Trap wrote:So it's potentially long, and I'm right now fighting with files to make a communication system between two programs Passing messages to a unique file on disk. Doing that clean (checking file possible errors) is not that easy so far...
sounds like a useful routine... start another thread!
Tourist Trap
Posts: 2850
Joined: Jun 02, 2015 16:24

### Re: Tile - the perspective way

Yes you're right of course. Here is it:
• http://www.freebasic.net/forum/viewtopic.php?f=7&t=24604
• http://www.freebasic.net/forum/viewtopic.php?f=8&t=24578
dodicat
Posts: 6377
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: Tile - the perspective way

A grid, similar to tourist trap's.

Code: Select all

`Screen 20#include "crt.bi"Type sincos    As Single s,cEnd TypeType v3    As Single x,y,z    As Ulong col    Declare Property length As Single    Declare Property unit As v3    Declare Function AxialRotate(As v3,As sincos,As V3) As v3    Declare Function perspective(eyepoint As v3) As v3    #define vct Type<v3>    #define dot *    #define cross ^End Type#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)Operator + (v1 As v3,v2 As v3) As v3Return Type<v3>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)End OperatorOperator -(v1 As v3,v2 As v3) As v3Return Type<v3>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z)End OperatorOperator * (f As Single,v1 As v3) As v3 Return vct(f*v1.x,f*v1.y,f*v1.z)End OperatorOperator *(v1 As v3,f As Single) As v3Return f*v1End OperatorOperator * (v1 As v3,v2 As v3) As Single 'dot productReturn v1.x*v2.x+v1.y*v2.y+v1.z*v2.zEnd OperatorOperator ^ (v1 As v3,v2 As v3) As v3     'cross productReturn Type<v3>(v1.y*v2.z-v2.y*v1.z,-(v1.x*v2.z-v2.x*v1.z),v1.x*v2.y-v2.x*v1.y)End OperatorOperator /(v1 As v3,n As Single) As v3Return Type<v3>(v1.x/n,v1.y/n,v1.z/n)End OperatorProperty v3.length As SingleReturn Sqr(this.x*this.x+this.y*this.y+this.z*this.z)End PropertyProperty v3.unit As v3Dim n As Single=this.lengthIf n=0 Then n=1e-20Return This/nEnd PropertyFunction v3.AxialRotate(centre As v3,Angle As sincos,norm As V3) As v3    Dim As v3 V=This-centre    Var ret= (V*angle.C+(Norm cross V)*angle.S+Norm*(Norm dot V)*(1-angle.c))+centre     Return vct(ret.x,ret.y,ret.z,col)End FunctionFunction v3.perspective(eyepoint As v3) As v3    Dim As Single   w=1+(this.z/eyepoint.z)    If w=0 Then w=1e-20    Var ret= eyepoint+(This-eyepoint)/w    Return vct(ret.x,ret.y,ret.z,col)End FunctionFunction Regulate(Byval MyFps As long,Byref fps As long) As long    Static As Double timervalue,lastsleeptime,t3,frames    Dim As Double t=Timer    frames+=1    If (t-t3)>=1 Then t3=t:fps=frames:frames=0    Dim As long sleeptime=lastsleeptime+((1/myfps)-T+timervalue)*1000    If sleeptime<1 Then sleeptime=1    lastsleeptime=sleeptime    timervalue=T    Return sleeptimeEnd FunctionSub drawarray(a() As V3)    For n As long=Lbound(a) To Ubound(a)-2 Step 2        if n<> ubound(a)\2-1 then Line(a(n).x,a(n).y)-(a(n+1).x,a(n+1).y)        Dim As long rad=map(-900,900,a(n).z,8,2)       if n<> ubound(a)\2-1 then Circle(a(n).x,a(n).y),rad,5,,,,f        rad=map(-900,900,a(n+1).z,8,2)       if n<> ubound(a)\2-1 then Circle(a(n+1).x,a(n+1).y),rad,4,,,,f    Next nEnd Sub'========================================================Redim As V3 pts()Dim As Single pi=4*Atn(1)'create all the pointsDim As long ctr,fpsDim As Long k=400,yyFor n As long=1 To 200 Step 4    ctr+=1    k=-k    Redim Preserve pts(1 To ctr)    pts(ctr)=Type<V3>(10*ctr+262,768\2,-k)Next nreDim As V3 rot(Lbound(pts) To Ubound(pts))var tmp=ubound(pts)'create another set, 90 degrees to first set.For n As long=Lbound(pts) To Ubound(pts)    rot(n)=pts(n).axialrotate(Type(1024/2,768/2,0),Type<sincos>(Sin(pi/2),Cos(pi/2)),Type<v3>(0,1,0))    next nredim as V3 T(1 to tmp*2)'add the two setsmemcpy(@T(1),@pts(1),(Ubound(pts)-Lbound(pts)+1)*Sizeof(V3))memcpy(@T(Ubound(pts)),@rot(1),(Ubound(rot)-Lbound(rot)+1)*Sizeof(V3))Dim As v3 axis=(0,1,0)'now tilt this y axis forwardaxis=axis.axialrotate(Type<v3>(0,0,0),Type<sincos>(Sin(.4),Cos(.4)),Type<v3>(1,0,0))axis=axis.unit'tilt all the points the same angle as the y axis (above)For n As long=Lbound(T) To Ubound(T)    T(n)=T(n).axialrotate(Type(1024/2,768/2,0),Type<sincos>(Sin(.4),Cos(.4)),Type<v3>(1,0,0))Next nredim rot(lbound(t) to ubound(T))Dim As sincos angDim As Single zDo    z+=.01    ang=Type<sincos>(Sin(z),Cos(z))    Screenlock    Cls    Draw String(50,50),"FPS = " &fps    For n As long=Lbound(T) To Ubound(T)        rot(n)=T(n).axialrotate(Type<v3>(1024\2,768\2,0),ang,axis)        rot(n)=rot(n).perspective(Type<v3>(1024\2,768\2,900))    Next n    drawarray(rot())        Screenunlock    Sleep regulate(50,fps)Loop Until inkey=chr(27)Print Ubound(pts)Sleep `