|
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: 1422 |
In principle, it would work like this: the image is generated on framebuffer F. If you want to place an object, you must first draw the background, then place the object, and then copy the image from framebuffer F to N. ...snip... Next x '--------------------------------- ' MOVE YOUR DRAW-OBJECT-CODE HERE ' AND ADD "needsRedraw = 1" '--------------------------------- If mmb4w Then PAGE WRITE 0:Blit 0,0,0,0,scrW,scrH,1 ...snip... --- Currently, redrawing only occurs when the player moves. If the object is also supposed to move within the space, it is necessary to replace the conditional redraw with a regular one, or at least take care the object change. Edited 2026-02-24 17:46 by Martin H. 'no comment |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
another millisecond/Frame saved @252000 Replace 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) with mx = Int(px) : my = Int(py) The additional +.01 serves only to prevent division by zero.dDx = 1000*Not rayDx + Abs(1 / (rayDx+.01))*(rayDx<>0) dDy = 1000*Not rayDy + Abs(1 / (rayDy+.01))*(rayDy<>0) Unfortunately, converting IF queries into mathematical functions only works in simple forms: i.e. IF a=0 then doit=1 else doit=5 becomes something like doit=not a +5*(a<>0) Replacing Queries with multiple THEN conditions do sadly not save time, rather the opposite. One important thing is to remove ‘expensive’ calculations and queries out of the loop for image calculation. As these are used several hundred times per frame. In contrast, the keyboard/gamepad query is only called once per frame. Of course, this can also be optimised, but it will do virtually nothing to improve the speed. Edited 2026-02-26 01:03 by Martin H. 'no comment |
||||
| Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1681 |
I asked Claude AI what it could do to speed up this program. Here is Claude's version note I have set mmb4w=1 ( This could be set using: MMB4W = 0 if MM.DEVICE$ = "MMBasic for Windows" then MMB4W = 1 Raycaster 2.zip Bill Keep safe. Live long and prosper. |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
Hello Bill, Thank you for your efforts. I will try to take your suggestions into account and test them.I don't quite understand the changes in your published file. None of the suggested changes have been incorporated into the file you published.To me, that looks like the code I posted on 23 February 2026. Did you perhaps zip the wrong file? As mentioned above, If Not rayDx Then dDx = 1000 Else dDx = Abs(1 / rayDx) If Not rayDy Then dDy = 1000 Else dDy = Abs( 1 / rayDy) slower than dDx = 1000*Not rayDx + Abs(1 / (rayDx+.01))*(rayDx<>0) dDy = 1000*Not rayDy + Abs(1 / (rayDy+.01))*(rayDy<>0) Replacing IF THEN ELSE with a logical formula whenever possible works faster in most cases. I will look at the rest of the suggestions and try to implement them. In my opinion, filling the floor/ceiling only when rotating is only correct if you are moving forwards; if you are moving backwards, the background must also be completely redrawn. Here, checking the possibilities would take more time than the 2 BOX commands at the beginning of the frame. Cheers Martin Edited 2026-02-26 17:30 by Martin H. 'no comment |
||||
| Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1681 |
Sorry Martin, I think this is the right one. I'm trying to do too many things at the one time. raycaster_v087.zip Bill Keep safe. Live long and prosper. |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
Haha, I know exactly what you're talking about Thank you, but the result is, let's say, semi-good ![]() This has more of a Minecraft feel to it than a Raicast. I'll will take a look at the code. I just had a discussion with MS-COPILOT about improving my DDA routine, he had a lot of suggestions, none of which really speeded things up, quite the opposite. (later, he then saw that too) ![]() That's why the AI suggestions aren't always real improvements; for example, Copilot keeps forgetting that it's about interpreted basic and that TRUE=1 in MMBASIC Cheers Martin Edited 2026-02-26 18:59 by Martin H. 'no comment |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
' Raycaster 2 in MMBasic, DDA method (Digital Differential Analyser) accelerated once again by ~5 ms.'V0.87.. added Lookuptables,changed to Blit vs Box,compressed math 'added possible dithering,compressed math more, changed map to 1D Array '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. 'Start Dim integer dither%=1,mmb4w 'add CMM2 here If MM.DEVICE$ = "MMBasic for Windows" Then MMB4W = 1 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 'Pre-calculate complex things where possible and create lookup tables. Dim integer angleIndex = 0 ' direction of view Dim integer rotStep = 6,lh,ys Dim integer stepX,stepY,x 'sin / cos LookUpTable Const LUTSIZE=360,MULT=128 Dim integer sinLUT(LUTSIZE), cosLUT(LUTSIZE) 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) Dim integer colSky = RGB(0,0,0),colFloor = RGB(0,85,0) ' --- Labyrinth Definition --- mapS=64:mapy=24:mapx=24 Dim integer mapW = 64,mapH = 51 Dim integer m(mapW*mapH),mx,my Restore MapData1 Dim k$ length 64 For y = 0 To mapH-1 :Read k$:Inc k$,"1" For x=0 To Len(k$):m(x + (y << 6))=Val(Mid$(k$,x+1,1)):Next Next ' --- Player Setup --- Dim px = 26.5,py = 45.5,dx = 1, dy = 0.5 Dim planeX = 0,planeY = 0.66 moveSpeed = 0.3 rotSpeed = 0.1 ' --- Grafic Setup --- Dim integer resStep = 2 'Resolution Dim integer scrW = MM.HRES-96 Dim integer scrH = MM.VRES*.75 precalc_shading 'precalculate the Dithering Dim integer needsRedraw = 1 ' Flag to force the first frame Dim invScrW = 2 / scrW Dim integer halfScrH = scrH / 2 Dim integer lw=Int((resStep+1)/2) 'main 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) dDx = Abs(1 / (rayDx+.001)) dDy = Abs(1 / (rayDy+.001)) 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 indx% = (my<<6) + mx yStepOffset% = stepY << 6 'cast one Ray Do While Not m(indx%) If sdX < sdY Then Inc sdX, dDx: Inc indx%,stepX :side = 0 Else Inc sdY, dDy: Inc indx%, yStepOffset% : 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(indx%)+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) '---> Insert game controller functions here 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 ' (Falls du nur bei Tastendruck zeichnen willst) dmx = dx * moveSpeed : dmy = dy * moveSpeed Select Case k$ Case "W" ' Check X-Kollision: (Int(py) << 6) + Int(px + dmx) If m((Int(py) << 6) + Int(px + dmx)) = 0 Then px = px + dmx ' Check Y-Kollision: (Int(py + dmy) << 6) + Int(px) If m((Int(py + dmy) << 6) + Int(px)) = 0 Then py = py + dmy Case "S" ' Check X-Kollision rckwrts If m((Int(py) << 6) + Int(px - dmx)) = 0 Then px = px - dmx ' Check Y-Kollision rckwrts If m((Int(py - dmy) << 6) + Int(px)) = 0 Then py = 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 Else FRAMEBUFFER close 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) Local integer s,n,y,x,p 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 Pixel baseX + x, y, col(n,f)*p 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" 'no comment |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 794 |
Hello Martin, with the ILI9341 standard driver I get timer values between 40 and 60. With the buffered driver it seems to be even faster, but I can't see the numbers. In both cases the "Mode 2" needs to be removed, but with the buffered driver Blit framebuffer F,N,0,0,0,0,scrW,scrH doesn't work.I replaced it with framebuffer copy f,n what does not give a complete correct screen, but the maze is fine and super fast. Maybe one for @matherp |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
Thanks for the feedback. I'm now nearing the end of my development and optimisation process. As far as I remember, we managed to get F to N Blitting working on LCD with Petscii robots.. What I'd still like to insert, before the version jump to 1.0 is the rhythmic swaying (‘view bobbing’) when running, as seen in Doom. But not today ;-) Cheers Martin Edited 2026-02-27 06:29 by Martin H. 'no comment |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 548 |
Hello Martin, That's really impressive! I just looked at it on the 2350 HDMIUSB and think the speed is really good. Especially because it's completely in Basic. Now just add a small 2D pixel map and it's perfect. Do you think it would be possible to add graphics to the walls? Or would that take too much time? Best regards, Matthias Edited 2026-02-27 06:40 by homa |
||||
| Martin H. Guru Joined: 04/06/2022 Location: GermanyPosts: 1422 |
Matthias, Yes, that would be nice, but I don't see any possibility for texture mapping at the moment, because apart from the size conversion, the walls would then really have to be drawn pixel by pixel or one has to pre-calculate every texture in every size and store it somewhere.. Apart from the fact that I don't have a idea for this yet, I'm not sure if we could manage 1 FPS with it. Cheers Martin Edited 2026-02-27 07:14 by Martin H. 'no comment |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 548 |
Martin, I have sent you a private message (in German). Matthias |
||||
| Bleep Guru Joined: 09/01/2022 Location: United KingdomPosts: 769 |
Hi Martin, Another data point for you, I have tried out your latest DDA program from above on 480x320 LCD ST7796S, 2350 running at 420MHz. All I had to do was remove the Mode 2, everything then just worked, full screen. At this resolution I'm getting around 60 to 76mS per frame, If I limit the screen size to 320x240 (using Poke Display Hres 320 & Poke Display Vres 240) I'm seeing an update rate of about 30 to 45mS per frame. Not Bad! :-) If I limit the CPU to 252MHz I get around 90 to 130mS and 50 to 75mS respectivly. Regards Kevin. PS. I've never tried "converting IF queries into mathematical functions" before, but I've now done it in a couple of pieces of code and mostly it made them faster :-) Thanks for the tip. |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 794 |
With the legacy ILI9341 driver it works for me as well, but not with the buffered.This is on USB RP2350B Edition V6.02.01b6 Which one are you using? @Martin The dithering looks and works really superb. Edited 2026-02-28 00:04 by dddns |
||||
| Bleep Guru Joined: 09/01/2022 Location: United KingdomPosts: 769 |
Not sure what you mean by "buffered" as in framebuffer? in which case framebuffers are working fine on my LCD screen. As I said, the only change I made was to remove the 'Mode 2'. Regards, Kevin. |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 794 |
Do you use the driver ST7796S or ST7796SBUFF ? Edit: if you are running it on an RP2040 then this question is of cause obsolete..haven't thought about this possibility Regards Edited 2026-02-28 04:14 by dddns |
||||
| Bleep Guru Joined: 09/01/2022 Location: United KingdomPosts: 769 |
Hi, Ok, no never use them, I prefer to know when the screen is actually updating. However I have just tested it and all I get is a cyan screen, with a single horizontal black line, so no, doesn't seem to work as is. But, if you remove all the Framebuffer create and write n and f commands, and the Blit framebuffer line, you do get something, there is some strange corruption along the tops of the walls, almost looks like the text writing along the walls!! very strange effect, but at least you can see it working. Regards Kevin. Edited 2026-02-28 04:12 by Bleep |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 794 |
This was just in time :) Many thanks for that answer, I'll maybe ask about in the beta thread Regards Dietmar Edit:How it works for me to get a picture of the maze Edited 2026-02-28 04:20 by dddns |
||||
| Bleep Guru Joined: 09/01/2022 Location: United KingdomPosts: 769 |
Hi Dietmar, If you go back to the original code, just removing the Mode 2, then modify the main Blit Blit framebuffer f,n,0,0,0,0,scrW,scrH to Blit framebuffer f,n,0,200,0,0,scrW,scrH You can see some detail in the single line, so it looks like the blit is only copying a single line, not the whole height of the frame, ie scrH. Regards, Kevin. Edited 2026-02-28 04:33 by Bleep |
||||
| dddns Guru Joined: 20/09/2024 Location: GermanyPosts: 794 |
If I replace with framebuffer copy f,n the maze is correct |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |