|
Forum Index : Microcontroller and PC projects : PicoMiteVGA: Framework for ray casting using the DDA method
| Author | Message | ||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5823 |
Hi Mick, Both you and Tom are right. If I look at (but .. who am I ?) my personal application domain, I would - on RP2040 VGA have loved to trade RAY with STRUCTURES. - on RP2040 microcontroller domain have loved to trade STEPPER for STRUCTURES. - on any RP2040 would have dismissed TURTLE. But that is me. I do not see the value in structures. But I know from the enthusiastic communication at the introduction of structures, that many do love it. And TURTLE has it's own audience (my grand children are to grown up, but they may have loved it once). Maybe for my grand-grand children in 10 years. Anyway. It is what it is. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| mozzie Senior Member Joined: 15/06/2020 Location: AustraliaPosts: 251 |
G'day, It probably shows the strength of the whole MMbasic / Picomite platform that with so many different users and use cases, it appears to satisfy 99% of them 99% of the time. This is a huge ask from any system. I too look at a MicroMite / PicoMite as an embedded system, but am amazed at what can be done with some of the extended functions we now have access to with HDMI / VGA graphics and so many LCD displays. On top of this we have an enormous amount of device support without resorting to libraries etc. Whilst my own abilities as a programmer are pretty poor, trying out the new commands / functions / systems as they are added and reading the posts of how others are using them has taught me a lot, and inspired many ideas, whether I will ever use some of it is another matter. Also only my 5c Regards, Lyle. |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
The freely scalable sprites certainly have their appeal. They would also be useful in other applications (such as Leon's Outrun port etc.) ym2c Martin 'no comment |
||||
| gadgetjack Senior Member Joined: 15/07/2016 Location: United StatesPosts: 206 |
Just putting in my 2 cents worth. I understand all the ones using this for control projects , I did that for years till I retired. But the graphics capabilities of this little machine have caught my eye from the beginning. I have followed now for a few years after finding this site and really enjoy the games and demo programs so many capable people on here write. I tip my hat to all of you. I really would like to see the RAY functions added , they would not eat that much memory up. Most designs have SD cards anyway for saving your code to. I vote for the RAY functions. Jack |
||||
| bfwolf Senior Member Joined: 03/01/2025 Location: GermanyPosts: 208 |
The discussion here about whether "ray casting" should be done via built-in commands, CSUBs, or directly in MMBasic, has once again shown me how beneficial it would be if MMBasic supported DLLs and their subs and functions. This would allow for the elegant and efficient implementation of highly specialized tasks suitable only for a selected user group and very specific use cases. And the MMBasic core wouldn't grow every time. bfwolf. |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11142 |
Can't be done - won't be done |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 555 |
@Martin H. that's impressive. @LeoNicolas thank you for the great explanatory video. I only knew the solution with the individual steps. @matherp Peter, is it possible to get the graphics for your example? I can't find them anywhere. I'm currently testing the version PicoMiteHDMI MMBasic USB RP2350A Edition V6.02.01b2 - i itMatthias Edited 2026-02-15 01:14 by homa |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
I have optimised this a little to keep the calculations within the DDA loop as low as possible. ' Raycaster 2 in MMBasic, DDA method (Digital Differential Analyser) 'V0.6 mmb4w=0 CLS If mmb4w Then MODE -7 :CLS RGB(cyan) PAGE WRITE 1 Else MODE 2: CLS RGB(cyan) FRAMEBUFFER create:FRAMEBUFFER write f End If CLS mapS=64:mapy=24:mapx=24 ' --- Labyrinth Definition --- Dim integer mapW = 57,mapH = 51 Dim integer m(mapW, mapH),Col%(3,2),cll Col%(1,0)=RGB(green):Col%(1,1)=RGB(0,170,0) Col%(2,0)=RGB(0,170,255):Col%(2,1)=RGB(0,85,255) Col%(3,0)=RGB(255,170,0):Col%(3,1)=RGB(255,85,0) Restore MapData1 For y = 0 To mapH-1 :Read k$:k$=k$+"1" For x=0 To Len(k$):m(x, y)=Val(Mid$(k$,x+1,1)):Next :Next ' --- Player Setup --- px = 26.5 : py = 45.5 dx = 1 : dy = 0.5 planeX = 0 : planeY = 0.66 moveSpeed = 0.3 rotSpeed = 0.1 buffer = 0.2 ' --- Grafic Setup --- resStep = 3 scrW = MM.HRES*.75 scrH = MM.VRES*.75 ' Flag to force the first frame needsRedraw = 1 invScrW = 2 / scrW halfScrH = scrH / 2 colWallA = RGB(green) colWallB = RGB(0,128,0) colSky = RGB(0,128,255) colFloor = RGB(0,85,0) Do ' Only paint when something has changed If needsRedraw = 1 Then tt=Timer ' Ceiling and floor Box 0, 0, scrW, halfScrH, 0, colSky, colSky Box 0, halfScrH, scrW, halfScrH, 0, colFloor, colFloor 'DDA For x = 0 To scrW - 1 Step resStep cameraX = x * invScrW - 1 rayDx = dx + planeX * cameraX rayDy = dy + planeY * cameraX mx = Int(px) : my = Int(py) If rayDx = 0 Then dDx = 1e30 Else invRayDx = 1 / rayDx: dDx = Abs(invRayDx) End If If rayDy = 0 Then dDy = 1e30 Else invRayDy = 1 / rayDy :dDy = Abs(invRayDy) End If If rayDx < 0 Then stepX = -1 : sdX = (px - mx) * dDx Else stepX = 1 : sdX = (mx + 1 - px) * dDx End If If rayDy < 0 Then stepY = -1 : sdY = (py - my) * dDy Else stepY = 1 : sdY = (my + 1 - py) * dDy End If 'cast one Ray hit = 0 Do While hit = 0 If sdX < sdY Then Inc sdX,dDx:Inc mx,stepX : side = 0 Else Inc sdY,dDy:Inc my,stepY : side = 1 End If If m(mx, my) Then hit = 1 Loop cll=m(mx, my) If side = 0 Then pDist = (sdX - dDx) Else pDist = (sdY - dDy) 'don't go through the wall If pDist < 0.1 Then pDist = 0.1 lH = Int(scrH / pDist) yS = halfScrH - (lH/2) wallCol=col%(cll,side) Box x, yS, resStep, lH, , wallCol,wallCol Next x If mmb4w Then PAGE WRITE 0:Blit 0,0,0,0,scrW,scrH,1 Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer PAGE WRITE 1 Else FRAMEBUFFER write n Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer FRAMEBUFFER write f Blit framebuffer F,N,0,0,0,0,scrW,scrH End If needsRedraw = 0 ' Reset Redraw Flag End If ' Wait for button (does not block, but checks efficiently) k$ = UCase$(Inkey$) Do :Loop Until Inkey$="" If k$ <> "" Then ' Enable redraw when a movement key is pressed If Instr("WASD", k$) > 0 Then needsRedraw = 1 Select Case k$ Case "W" If m(Int(px + dx * buffer), Int(py)) = 0 Then px = px + dx * moveSpeed If m(Int(px), Int(py + dy * buffer)) = 0 Then py = py + dy * moveSpeed Case "S" If m(Int(px - dx * buffer), Int(py)) = 0 Then px = px - dx * moveSpeed If m(Int(px), Int(py - dy * buffer)) = 0 Then py = py - dy * moveSpeed Case "D" oldDx = dx : dx = dx * Cos(rotSpeed) - dy * Sin(rotSpeed) dy = oldDx * Sin(rotSpeed) + dy * Cos(rotSpeed) oldPx = planeX : planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed) planeY = oldPx * Sin(rotSpeed) + planeY * Cos(rotSpeed) Case "A" oldDx = dx : dx = dx * Cos(-rotSpeed) - dy * Sin(-rotSpeed) dy = oldDx * Sin(-rotSpeed) + dy * Cos(-rotSpeed) oldPx = planeX : planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed) planeY = oldPx * Sin(-rotSpeed) + planeY * Cos(-rotSpeed) End Select End If Loop Until k$ = Chr$(27) If mmb4w Then PAGE WRITE 0 MapData1: ' Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111130000000000333331111111111111111" Data "11111000011111111111111130000000000333331111111111111111" Data "11111000011111110000000000000000000300331111111111111111" Data "11111000011111110000000030000000000300331111111111111111" Data "11111110111111110011111130000000000333331111111111111111" Data "11111000000001110011111130000000000333331111111111111111" Data "11111000000001110011111133333003333331111111111111111111" Data "11111000000001000000111133333003333331111111111111111111" Data "11111000000000000000111111333003333331111111111111111111" Data "11111000000001000000111111333003311111111111111111111111" Data "11111000000001111111111333333003333111111111111111111111" Data "10011111001111111111111333333003333111111111111111111111" Data "10011111001111111111111300000000333111111111111111111111" Data "11011111001111111111111300333003333111111111111111111111" Data "10011111001111111111111300333003333111111111111111111111" Data "10000000001111111111111300333003333111111111111111111111" Data "10010000001111111111111333333003311111111111111111111111" Data "10011111111111111111111111113003111111111111122222222222" Data "10011000000001111111111111110000111111111111122222222222" Data "10011000000001111111111100000000000011222222222000000022" Data "10011000000001111111111000000000000002222222222000000022" Data "10001000000001111111111000000000000000000000002000000022" Data "10000000000001111111111000000000000000000000000000000022" Data "10001000000001111111111000000000000002000000222000000022" Data "10011000000001111111111000000000000001122002222000000022" Data "10011000000001111111111111110000111111122002222000000022" Data "10011111011111111111111111112202211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10010001001111111111111111122002211111122002222222222211" Data "10000001001111111111111111122002211111122002222020022211" Data "10011111001111111111111111122002211111122000000000000211" Data "10011111001111111111111111122002211111122000000000002211" Data "10000000000000010000011111122002211111122022002020220211" Data "10000000000000000000000111122002211111122222222222222211" Data "10000000000000010000011111122002211111111111111111111111" Data "11111111111111110111111122222002222221111111111111111111" Data "11111111110010000111111222222202222221111111111111111111" Data "11111111110000011111111200000000000021111111111111111111" Data "11111111111011111111111200002002000021111111111111111111" Data "11111111111001111111111200002002000021111111111111111111" Data "11111111111001111111111222222002222221111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111222222002222221111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111222222222222221111111111111111111" I have revised the map again. Now you get different values when you hit a wall. Here, I have planned to colour different areas differently (as in Wolf3d), which should help with orientation. The value found is stored in 'cll' but not yet evaluated. Have Fun Cheers Martin Edited 2026-02-16 22:06 by Martin H. 'no comment |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
Addendum: I have adjusted the above listing. Different colours for areas are now taken into account by the programme. ![]() 'no comment |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5823 |
Hi Martin, You may need a small fix. The use of floor and ceiling color is essential for the program to run. If I change them to rgb(black) and rgb(white) I cannot navigate anymore. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
Well, I don't understand that, becausecolSky = RGB(White) colFloor = RGB(Black) only serve to give the two BOX commands a colour. Box 0, 0, scrW, halfScrH, 0, colSky, colSky to draw the Background.Box 0, halfScrH, scrW, halfScrH, 0, colFloor, colFloor They are not taken into account further and have no influence on the calculation of the walls. They are purely visual. What I can imagine is that it helps to clear the keyboard buffer after the query. So that you don't accidentally run into a wall. (It shouldn't happen, but it makes the controls more direct.) k$ = UCase$(Inkey$) Do :Loop Until Inkey$="" ![]() Edited 2026-02-17 00:03 by Martin H. 'no comment |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5823 |
Yeah, works now. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 816 |
This is really nice, many thanks for sharing! I've tested it with an ILI9341 LCD and it runs fast and smooth. Regards |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
@dddns Thanks for the feedback. I don't have an LCD Pico myself at the moment, but it's good to hear that it works there too. Here I have further optimised the routines and now pre-calculate the angles in lookup tables. On an RP2040 @225, image rendering now takes 40-85 ms. I don't see any further optimisation possibilities at this time. If you can think of any further optimisation possibilities, please let me know. ' Raycaster 2 in MMBasic, DDA method (Digital Differential Analyser) 'V0.8.. added Lookuptables mmb4w=0 CLS If mmb4w Then MODE -7 :CLS RGB(cyan) PAGE WRITE 1 Else MODE 2: CLS RGB(cyan) FRAMEBUFFER create:FRAMEBUFFER write f End If CLS 'sin / cos Look Up Table Const LUTSIZE=360,MULT=128 Dim integer sinLUT(LUTSIZE), cosLUT(LUTSIZE) Dim integer angleIndex = 0 ' direction of view Dim integer rotStep = 5 For i = 0 To LUTSIZE - 1 angle = i * (2 * Pi / LUTSIZE) sinLUT(i) = Int(Sin(angle)*MULT) cosLUT(i) = Int(Cos(angle)*MULT) Next mapS=64:mapy=24:mapx=24 ' --- Labyrinth Definition --- Dim integer mapW = 57,mapH = 51,lH,scrW,scrH Dim integer m(mapW, mapH),Col(3,2),lw,hit Col(1,0)=RGB(green):Col(1,1)=RGB(0,170,0) Col(2,0)=RGB(0,170,255):Col(2,1)=RGB(0,85,255) Col(3,0)=RGB(255,170,0):Col(3,1)=RGB(255,85,0) Restore MapData1 For y = 0 To mapH-1 :Read k$:k$=k$+"1" For x=0 To Len(k$):m(x, y)=Val(Mid$(k$,x+1,1)):Next :Next ' --- Player Setup --- px = 26.5 : py = 45.5 dx = 1 : dy = 0.5 planeX = 0 : planeY = 0.66 moveSpeed = 0.3 rotSpeed = 0.1 buffer = 0.2 ' --- Grafic Setup --- resStep = 3 scrW = MM.HRES*.75 scrH = MM.VRES*.75 ' Flag to force the first frame needsRedraw = 1 invScrW = 2 / scrW halfScrH = scrH / 2 colSky = RGB(255,255,255) colFloor = RGB(0,85,0) lw=Int((resStep+1)/2) Do ' Only paint when something has changed dx = cosLUT(angleIndex)/MULT dy = sinLUT(angleIndex)/MULT planeX = -dy * 0.66 planeY = dx * 0.66 If needsRedraw Then tt=Timer ' Ceiling and floor Box 0, 0, scrW, halfScrH, , colSky, colSky Box 0, halfScrH, scrW, halfScrH, , colFloor, colFloor 'DDA For x = 0 To scrW - 1 Step resStep cameraX = x * invScrW - 1 rayDx = dx + planeX * cameraX rayDy = dy + planeY * cameraX mx = Int(px) : my = Int(py) If Not rayDx Then dDx = 1000 Else invRayDx = 1 / rayDx: dDx = Abs(invRayDx) If Not rayDy Then dDy = 1000 Else invRayDy = 1 / rayDy :dDy = Abs(invRayDy) If rayDx < 0 Then stepX=-1:sdX=(px - mx)*dDx Else stepX=1:sdX=(mx+1-px)*dDx End If If rayDy < 0 Then stepY = -1 : sdY = (py - my) * dDy Else stepY = 1 : sdY = (my + 1 - py) * dDy End If 'cast one Ray hit = 0 Do While Not hit If sdX < sdY Then Inc sdX,dDx:Inc mx,stepX : side = 0 Else Inc sdY,dDy:Inc my,stepY : side = 1 End If hit=m(mx, my) Loop If side = 0 Then pDist = sdX - dDx Else pDist = sdY - dDy 'don't go through the wall If pDist < 0.1 Then pDist = 0.1 lH = Int(scrH / pDist) yS = halfScrH - (lH>>1) Box x, yS, resStep, lH, LW, Col(hit,side) Next x If mmb4w Then PAGE WRITE 0:Blit 0,0,0,0,scrW,scrH,1 Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer PAGE WRITE 1 Else FRAMEBUFFER write n Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer FRAMEBUFFER write f Blit framebuffer F,N,0,0,0,0,scrW,scrH End If needsRedraw = 0 ' Reset Redraw Flag End If ' Wait for button (does not block, but checks efficiently) k$ = UCase$(Inkey$) ' Do :Loop Until Inkey$="" If k$ <> "" Then ' Enable redraw when a movement key is pressed If Instr("WASD", k$) Then needsRedraw = 1 Select Case k$ Case "W" If Not (m(Int(px + dx * moveSpeed), Int(py))) Then px = px + dx * moveSpeed If Not (m(Int(px), Int(py + dy * moveSpeed))) Then py = py + dy * moveSpeed Case "S" If Not (m(Int(px - dx * moveSpeed), Int(py))) Then px = px - dx * moveSpeed If Not (m(Int(px), Int(py - dy * moveSpeed))) Then py = py - dy * moveSpeed Case "D" ' turn right angleIndex = (angleIndex + rotStep) Mod LUTSIZE Case "A" ' turn left angleIndex = (angleIndex - rotStep + LUTSIZE) Mod LUTSIZE End Select End If Loop Until k$ = Chr$(27) If mmb4w Then PAGE WRITE 0 MapData1: ' Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111130000000000333331111111111111111" Data "11111000011111111111111130000000000333331111111111111111" Data "11111000011111110000000000000000000300331111111111111111" Data "11111000011111110000000030000000000300331111111111111111" Data "11111110111111110011111130000000000333331111111111111111" Data "11111000000001110011111130000000000333331111111111111111" Data "11111000000001110011111133333003333331111111111111111111" Data "11111000000001000000111133333003333331111111111111111111" Data "11111000000000000000111111333003333331111111111111111111" Data "11111000000001000000111111333003311111111111111111111111" Data "11111000000001111111111333333003333111111111111111111111" Data "10011111001111111111111333333003333111111111111111111111" Data "10011111001111111111111300000000333111111111111111111111" Data "11011111001111111111111300333003333111111111111111111111" Data "10011111001111111111111300333003333111111111111111111111" Data "10000000001111111111111300333003333111111111111111111111" Data "10010000001111111111111333333003311111111111111111111111" Data "10011111111111111111111111113003111111111111122222222222" Data "10011000000001111111111111110000111111111111122222222222" Data "10011000000001111111111100000000000011222222222000000022" Data "10011000000001111111111000000000000002222222222000000022" Data "10001000000001111111111000000000000000000000002000000022" Data "10000000000001111111111000000000000000000000000000000022" Data "10001000000001111111111000000000000002000000222000000022" Data "10011000000001111111111000000000000001122002222000000022" Data "10011000000001111111111111110000111111122002222000000022" Data "10011111011111111111111111112202211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10010001001111111111111111122002211111122002222222222211" Data "10000001001111111111111111122002211111122002222020022211" Data "10011111001111111111111111122002211111122000000000000211" Data "10011111001111111111111111122002211111122000000000002211" Data "10000000000000010000011111122002211111122022002020220211" Data "10000000000000000000000111122002211111122222222222222211" Data "10000000000000010000011111122002211111111111111111111111" Data "11111111111111110111111122222002222221111111111111111111" Data "11111111110010000111111222222202222221111111111111111111" Data "11111111110000011111111200000000000021111111111111111111" Data "11111111111011111111111200002002000021111111111111111111" Data "11111111111001111111111200002002000021111111111111111111" Data "11111111111001111111111222222002222221111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111222222002222221111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111222222222222221111111111111111111" The next thing I will try is to replace the box commands with Blit to save a few ms. Additionally, it would be possible to draw the depth darker using dithering. Cheers Martin Edited 2026-02-19 20:42 by Martin H. 'no comment |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5823 |
Hi Martin, I am surprized that using a lookup table for sine and cosine is actually faster than calculating the sine and the cosine. This should be so fast, especially in 2350. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
Volhout Well, I've tweaked a lot of things to remove "expensive" calculations from the cast-loop. The lookup tables are just one of them, although they are only calculated once per frame. Cheers Martin Edited 2026-02-19 22:14 by Martin H. 'no comment |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
Because it's Sunday. Version 0.86, some calculations have been sped up again, and it is now possible with the dither%-switch to darken the walls depending on 'the distance (selectable dithering). Everyone is free to use and improve the routines. ' Raycaster 2 in MMBasic, DDA method (Digital Differential Analyser) 'V0.86.. added Lookuptables,changed to Blit vs Box,compressed math 'added possible dithering 'By Martin H. for https://www.thebackshed.com 'The software is subject to the terms of the GNU General Public Licence (GPL) 'and may be freely used, copied and updated. 'Individuals are encouraged to freely use and modify the software. dim integer dither%=1,mmb4w=0 CLS If mmb4w Then MODE -7 :CLS RGB(cyan) PAGE WRITE 1 Else MODE 2: CLS RGB(cyan) FRAMEBUFFER create:FRAMEBUFFER write f End If CLS 'sin / cos LookUpTable Const LUTSIZE=360,MULT=128 Dim integer sinLUT(LUTSIZE), cosLUT(LUTSIZE) Dim integer angleIndex = 0 ' direction of view Dim integer rotStep = 5,lh,ys For i = 0 To LUTSIZE - 1 angle = i * (2 * Pi / LUTSIZE) sinLUT(i) = Int(Sin(angle)*MULT) cosLUT(i) = Int(Cos(angle)*MULT) Next ' --- Color scheme Definition --- Dim integer Col(3,2) Col(1,0)=RGB(green):Col(1,1)=RGB(0,170,0) Col(2,0)=RGB(0,170,255):Col(2,1)=RGB(0,85,255) Col(3,0)=RGB(255,170,0):Col(3,1)=RGB(255,85,0) colSky = RGB(0,0,0) colFloor = RGB(0,85,0) ' --- Labyrinth Definition --- mapS=64:mapy=24:mapx=24 Dim integer mapW = 57,mapH = 51,scrW,scrH Dim integer m(mapW, mapH),lw,mx,my',hit Restore MapData1 For y = 0 To mapH-1 :Read k$:k$=k$+"1" For x=0 To Len(k$):m(x, y)=Val(Mid$(k$,x+1,1)):Next :Next ' --- Player Setup --- px = 26.5 : py = 45.5 dx = 1 : dy = 0.5 planeX = 0 : planeY = 0.66 moveSpeed = 0.3 rotSpeed = 0.1 buffer = 0.2 ' --- Grafic Setup --- resStep = 2 scrW = MM.HRES-96 scrH = MM.VRES*.75 precalc_shading needsRedraw = 1 ' Flag to force the first frame invScrW = 2 / scrW halfScrH = scrH / 2 lw=Int((resStep+1)/2) Do ' Only paint when something has changed If needsRedraw Then dx = cosLUT(angleIndex)/MULT dy = sinLUT(angleIndex)/MULT planeX = -dy * 0.66 planeY = dx * 0.66 tt=Timer ' Ceiling and floor Box 0, 0, scrW, halfScrH, , colSky, colSky Box 0, halfScrH, scrW, halfScrH, , colFloor, colFloor 'DDAA cast field of view For x = 0 To scrW - 1 Step resStep cameraX = x * invScrW - 1 rayDx = dx + planeX * cameraX rayDy = dy + planeY * cameraX mx = Int(px) : my = Int(py) If Not rayDx Then dDx = 1000 Else dDx = Abs(1 / rayDx) If Not rayDy Then dDy = 1000 Else dDy = Abs( 1 / rayDy) If rayDx < 0 Then stepX=-1:sdX=(px - mx)*dDx Else stepX=1:sdX=(mx+1-px)*dDx End If If rayDy < 0 Then stepY = -1: sdY = (py - my) * dDy Else stepY = 1 : sdY = (my + 1 - py) * dDy End If 'cast one Ray Do While Not m(mx,my) If sdX < sdY Then Inc sdX,dDx:Inc mx,stepX : side = 0 Else Inc sdY,dDy:Inc my,stepY : side = 1 End If Loop pDist=Max(0.1,(sdX - dDx)*Not(side)+(sdY-dDy)*side) lH = scrH / pDist shade = min (int(pDist>>3)*24,72)*dither% Blit shade+scrw-8+8*m(mx,my)+4*side,0,x,Max(0,halfScrH-(lH>>1)),resStep,Min(lH,scrH) Next x If mmb4w Then PAGE WRITE 0:Blit 0,0,0,0,scrW,scrH,1 Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer PAGE WRITE 1 Else FRAMEBUFFER write n Text scrW,0,"X="+Str$(Int(px))+" " Text scrW,12,"Y="+Str$(Int(py))+" " Text scrW,24,Str$(Timer-tt)+" ":tt=Timer FRAMEBUFFER write f Blit framebuffer F,N,0,0,0,0,scrW,scrH End If needsRedraw = 0 ' Reset Redraw Flag End If ' Wait for button (does not block, but checks efficiently) k$ = UCase$(Inkey$) If k$ <> "" Then Do :Loop Until Inkey$="" ' Enable redraw when a movement key is pressed 'If Instr("WASD", k$) Then needsRedraw = 1 Select Case k$ Case "W" dmx=dx * moveSpeed:dmy=dy * moveSpeed 'don't go through the wall If Not (m(Int(px + dmx), Int(py))) Then Inc px,dmx If Not (m(Int(px), Int(py + dmy))) Then Inc py,dmy Case "S" dmx=dx * moveSpeed:dmy=dy * moveSpeed 'don't go through the wall If Not (m(Int(px - dmx), Int(py))) Then Inc px ,- dmx If Not (m(Int(px), Int(py - dmy))) Then Inc py ,- dmy Case "D" ' Rechts drehen angleIndex = (angleIndex + rotStep) Mod LUTSIZE Case "A" ' Links drehen angleIndex = (angleIndex - rotStep + LUTSIZE) Mod LUTSIZE End Select End If Loop Until k$ = Chr$(27) If mmb4w Then PAGE WRITE 0 sub precalc_shading local integer d75%(3,3) = (1,0,1,0, 1,1,1,1, 0,1,0,1, 1,1,1,1) local integer d50%(3,3) = (1,0,1,0, 0,1,0,1, 1,0,1,0, 0,1,0,1) local integer d25%(3,3) = (1,0,1,0, 0,0,0,0, 0,1,0,1, 0,0,0,0) For s = 1 To 3 ' 1=75%, 2=50%, 3=25% shadeBase = scrw+24+ (s-1)*24 ' 24 Pixel pro Block For n = 1 To 3 For f = 0 To 1 idx = (n-1)*2 + f ' 0..5 baseX = shadeBase + idx*4 For y = 0 To MM.VRES-1 For x = 0 To 3 Select Case s Case 1: p = d75%(y And 3, x) Case 2: p = d50%(y And 3, x) Case 3: p = d25%(y And 3, x) End Select If p Then Pixel baseX + x, y, col(n,f) Else Pixel baseX + x, y, 0 End If Next x Next y Next f Next n Next s For n=1 To 3:For f=0 To 1 box scrw+8*(n-1)+4*f,0,4,MM.VRES,,col(n,f),col(n,f):Next :Next end Sub MapData1: ' Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111133333333333331111111111111111111" Data "11111111111111111111111130000000000333331111111111111111" Data "11111000011111111111111130000000000333331111111111111111" Data "11111000011111110000000000000000000300331111111111111111" Data "11111000011111110000000030000000000300331111111111111111" Data "11111110111111110011111130000000000333331111111111111111" Data "11111000000001110011111130000000000333331111111111111111" Data "11111000000001110011111133333003333331111111111111111111" Data "11111000000001000000111133333003333331111111111111111111" Data "11111000000000000000111111333003333331111111111111111111" Data "11111000000001000000111111333003311111111111111111111111" Data "11111000000001111111111333333003333111111111111111111111" Data "10011111001111111111111333333003333111111111111111111111" Data "10011111001111111111111300000000333111111111111111111111" Data "11011111001111111111111300333003333111111111111111111111" Data "10011111001111111111111300333003333111111111111111111111" Data "10000000001111111111111300333003333111111111111111111111" Data "10010000001111111111111333333003311111111111111111111111" Data "10011111111111111111111111113003111111111111122222222222" Data "10011000000001111111111111110000111111111111122222222222" Data "10011000000001111111111100000000000011222222222000000022" Data "10011000000001111111111000000000000002222222222000000022" Data "10001000000001111111111000000000000000000000002000000022" Data "10000000000001111111111000000000000000000000000000000022" Data "10001000000001111111111000000000000002000000222000000022" Data "10011000000001111111111000000000000001122002222000000022" Data "10011000000001111111111111110000111111122002222000000022" Data "10011111011111111111111111112202211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10011111001111111111111111122002211111122002222222222222" Data "10010001001111111111111111122002211111122002222222222211" Data "10000001001111111111111111122002211111122002222020022211" Data "10011111001111111111111111122002211111122000000000000211" Data "10011111001111111111111111122002211111122000000000002211" Data "10000000000000010000011111122002211111122022002020220211" Data "10000000000000000000000111122002211111122222222222222211" Data "10000000000000010000011111122002211111111111111111111111" Data "11111111111111110111111122222002222221111111111111111111" Data "11111111110010000111111222222202222221111111111111111111" Data "11111111110000011111111200000000000021111111111111111111" Data "11111111111011111111111200002002000021111111111111111111" Data "11111111111001111111111200002002000021111111111111111111" Data "11111111111001111111111222222002222221111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200002002000021111111111111111111" Data "11111111111111111111111222222002222221111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111200000000000021111111111111111111" Data "11111111111111111111111222222222222221111111111111111111" ![]() cheers Martin Edited 2026-02-23 02:43 by Martin H. 'no comment |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 816 |
Even though I don't understand a word, that's terrific. Super fast and (almost) less code than with the ray command itself. |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1441 |
That's not your fault; I wouldn't be able to read the formulas anymore either. I removed the calculations from the loop as best I could. I replaced many “if queries” with calculations using true and false. Testing line by line to find out which type of calculation is currently faster. This speeds up execution considerably, but does not contribute to readability. To understand the calculation and how it works, it's best to watch this Video . Then compare the function from the original routine For x = 0 To scrW - 1 Step resStep cameraX = 2 * x / scrW - 1 rayDx = dx + planeX * cameraX rayDy = dy + planeY * cameraX mx = Int(px) : my = Int(py) If rayDx = 0 Then dDx = 1e30 Else dDx = Abs(1 / rayDx) If rayDy = 0 Then dDy = 1e30 Else dDy = Abs(1 / rayDy) If rayDx < 0 Then stepX = -1 : sdX = (px - mx) * dDx Else stepX = 1 : sdX = (mx + 1.0 - px) * dDx End If If rayDy < 0 Then stepY = -1 : sdY = (py - my) * dDy Else stepY = 1 : sdY = (my + 1.0 - py) * dDy End If hit = 0 Do While hit = 0 If sdX < sdY Then sdX = sdX + dDx : mx = mx + stepX : side = 0 Else sdY = sdY + dDy : my = my + stepY : side = 1 EndIf If m(mx, my) > 0 Then hit = 1 Loop If side = 0 Then pDist = (sdX - dDx) Else pDist = (sdY - dDy) If pDist < 0.1 Then pDist = 0.1 lH = Int(scrH / pDist) yS = (scrH / 2) - (lH / 2) wallCol = RGB(GREEN) If side = 1 Then wallCol = RGB(0, 128, 0) Box x, yS, resStep, lH, , wallCol Next x with the current one; that will help you understand. For x = 0 To scrW - 1 Step resStep cameraX = x * invScrW - 1 rayDx = dx + planeX * cameraX rayDy = dy + planeY * cameraX mx = Int(px) : my = Int(py) If Not rayDx Then dDx = 1000 Else dDx = Abs(1 / rayDx) If Not rayDy Then dDy = 1000 Else dDy = Abs( 1 / rayDy) If rayDx < 0 Then stepX=-1:sdX=(px - mx)*dDx Else stepX=1:sdX=(mx+1-px)*dDx End If If rayDy < 0 Then stepY = -1: sdY = (py - my) * dDy Else stepY = 1 : sdY = (my + 1 - py) * dDy End If 'cast one Ray Do While Not m(mx,my) If sdX < sdY Then Inc sdX,dDx:Inc mx,stepX : side = 0 Else Inc sdY,dDy:Inc my,stepY : side = 1 End If Loop pDist=Max(0.1,(sdX - dDx)*Not(side)+(sdY-dDy)*side) lH = scrH / pDist shade = min (int(pDist>>3)*24,72)*dither% Blit shade+scrw-8+8*m(mx,my)+4*side,0,x,Max(0,halfScrH-(lH>>1)),resStep,Min(lH,scrH) Next x If I find further ways to optimize it, it will certainly become even more unreadable. Have Fun Martin 'no comment |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 816 |
Amazing, optimize it as much as you can :) ..is there a way to place a (3D) icon on the floor? Best success! |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |