|
Forum Index : Microcontroller and PC projects : PicoMiteVGA: Framework for ray casting using the DDA method
| Author | Message | ||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1346 |
DDA method (Digital Differential Analyser).Much faster than my previous attempts. ' Raycaster 2 in MMBasic, DDA method (Digital Differential Analyser) 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 mapW = 57 Dim mapH = 51 Dim m(mapW, mapH) 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 'For y = 0 To mapH-1 : For x = 0 To mapW-1 : Read m(x, y) : Next x : Next y ' --- Player Setup --- px = 26.5 : py = 45.5 dx = 1.0 : dy = 0.5 planeX = 0.0 : planeY = 0.66 moveSpeed = 0.2 rotSpeed = 0.1 buffer = 0.2 ' --- Grafic Setup --- resStep = 2 scrW = MM.HRES*.75 scrH = MM.VRES*.75 ' Flag to force the first frame needsRedraw = 1 Do ' Only paint when something has changed If needsRedraw = 1 Then ' Ceiling and floor Box 0, 0, scrW, scrH/2, 0, RGB(0,128,255), RGB(0,128,255) Box 0, scrH/2, scrW, scrH/2, 0, RGB(255,128,0), RGB(255,128,0) 'DDA 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 If mmb4w Then PAGE WRITE 0:Blit 0,0,0,0,scrW,scrH,1:PAGE WRITE 1 Else FRAMEBUFFER write n Text 290,0,"X="+Str$(Int(px))+" " Text 290,12,"Y="+Str$(Int(py))+" " 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 ' 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 "11111111111111111111111111111111111111111111111111111111" Data "11111111111111111111111110000000000111111111111111111111" Data "11111000011111111010111110000000000111111111111111111111" Data "11111000011111100000000000000000000100111111111111111111" Data "11111000011111100000000010000000000100111111111111111111" Data "11111011111111110011111110000000000111111111111111111111" Data "11111000000000110011111110000000000111111111111111111111" Data "11111000000001110010111111111001111111111111111111111111" Data "11111000000000000000111111111001111111111111111111111111" Data "11111000000000000000111111111001111111111111111111111111" Data "10000000000001000000111111111001111111111111111111111111" Data "11111000000001111111111111111001111111111111111111111111" Data "10000011111101111111111111111001111111111111111111111111" Data "10011111001111111111111100000000011111111111111111111111" Data "10111111001111111111111100111001111111111111111111111111" Data "10011111001111111111111100111001111111111111111111111111" Data "10000001001111111111111100111001111111111111111111111111" Data "10000011001111111111111111111001111111111111111111111111" Data "10010101111011111111111111111001010100111111101111110111" Data "10011000000001111111111101110000111011111111101000000011" Data "10010000000001111111111100000000000010010001001000000000" Data "10011000000001111111111000000000000001111111101000000011" Data "10001000000000111111111000000000000000000000001000000000" Data "10001000000001111111111000000000000000000000000000000001" Data "10001000000001111111111000000000000001000000001000000000" Data "10011000000001111111111000000000000000111001111000000001" Data "10011000000001111111111111010000101111110000101000000011" Data "10011111011011111111111111111101110100110001001011110101" Data "10001111001111111111111111111000111111111000100110010001" Data "10011111001111111111111111110000111111110001001000100100" Data "10010001001111111111111111111000111111111000111111111111" Data "10000001001111111111111111110001111111110001001000010111" Data "10011111001111101111111111111001111111111000000000000111" Data "10001010001111011110101111110001011111110000000000001111" Data "10000000000000000000011111111000111111111011001010110111" Data "10000000000000000000000111110000111111111111111111111111" Data "10000000000000010000011111110001111111111111111111111111" Data "11101110111101111111111111111000111111111111111111111111" Data "11111111110010000111111111101101111011111111111111111111" Data "11111111110000011111111100000000000011111111111111111111" Data "11111111111011111111111100001001000001111111111111111111" Data "11111111111001111111111000000000000011111111111111111111" Data "11111111111001111111111111111001111111111111111111111111" Data "11111111111111111111111100001001000011111111111111111111" Data "11111111111111111111111100000000000001111111111111111111" Data "11111111111111111111111100001001000011111111111111111111" Data "11111111111111111111111111111001111111111111111111111111" Data "11111111111111111111111100000000000011111111111111111111" Data "11111111111111111111111100000000000011111111111111111111" Data "11111111111111111111111100101010001011111111111111111111" Data "11111111111111111111111111111111111111111111111111111111" This is just a basic framework. Feel free to change and expand it. Cheers Martin 'no comment |
||||
| LeoNicolas Guru Joined: 07/10/2020 Location: CanadaPosts: 560 |
Nice job Martin For anyone that wants to know more about DDA and ray casting https://www.youtube.com/watch?v=NbSee-XM7WA |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1346 |
I forgot to mention, the keys are ‘AD WS’ and ESC to quit. 'no comment |
||||
| Peter63 Senior Member Joined: 28/07/2017 Location: SwedenPosts: 117 |
Hello Martin I tried it on PicoMiteHDMIUSB V6.02.00RC6 and it works perfectly. /Peter63 |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1346 |
Hi Peter, Thanks for the feedback. I also tried it on Pico2 with HDMI (Try MODE 3). It should also run on CMM2 and MMBasic for Windows if you set mmb4w=1 at the start of the Programm. Cheers Martin Edited 2026-02-09 16:49 by Martin H. 'no comment |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5665 |
Hi Martin, Works nice (RP2040 VGA), but requires quite a lot of math. So it is somewhat slow. Maybe a lot better on 2350. Now I look at what you are planning to do, could you not make use of the 3D DRAW commands in MMBasic. I am not sure the 3D DRAW routines Peter implemented can handle the whole map, but they certainly could handle the visible part of the map (they also can rotate a multiface football). If you place your camera inside the football (inside your maze) this could work ? Regards, Volhout EDIT: Martin, I tried to speed things up by changing "resStep" from 2 to 5, but that corrupts the drawing. Change line 78 into adding "wallCol" as fill color. Edited 2026-02-09 18:42 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1346 |
Volhout, You can speed it up by increasing resStep = 2 to resStep = 4 and changing the box command before NEXT X to ‘Box x, yS, resStep, lH, , wallCol, wallCol’. This halves the number of calculations required, but also the x resolution. It could be, of course, possible to use the 3D DRAW routines, but this is a completely different approach to ray casting. To do this, the field of view would have to be vectorised and converted into 3D models. This is certainly possible, but I'm not sure if it would be faster in the end. The initial aim here was to limit the raycast calculations to simple/fast calculations(certainly still room for improvement). I have no experience with the 3D DRAW routines, but I am open to being convinced. Cheers Martin Edited 2026-02-09 19:02 by Martin H. 'no comment |
||||
| thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4337 |
A screenshot for those who are interested, but not enough to crank up the 'Mite: ![]() Generated by MMB4L running under WSL2 on Windows whilst simulating a PicoMite .Best wishes, Tom Edited 2026-02-09 20:49 by thwill MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1346 |
Yes, that sounds reasonable. ![]() 'no comment |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10931 |
How about this ? - -worth including in the RP2350 firmware? Manual: Raycaster_User_Manual.pdf Example code: |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |