Page 1 of 3

Proper raycast examples (now with textures :)

Posted: Mon Dec 29, 2008 9:56 pm
by Godai
Yep. Finally got it going :)
To see how the final example will look, check out the flash version at http://www.furi.dk/raycaster

Note: Code is for 4.30 version of PureBasic (latest version atm)

Code: Select all

; Raycasting example 1 (Solid colored walls only)
EnableExplicit

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l = 640
Global screenHeight.l = 480

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Declare render funcs
Declare RenderSolidWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read.b worldMap(x, y)
  Next x
Next y 

; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #1", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) 
OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)

; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing  
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
  
  ; Pump window events
  Global Event.l = WindowEvent()
  
  ; Start rendering
  StartDrawing(ScreenOutput())
    
  ; Render solid walls
  RenderSolidWalls()
  
  ; End rendering
  StopDrawing()
  
  ; Flip buffers
  FlipBuffers()
  
  ; Check keyboard + timed movement
  ExamineKeyboard()
  
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
  
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 5.0
  Global rotSpeed.f = frameTime * 3.0
  
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)
    
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
    
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
  
  EndIf
  
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
  
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
    
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
  
  EndIf
  
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
  
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
    
  EndIf
  
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
  
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
  
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)

; Render solid walls
Procedure RenderSolidWalls()

  ; Misc vars
  Protected x.l = 0
  
  ; Draw some boxes
  Box(0,0,screenWidth, screenHeight/2, RGB(10,10,10)) 
  Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50)) 
  
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth
  
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
      
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
      
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
            
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
      
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
        
      Else
      
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
        
      EndIf
      
      If (rayDirY < 0)
      
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
        
      Else
      
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
        
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
        
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
        
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
        
        Else
        
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
          
        EndIf
        
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
        
          hit = 1
        
        EndIf         
        
      Wend
      
      ; Calculate distance projected onto camera direction (includes fisheye removal)
      If side = 0
      
        perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
      
      Else
      
        perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
      
      EndIf

      ; Calculate height of line to draw onto screen
      Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
            
      ; Calculate lowest and highest pixel for current line strip
      Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
      If drawStart < 0
        
        drawStart = 0
        
      EndIf
      
      Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
      If drawEnd >= screenHeight
      
        drawEnd = screenHeight - 1
        
      EndIf
      
      ; Color to based on current wall type
      Protected color.l
      
      Select worldMap(mapX, mapY)
      
        Case 1           
          color = RGB(255,0,0) ; Red
        
        Case 2         
          color = RGB(0,255,0) ; Green
        
        Case 3   
          color = RGB(0,0,255) ; Blue
          
        Case 4  
          color = RGB(255,255,255) ; White
        
        Default 
          color = RGB(255,255,0) ; Yellow
      
      EndSelect
      
      ; Shade color based on wall side hit
      If side = 1
      
        color = (color >> 1) & 8355711;
      
      EndIf

      ; Draw line
      LineXY(x, drawStart, x, drawEnd, color) 

  Next

EndProcedure

; Map data
DataSection

  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

EndDataSection  

Yep. Example 2 is here with textured walls.
A couple of notes: No floor function? WTF? Also gave up on direct pixel access. Plot is pretty optimized tho, so it runs quite well in non-debug :)
Later when I've done floor, cieling + objects I'll have a go at optimizing it.
Also shows simple texture generation pattern. (bitwise or pattern)

Code: Select all

; Raycasting example 2 (Textured walls)
EnableExplicit

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l = 640
Global screenHeight.l = 480
Global texWidth.l = 64
Global texHeight.l = 64

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Texture
Global Dim tex.l(texWidth,texWidth)

; Declare render funcs
Declare RenderTexturedWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read.b worldMap(x, y)
  Next x
Next y 

; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #2", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) 
OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)

; Create texture image using bitwise or (|) texture generation
; Just a dim for  now, since I gave up on the direct pixel access for now :)
Global tx.l = 0
Global ty.l = 0

For ty = 0 To texWidth-1
  For tx = 0 To texWidth-1
  
    Global temp.b = (tx | ty) * 3 ; Times 3 for brighter texture :)
    tex(tx,ty) = RGB(temp,temp,temp)
  
  Next
Next

; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing  
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
  
  ; Pump window events
  Global Event.l = WindowEvent()
  
  ; Start rendering
  StartDrawing(ScreenOutput())
    
  ; Render textured walls
  RenderTexturedWalls()
  
  ; End rendering
  StopDrawing()
  
  ; Flip buffers
  FlipBuffers()
  
  ; Check keyboard + timed movement
  ExamineKeyboard()
  
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
  
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 5.0
  Global rotSpeed.f = frameTime * 3.0
  
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)
    
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
    
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
  
  EndIf
  
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
  
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
    
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
  
  EndIf
  
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
  
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
    
  EndIf
  
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
  
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
 
  
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)


; Render textured walls
Procedure RenderTexturedWalls()

  ; Misc vars
  Protected x.l = 0
  
  ; Draw some boxes
  Box(0,0,screenWidth, screenHeight/2, RGB(10,10,10)) 
  Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50)) 
  
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth-1
  
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
      
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
      
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
            
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
      
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
        
      Else
      
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
        
      EndIf
      
      If (rayDirY < 0)
      
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
        
      Else
      
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
        
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
        
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
        
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
        
        Else
        
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
          
        EndIf
        
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
        
          hit = 1
        
        EndIf         
        
      Wend
      
      ; Calculate distance projected onto camera direction (includes fisheye removal)
      If side = 0
      
        perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
      
      Else
      
        perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
      
      EndIf

      ; Calculate height of line to draw onto screen
      Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
            
      ; Calculate lowest and highest pixel for current line strip
      Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
      If drawStart < 0
        
        drawStart = 0
        
      EndIf
      
      Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
      If drawEnd >= screenHeight
      
        drawEnd = screenHeight - 1
        
      EndIf
      
      ; Using plot here. Since plot is optimized it's fine for a tutorial since I gave up on the direct pixel access in PB for now :)
       
      ; Determine exact point where wall was hit for texture generation
      Protected wallX.f
      If side = 1
        
        wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
        
      Else 
      
        wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
        
      EndIf
        
      wallX = wallX - Int(wallX); What no floor? Also added abs because of this. Line should be 'wallX = wallX - Floor(wallX)'
      wallX = Abs(wallX) ; Should not be here, but since we got no floor :)
       
      ; Calculate x coordinate on the texture
      Protected texX.l = Int(wallX * texWidth);
      If side = 0 And rayDirX > 0
      
        texX = texWidth - texX - 1
      
      EndIf
      
      If side = 1 And rayDirY < 0
      
        texX = texWidth - texX - 1
      
      EndIf
      
      ; Render textured strip
      Protected y.l = 0
      For y = drawStart To drawEnd
        
        Protected d.l = y * 256 - screenHeight * 128 + lineHeight * 128; Avoid floats
        
        Protected texY.l = ((d * texHeight) / lineHeight) / 256;
        Protected color.l = tex(texX, texY)
        
        ; Again shift to darken color depending on side
        If side = 1
        
         color = (color >> 1) & 8355711
        
        EndIf
        
        ; Draw pixel
        Plot(x,y,color)
      
     Next

  Next

EndProcedure

; Map data
DataSection

  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

EndDataSection  

Posted: Tue Dec 30, 2008 7:57 am
by Rook Zimbabwe
See I told you all... Just wait for Godai

:D

Posted: Tue Dec 30, 2008 8:39 am
by idle
I didn't expect that! :D

Now I want to play Wolf all over again!

Posted: Tue Dec 30, 2008 9:11 am
by Kwai chang caine
Great ....almost like a real game of ATARI, there are several years ago 8)
I don't know it's possible in PB :shock:
Congratulation 8)

Posted: Tue Dec 30, 2008 6:45 pm
by Anonymous
Great !

I adding lighting fadeout :
See line 323

Code: Select all

; Raycasting example 2 (Textured walls)

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l = 640
Global screenHeight.l = 480
Global texWidth.l = 64
Global texHeight.l = 64

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Texture
Global Dim tex.l(texWidth,texWidth)

; Declare render funcs
Declare RenderTexturedWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read.b worldMap(x, y)
  Next x
Next y

; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #2", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)

; Create texture image using bitwise or (|) texture generation
; Just a dim for  now, since I gave up on the direct pixel access for now :)
Global tx.l = 0
Global ty.l = 0

For ty = 0 To texWidth-1
  For tx = 0 To texWidth-1
 
    Global temp.b = (tx | ty) * 3 ; Times 3 for brighter texture :)
    tex(tx,ty) = RGB(temp,temp,temp)
 
  Next
Next

; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing 
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
 
  ; Pump window events
  Global Event.l = WindowEvent()
 
  ; Start rendering
  StartDrawing(ScreenOutput())
   
  ; Render textured walls
  RenderTexturedWalls()
 
  ; End rendering
  StopDrawing()
 
  ; Flip buffers
  FlipBuffers()
 
  ; Check keyboard + timed movement
  ExamineKeyboard()
 
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
 
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 5.0
  Global rotSpeed.f = frameTime * 3.0
 
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)
   
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
 
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
   
  EndIf
 
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
 
 
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)


; Render textured walls
Procedure RenderTexturedWalls()

  ; Misc vars
  Protected x.l = 0
 
  ; Draw some boxes
  Box(0,0,screenWidth, screenHeight/2, RGB(10,10,10))
  Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50))
 
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth-1
 
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
     
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
     
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
           
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
     
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
       
      Else
     
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
       
      EndIf
     
      If (rayDirY < 0)
     
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
       
      Else
     
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
       
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
       
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
       
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
       
        Else
       
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
         
        EndIf
       
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
       
          hit = 1
       
        EndIf         
       
      Wend
     
      ; Calculate distance projected onto camera direction (includes fisheye removal)
      If side = 0
     
        perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
     
      Else
     
        perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
     
      EndIf

      ; Calculate height of line to draw onto screen
      Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
           
      ; Calculate lowest and highest pixel for current line strip
      Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
      If drawStart < 0
       
        drawStart = 0
       
      EndIf
     
      Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
      If drawEnd >= screenHeight
     
        drawEnd = screenHeight - 1
       
      EndIf
     
      ; Using plot here. Since plot is optimized it's fine for a tutorial since I gave up on the direct pixel access in PB for now :)
       
      ; Determine exact point where wall was hit for texture generation
      Protected wallX.f
      If side = 1
       
        wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
       
      Else
     
        wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
       
      EndIf
       
      wallX = wallX - Int(wallX); What no floor? Also added abs because of this. Line should be 'wallX = wallX - Floor(wallX)'
      wallX = Abs(wallX) ; Should not be here, but since we got no floor :)
       
      ; Calculate x coordinate on the texture
      Protected texX.l = Int(wallX * texWidth);
      If side = 0 And rayDirX > 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      If side = 1 And rayDirY < 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      ; Render textured strip
      Protected y.l = 0
      For y = drawStart To drawEnd
       
        Protected d.l = y * 256 - screenHeight * 128 + lineHeight * 128; Avoid floats
       
        Protected texY.l = ((d * texHeight) / lineHeight) / 256;
        Protected color.l = tex(texX, texY)
       
        ; Again shift to darken color depending on side
        If side = 1
       
         color = (color >> 1) & 8355711
       
        EndIf
       
       
        ; Draw pixel
        
        Light = ((perpWallDist*255)/25)
        If Light < 0 : Light = 0 : EndIf
        If Light > 255 : Light = 255 : EndIf
        Factor.f = (255 - Light)/255
        
        Red_ = Red(color) *Factor 
        Green_ = Green(color)*Factor
        Blue_ = Blue(color)*Factor
        
        Plot(x,y, RGB(Red_,Green_,Blue_ )  )
     
     Next

  Next

EndProcedure

; Map data
DataSection

  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

EndDataSection 

Posted: Tue Dec 30, 2008 7:13 pm
by Rook Zimbabwe
Bator, NICE ONE! :D

It runs a bit slower on my laptop than the initial entry though and I do miss the colors! But all it needs is someone shouting "ACHTUNG!" randoomly and a chaingun and it is wolfenstien all over again!

Posted: Tue Dec 30, 2008 7:53 pm
by Godai
Hey! That was my job :)
Oh well. Maybe I can add some lightmaps later...

You could move the lighting calculation outside the strip render loop as the light is the same for the entire strip. In the old days we pre-calculated it in a lookup table, so that is also an option :)

Stay tuned as I will be adding textured floor and cieling as well as billboard objects for your Wolfensteiny pleasure!

Remember: Until I get to the optimization, turn off debugger for max speed

Posted: Tue Dec 30, 2008 8:03 pm
by Anonymous
Draw pixel on Z-BUFFER ( like allocatememory() )
with Z-Buffer , you can make volumetric fog , mirror , etc...
and rendering the z-buffer into a sprite ! :D

see this :

Image

File:1->CubeBench2.rar
Image

Posted: Tue Dec 30, 2008 10:16 pm
by idle
very cool, now I want to play but I really should do some work instead.

added in a few more textures

and added wolf bitmaps

edited for cross platform

Code: Select all


; Raycasting example 3 (bitmapped Textured walls)

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l = 640
Global screenHeight.l = 480
Global texWidth.l = 64
Global texHeight.l = 64

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Texture
Global Dim tex.l(9,texWidth,texWidth)

; Declare render funcs
Declare RenderTexturedWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read.b worldMap(x, y)
  Next x
Next y

;texture grabber------ 


UseJPEGImageDecoder()

If FileSize("wolf.jpg") < 0
 
 InitNetwork()
 rx = ReceiveHTTPFile("http://www.idlearts.com/images/wolf.jpg", "wolf.jpg")
  
EndIf
  
hImage = LoadImage(#PB_Any,"wolf.jpg")  

*tmem = AllocateMemory((texWidth*texWidth) * 3)
If hImage 
ptx.i
For a = 0 To 9 
   timg = GrabImage(hImage,#PB_Any,ptx,0,texWidth,texWidth)
   StartDrawing(ImageOutput(timg))
   
   For xx = 0 To texWidth 
   For yy = 0 To texWidth 
      tex(a,xx,yy) = Point(xx, yy)
   Next 
   Next 
   StopDrawing()   

   ptx + texWidth
   FreeImage(timg) 
Next 

EndIf 


; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #2", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)

; Create texture image using bitwise or (|) texture generation
; Just a dim for  now, since I gave up on the direct pixel access for now :)
Global tx.l = 0
Global ty.l = 0

;over writes the textures uncomment them to suit 
; For tx = 0 To texWidth -1   
;   For ty = 0 To texHeight -1
;   
;     xorcolor.i = ((tx * 256 / texWidth) ! (ty * 256 / texHeight));
;     ycolor.i = ty * 256 / texHeight;
;     xycolor.i = ty * 128 / texHeight + tx * 128 / texWidth;
;     ;If tx <> ty And tx <> texWidth - ty : xx=1 : Else : xx=0 :EndIf
;     ;tex(1,tx,ty) = 254 * xx; //flat red texture with black cross
;     ;tex(2,tx,ty) = xycolor + 256 * xycolor + 65536 * xycolor; //sloped greyscale
;     ;tex(3,tx,ty) = 65536 * xycolor + (255<<3) * xycolor; //sloped parrot gradient
;     ;tex(4,tx,ty) = xorcolor + 256 * xorcolor + 65536 * xorcolor; //xor greyscale
;     ;tex(5,tx,ty) = 256 * xorcolor; //xor green
;     ;tex(6,tx,ty) = 192 * ((tx % 16) & (ty % 16)) >> 1; //red bricks 
;     ;tex(7,tx,ty) = 65536 * ycolor; //red gradient
;     ;tex(0,tx,ty) = 128 + 256 * 128 + 65536 * 128; //flat grey texture
; 
;   Next
; Next

; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
 
  ; Pump window events
  Global Event.l = WindowEvent()
 
  ; Start rendering
  StartDrawing(ScreenOutput())
   
  ; Render textured walls
  RenderTexturedWalls()
  
  ; End rendering
  StopDrawing()
 
  ; Flip buffers
  FlipBuffers()
 
  ; Check keyboard + timed movement
  ExamineKeyboard()
 
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
 
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  ;SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 8.0
  Global rotSpeed.f = frameTime * 3.0
 
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)
   
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
 
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
   
  EndIf
 
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
 
 Delay(0)
 
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)



; Render textured walls
Procedure RenderTexturedWalls()

  ; Misc vars
  Protected x.l = 0
 
  ; Draw some boxes
  Box(0,0,screenWidth, screenHeight/2, RGB(10,10,10))
  Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50))
 
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth-1
 
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
     
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
     
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
           
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
     
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
       
      Else
     
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
       
      EndIf
     
      If (rayDirY < 0)
     
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
       
      Else
     
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
       
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
       
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
       
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
       
        Else
       
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
         
        EndIf
       
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
       
          hit = 1
       
        EndIf         
       
      Wend
     
      ; Calculate distance projected onto camera direction (includes fisheye removal)
      If side = 0
     
        perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
     
      Else
     
        perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
     
      EndIf

      ; Calculate height of line to draw onto screen
      Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
           
      ; Calculate lowest and highest pixel for current line strip
      Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
      If drawStart < 0
       
        drawStart = 0
       
      EndIf
     
      Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
      If drawEnd >= screenHeight
     
        drawEnd = screenHeight - 1
       
      EndIf
     
      ; Using plot here. Since plot is optimized it's fine for a tutorial since I gave up on the direct pixel access in PB for now :)
       
      ; Determine exact point where wall was hit for texture generation
      Protected wallX.f
      If side = 1
       
        wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
       
      Else
     
        wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
       
      EndIf
       
      wallX = wallX - Int(wallX); What no floor? Also added abs because of this. Line should be 'wallX = wallX - Floor(wallX)'
      wallX = Abs(wallX) ; Should not be here, but since we got no floor :)
       
      ; Calculate x coordinate on the texture
      Protected texX.l = Int(wallX * texWidth);
      If side = 0 And rayDirX > 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      If side = 1 And rayDirY < 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      ; Render textured strip
      Protected y.l = 0
      For y = drawStart To drawEnd
       
        Protected d.l = y * 256 - screenHeight * 128 + lineHeight * 128; Avoid floats
       
        Protected texY.l = ((d * texHeight) / lineHeight) / 256;
          
        Protected color.l = tex(worldMap(mapX, mapY)-1,texX, texY) 
       
        ; Again shift to darken color depending on side
        If side = 1
       
         color = (color >> 1) & 8355711
       
        EndIf
       
       
        ; Draw pixel
       
        Light = ((perpWallDist*255)/66)
        If Light < 0 : Light = 0 : EndIf
        If Light > 255 : Light = 255 : EndIf
        Factor.f = (255 - Light)/255
       
        Red_ = Red(color) *Factor
        Green_ = Green(color)*Factor
        Blue_ = Blue(color)*Factor
       
        Plot(x,y, RGB(Red_,Green_,Blue_ )  )
        
        
        
     Next
        
    
  Next

EndProcedure

; Map data
DataSection

  Data.b  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,3,3,3,3,0,0,0,0,3,0,3,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,3,0,0,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,3,0,3,3,0,0,0,0,3,0,3,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,4,4,4,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,2
  Data.b  1,0,0,0,0,4,0,0,0,0,6,6,6,0,0,0,8,8,0,0,0,0,0,2
  Data.b  2,0,0,0,0,4,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,0,0,0,0,2
  Data.b  1,5,5,5,5,5,5,5,5,0,0,0,0,0,7,0,0,0,7,0,0,0,0,2
  Data.b  1,5,0,5,0,0,0,0,5,0,0,0,0,0,7,0,9,0,7,0,0,0,0,2
  Data.b  1,5,0,0,0,0,9,0,5,0,0,0,0,0,7,0,0,0,7,0,0,0,0,2
  Data.b  1,5,0,5,0,0,0,0,5,0,0,0,0,0,7,7,0,7,7,0,0,0,0,2
  Data.b  1,5,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2

EndDataSection 

Posted: Wed Dec 31, 2008 4:16 am
by GBeebe
These examples work very well in Linux too :)
except, there isn't a BITMAP structure for the last one.

Posted: Wed Dec 31, 2008 4:25 am
by idle
Edited the post should work now

Posted: Wed Dec 31, 2008 8:57 am
by Godai
Great work guys. Although we really should load purebasic sprites and get the data from there (I was lazy that day).
The great thing about raycasting is that it can run on slow systems, even those without 3D hardware.
And it gives that nice retro look :)
(And it's cross-platform ofcourse)

BTW: Love the auto texture download. And inside the dungeon? Arggh. The eye!!!

Posted: Wed Dec 31, 2008 12:57 pm
by Anonymous
ZBuffer.pb

Code: Select all

Structure Z_BUF_INFO
  Color.l
  z.l
EndStructure

Structure Z_BUFFER
  *Background.l
  *Zbuffer.l
  FogColor.l
  FogStart.f
  FogEnd.f
  ZbufCountOp.l ; Compteur d'opérations 
  SizeW.l
  SizeH.l
EndStructure


Global SoftZbuffer.Z_BUFFER

#FOG_S=0
#FOG_E=30

Declare ColorLuminosity(Couleur, Echelle.f) 
Declare ColorTint(Couleur, Echelle.f)
Declare ColorBlend(ColA.l,ColB.l,Percent.l)


Procedure EnableZBuffer(W.l,H.l)
    SoftZbuffer\Background = AllocateMemory(W*H*SizeOf(Z_BUF_INFO)+SizeOf(Z_BUF_INFO))
    SoftZbuffer\Zbuffer    = AllocateMemory((W*H*SizeOf(Z_BUF_INFO))+SizeOf(Z_BUF_INFO))
    SoftZbuffer\FogColor   = $0
    SoftZbuffer\FogStart   = #FOG_S
    SoftZbuffer\FogEnd     = #FOG_E
    SoftZbuffer\SizeW      = W
    SoftZbuffer\SizeH      = H
    
      For By = 0 To SoftZbuffer\SizeH-1
        For Bx = 0 To SoftZbuffer\SizeW-1
          PokeL( SoftZbuffer\Zbuffer + (Bx*Szf) + W * (By*SizeOf(Z_BUF_INFO)) ,$0000FF)
          PokeL((SoftZbuffer\Zbuffer + (Bx*Szf) + W * (By*SizeOf(Z_BUF_INFO))+4),FogEnd)
        Next
      Next 
    
    
    
EndProcedure

Procedure WriteZBuffer(x,y,z,color)


If z>0 And z<SoftZbuffer\FogEnd ;And PeekL( (SoftZbuffer\Zbuffer + (X*Szf) + SizeW * (Y*SzF))+4)>z
  SzF = SizeOf(Z_BUF_INFO)
  PokeL( SoftZbuffer\Zbuffer + (x*Szf) + SoftZbuffer\SizeW * (y*SizeOf(Z_BUF_INFO)) ,color)
  PokeL((SoftZbuffer\Zbuffer + (x*Szf) + SoftZbuffer\SizeW * (y*SizeOf(Z_BUF_INFO))+4),z)
  SoftZbuffer\ZbufCountOp + 1
  ProcedureReturn 1
EndIf   
  ProcedureReturn -1
EndProcedure

Procedure.l ReadZBuffer(X,Y)
SzF = SizeOf(Z_BUF_INFO)
  ProcedureReturn PeekL( (SoftZbuffer\Zbuffer + (X*Szf) + SoftZbuffer\SizeW * (Y*SzF))+4) ;Zvalue
EndProcedure

Procedure FlipZbuffer(Output)

Static SY.l,Timer.l





StartDrawing(Output)
    
    Buffer  = DrawingBuffer()
    BufferP = DrawingBufferPitch()
    PixelF  = DrawingBufferPixelFormat()
   
   
    Select PixelF
      Case  #PB_PixelFormat_8Bits       : PF=1
      Case  #PB_PixelFormat_15Bits      : PF=2
      Case  #PB_PixelFormat_16Bits      : PF=2
      Case  #PB_PixelFormat_24Bits_RGB  : PF=3
      Case  #PB_PixelFormat_24Bits_BGR  : PF=3
      Case  #PB_PixelFormat_32Bits_RGB  : PF=4
      Case  #PB_PixelFormat_32Bits_BGR  : PF=4
    EndSelect 

    
    SzF = SizeOf(Z_BUF_INFO)
    
    For By = 0 To SoftZbuffer\SizeH-1   Step 2 ; SCANLINE
      For Bx = 0 To SoftZbuffer\SizeW-1
      
      
      
       ZValue = PeekL( (SoftZbuffer\Zbuffer + (Bx*Szf) + SoftZbuffer\SizeW * (By*SzF))+4)
   
       If ZValue => SoftZbuffer\FogEnd
        PokeL((SoftZbuffer\Zbuffer + (Bx*Szf) + SoftZbuffer\SizeW * (By*SizeOf(Z_BUF_INFO))),SoftZbuffer\FogColor)
        PokeL((SoftZbuffer\Zbuffer + (Bx*Szf) + SoftZbuffer\SizeW * (By*SizeOf(Z_BUF_INFO))+4),SoftZbuffer\FogEnd)
       EndIf 
   
    If ZValue > 0 
      ; Calcul de la luminosité finale du Zbuffer
        Fct.f =  (1/(ZValue/(SoftZbuffer\FogEnd/8)))
        Col = PeekL( (SoftZbuffer\Zbuffer + (Bx*Szf) + SoftZbuffer\SizeW * (By*SzF)))
        Col = ColorLuminosity(Col, Fct)
    ; Melange de la couleur avec le brouillard ambient    
        FogSize = SoftZbuffer\FogEnd + SoftZbuffer\FogStart
        Col = ColorBlend(SoftZbuffer\FogColor,Col,(ZValue * 100)/FogSize)
      
      
;       If SY+50>By And SY<By
;         Col = ColorLuminosity(Col,1)
;         Col = ColorBlend(Col,$FFFFFF,Random(100))
;       Else
;         Col = ColorLuminosity(Col,0.9)
;       EndIf 
      
        ;Color = PeekL( (SoftZbuffer\Zbuffer + (Bx*Szf) + SizeW * (By*SzF)))
        AdresseVideo  = Buffer + ( Bx * PF ) + (BufferP/PF) * (By * PF)
        PokeL(AdresseVideo,Col) 
        Else
         AdresseVideo  = Buffer + ( Bx * PF ) + (BufferP/PF) * (By * PF)
         PokeL(AdresseVideo,SoftZbuffer\FogColor) 
    EndIf  
        
        
        
        
        
        
      Next
    Next 



StopDrawing()


EndProcedure


Procedure ClearZBuffer()
FreeMemory(SoftZbuffer\Zbuffer)
SoftZbuffer\Zbuffer     = AllocateMemory((SoftZbuffer\SizeW * SoftZbuffer\SizeH*SizeOf(Z_BUF_INFO))+SizeOf(Z_BUF_INFO))
SoftZbuffer\ZbufCountOp = 0
EndProcedure

Procedure.l ColorLuminosity(Couleur, Echelle.f) 
  Protected Rouge, Vert, Bleu
 
  Rouge = Red(Couleur) * Echelle
  Vert = Green(Couleur) * Echelle
  Bleu = Blue(Couleur) * Echelle
 
  If Rouge > 255 : Rouge = 255 : EndIf
  If Vert > 255 : Vert = 255 : EndIf
  If Bleu > 255 : Bleu = 255 : EndIf
 
  ProcedureReturn RGB(Rouge, Vert, Bleu)
EndProcedure 

Procedure.l ColorTint(Couleur, Echelle.f) ; Changer la teinte d'une couleur (Echelle comprise entre -1 et 1)
  Protected Rouge, Vert, Bleu
 
  Rouge = Red(Couleur)
  Vert = Green(Couleur)
  Bleu = Blue(Couleur)
 
  If Echelle > 0
    Rouge = Rouge * (1 - Echelle) + Bleu * Echelle
    Vert = Vert * (1 - Echelle) + Rouge * Echelle
    Bleu = Bleu * (1 - Echelle) + Vert * Echelle
  Else
    Rouge = Rouge * (1 + Echelle) - Vert * Echelle
    Vert = Vert * (1 + Echelle) - Bleu * Echelle
    Bleu = Bleu * (1 + Echelle) - Rouge * Echelle
  EndIf
   
  ProcedureReturn RGB(Rouge, Vert, Bleu)
EndProcedure

Procedure.l ColorBlend(ColA.l,ColB.l,Percent.l) ; Melange deux couleur en %
FR= ((Red(ColA)*Percent)/100) + ((Red(ColB)*(100-Percent)) / 100) 
FG= ((Green(ColA)*Percent)/100) + ((Green(ColB)*(100-Percent)) / 100) 
FB= ((Blue(ColA)*Percent)/100) + ((Blue(ColB)*(100-Percent)) / 100) 
ProcedureReturn RGB(FR,FG,FB)
EndProcedure

; EnableZBuffer()
; 
; WriteZBuffer(10,10,100,255)
; Debug ReadZBuffer(10,10)



And the main file :

Code: Select all

XIncludeFile "ZBuffer.pb"


; Raycasting example 3 (bitmapped Textured walls)

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l = 640
Global screenHeight.l = 480
Global texWidth.l = 64
Global texHeight.l = 64

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Texture
Global Dim tex.l(9,texWidth,texWidth)

; Declare render funcs
Declare RenderTexturedWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read.b worldMap(x, y)
  Next x
Next y

;texture grabber------


UseJPEGImageDecoder()

If FileSize("wolf.jpg") < 0
 
 InitNetwork()
 rx = ReceiveHTTPFile("http://www.idlearts.com/images/wolf.jpg", "wolf.jpg")
 
EndIf
 
hImage = LoadImage(#PB_Any,"wolf.jpg") 

*tmem = AllocateMemory((texWidth*texWidth) * 3)
If hImage
ptx.i
For a = 0 To 9
   timg = GrabImage(hImage,#PB_Any,ptx,0,texWidth,texWidth)
   StartDrawing(ImageOutput(timg))
   
   For xx = 0 To texWidth
   For yy = 0 To texWidth
      tex(a,xx,yy) = Point(xx, yy)
   Next
   Next
   StopDrawing()   

   ptx + texWidth
   FreeImage(timg)
Next

EndIf


; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #2", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)

;CREATE Z-BUFFER
EnableZBuffer( screenWidth, screenHeight)


; Create texture image using bitwise or (|) texture generation
; Just a dim for  now, since I gave up on the direct pixel access for now :)
Global tx.l = 0
Global ty.l = 0

;over writes the textures uncomment them to suit
; For tx = 0 To texWidth -1   
;   For ty = 0 To texHeight -1
;   
;     xorcolor.i = ((tx * 256 / texWidth) ! (ty * 256 / texHeight));
;     ycolor.i = ty * 256 / texHeight;
;     xycolor.i = ty * 128 / texHeight + tx * 128 / texWidth;
;     ;If tx <> ty And tx <> texWidth - ty : xx=1 : Else : xx=0 :EndIf
;     ;tex(1,tx,ty) = 254 * xx; //flat red texture with black cross
;     ;tex(2,tx,ty) = xycolor + 256 * xycolor + 65536 * xycolor; //sloped greyscale
;     ;tex(3,tx,ty) = 65536 * xycolor + (255<<3) * xycolor; //sloped parrot gradient
;     ;tex(4,tx,ty) = xorcolor + 256 * xorcolor + 65536 * xorcolor; //xor greyscale
;     ;tex(5,tx,ty) = 256 * xorcolor; //xor green
;     ;tex(6,tx,ty) = 192 * ((tx % 16) & (ty % 16)) >> 1; //red bricks
;     ;tex(7,tx,ty) = 65536 * ycolor; //red gradient
;     ;tex(0,tx,ty) = 128 + 256 * 128 + 65536 * 128; //flat grey texture
;
;   Next
; Next

; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
 ClearScreen(0)
 
  ; Pump window events
  Global Event.l = WindowEvent()
 
  ; Start rendering
  ;StartDrawing(ScreenOutput())
   ClearZBuffer()
  ; Render textured walls
  RenderTexturedWalls()
 
 FlipZbuffer(ScreenOutput())
 
  ; End rendering
  ;StopDrawing()
 
  ; Flip buffers
  FlipBuffers()
 
  ; Check keyboard + timed movement
  ExamineKeyboard()
 
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
 
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  ;SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 8.0
  Global rotSpeed.f = frameTime * 3.0
 
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)
   
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
 
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
   
  EndIf
 
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
 
 Delay(0)
 
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)



; Render textured walls
Procedure RenderTexturedWalls()

  ; Misc vars
  Protected x.l = 0
 
  ; Draw some boxes
  ;Box(0,0,screenWidth, screenHeight/2, RGB(10,10,10))
  ;Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50))
 
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth-1
 
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
     
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
     
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
           
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
     
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
       
      Else
     
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
       
      EndIf
     
      If (rayDirY < 0)
     
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
       
      Else
     
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
       
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
       
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
       
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
       
        Else
       
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
         
        EndIf
       
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
       
          hit = 1
       
        EndIf         
       
      Wend
     
      ; Calculate distance projected onto camera direction (includes fisheye removal)
      If side = 0
     
        perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
     
      Else
     
        perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
     
      EndIf

      ; Calculate height of line to draw onto screen
      Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
           
      ; Calculate lowest and highest pixel for current line strip
      Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
      If drawStart < 0
       
        drawStart = 0
       
      EndIf
     
      Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
      If drawEnd >= screenHeight
     
        drawEnd = screenHeight - 1
       
      EndIf
     
      ; Using plot here. Since plot is optimized it's fine for a tutorial since I gave up on the direct pixel access in PB for now :)
       
      ; Determine exact point where wall was hit for texture generation
      Protected wallX.f
      If side = 1
       
        wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
       
      Else
     
        wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
       
      EndIf
       
      wallX = wallX - Int(wallX); What no floor? Also added abs because of this. Line should be 'wallX = wallX - Floor(wallX)'
      wallX = Abs(wallX) ; Should not be here, but since we got no floor :)
       
      ; Calculate x coordinate on the texture
      Protected texX.l = Int(wallX * texWidth);
      If side = 0 And rayDirX > 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      If side = 1 And rayDirY < 0
     
        texX = texWidth - texX - 1
     
      EndIf
     
      ; Render textured strip
      Protected y.l = 0
      For y = drawStart To drawEnd
       
        Protected d.l = y * 256 - screenHeight * 128 + lineHeight * 128; Avoid floats
       
        Protected texY.l = ((d * texHeight) / lineHeight) / 256;
         
        Protected color.l = tex(worldMap(mapX, mapY)-1,texX, texY)
       
        ; Again shift to darken color depending on side
        If side = 1
       
         color = (color >> 1) & 8355711
       
        EndIf
       
       
        ; Draw pixel
       
        Light = ((perpWallDist*255)/12)
        If Light < 0 : Light = 0 : EndIf
        If Light > 255 : Light = 255 : EndIf
        Factor.f = (255 - Light)/255
       
        Red_ = Red(color) *Factor
        Green_ = Green(color)*Factor
        Blue_ = Blue(color)*Factor
       
        ;Plot(x,y, RGB(Red_,Green_,Blue_ )  )
       WriteZBuffer(x,y,perpWallDist, color )
       
       
     Next
       
   
  Next

EndProcedure

; Map data
DataSection

  Data.b  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,3,3,3,3,0,0,0,0,3,0,3,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,3,0,0,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,3,3,0,3,3,0,0,0,0,3,0,3,0,3,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,4,4,4,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,2
  Data.b  1,0,0,0,0,4,0,0,0,0,6,6,6,0,0,0,8,8,0,0,0,0,0,2
  Data.b  2,0,0,0,0,4,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,2
  Data.b  2,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,0,0,0,0,2
  Data.b  1,5,5,5,5,5,5,5,5,0,0,0,0,0,7,0,0,0,7,0,0,0,0,2
  Data.b  1,5,0,5,0,0,0,0,5,0,0,0,0,0,7,0,9,0,7,0,0,0,0,2
  Data.b  1,5,0,0,0,0,9,0,5,0,0,0,0,0,7,0,0,0,7,0,0,0,0,2
  Data.b  1,5,0,5,0,0,0,0,5,0,0,0,0,0,7,7,0,7,7,0,0,0,0,2
  Data.b  1,5,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2
  Data.b  1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2

EndDataSection

Posted: Wed Dec 31, 2008 1:02 pm
by Godai
Cool. Actually we'll need a z-buffer later for sorting billboard objects :)

Posted: Fri Jan 02, 2009 4:31 am
by GBeebe
@Cpl.Bator That's super slow on my machine. I made my own attempt at a zbuffer, I drew a purble box in the center of the texture, purple is ignored so it looks as if there's a hole in it...

Code: Select all

; Raycasting example 2 (Textured walls)

; Some general stuff
Global mapWidth.l = 24
Global mapHeight.l = 24
Global screenWidth.l =320
Global screenHeight.l = 200
Global texWidth.l = 64
Global texHeight.l = 64

Global Dim PixelZ.f (screenWidth, screenHeight)

; Map container
Global Dim worldMap.b(mapWidth, mapHeight)

; Texture
Global Dim tex.l(texWidth,texWidth)

; Declare render funcs
Declare RenderTexturedWalls()

; Read map
Global x.l = 0
Global y.l = 0
For y.l = 0 To mapHeight-1
  For x.l = 0 To mapWidth-1
    Read worldMap(x, y)
  Next x
Next y

; Open window screen + init keyboard/graphics screen (No error checking as this is an example of raycasting)
InitKeyboard()
InitSprite()
;OpenWindow(0, 0, 0, screenWidth, screenHeight, "Raycast Example #2", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
;OpenWindowedScreen(WindowID(0), 0, 0, screenWidth, screenHeight, 0, 0, 0)
OpenScreen(screenWidth, screenHeight, 24, "")
; Create texture image using bitwise or (|) texture generation
; Just a dim for  now, since I gave up on the direct pixel access for now :)
Global tx.l = 0
Global ty.l = 0

For ty = 0 To texWidth-1
  For tx = 0 To texWidth-1
 
    Global temp.b = (tx | ty) * 3 ; Times 3 for brighter texture :)
    tex(tx,ty) = RGB(temp,temp,temp)
 
  Next
Next
;I'm cutting a hile in the texture
For a = 22 To 42
    For b = 22 To 42
        tex(a, b) = 16711935 ;this is purple: rgb(255, 0, 255) 
    Next
Next
    
; Start position
Global posX.f = 22
Global posY.f = 12

; Initial direction vector
Global dirX.f = -1
Global dirY.f = 0

; 2D camera plane
Global planeX.f = 0
Global planeY.f = 0.66

; Frame timing
Global time.f = 0
Global oldTime.f = 0

; Main loop
Repeat
 
  ; Pump window events
  ;Global Event.l = WindowEvent()
 
  ; Start rendering
  StartDrawing(ScreenOutput())
   
  ; Render textured walls
  RenderTexturedWalls()
 
  ; End rendering
  StopDrawing()
 
  ; Flip buffers
  FlipBuffers()
 
  ; Check keyboard + timed movement
  ExamineKeyboard()
 
  ; Calculate frame timing
  oldTime = time;
  time = ElapsedMilliseconds();
  Global frameTime.f = (time - oldTime) / 1000.0
 
  ; Set FPS in title bar (wonky in PB. Dunno why. Works in C++/Flash version ;)
  ;SetWindowTitle(0, "Raycast Example #1 (FPS: " + Str(Int(1.0 / frameTime)) + ")")

  ; Calculate speed modifiers (constant values are squares/second and radians/second)
  Global moveSpeed.f = frameTime * 5.0
  Global rotSpeed.f = frameTime * 3.0
 
 ;move right?
 If KeyboardPushed(#PB_Key_Period)
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirY * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirX * moveSpeed)) = 0
      posY = posY + (dirX * moveSpeed)
    EndIf
 
 EndIf
 
 ;move left?
 If KeyboardPushed(#PB_Key_Comma)
    If worldMap(Int(posX + dirY * moveSpeed), Int(posY)) = 0
      posX = posX - (dirY * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY - (dirX * moveSpeed)
    EndIf
 
 EndIf
 
  ; Move forward?
  If KeyboardPushed(#PB_Key_Up)  
    
    If worldMap(Int(posX + dirX * moveSpeed), Int(posY)) = 0
      posX = posX + (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX), Int(posY + dirY * moveSpeed)) = 0
      posY = posY + (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Move back?
  If KeyboardPushed(#PB_Key_Down)
 
    If worldMap(Int(posX - dirX * moveSpeed),Int(posY)) = 0
      posX = posX - (dirX * moveSpeed)
    EndIf
   
    If worldMap(Int(posX),Int(posY - dirY * moveSpeed)) = 0
      posY = posY - (dirY * moveSpeed)
    EndIf
 
  EndIf
 
  ; Rotate right? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Right)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(-rotSpeed) - dirY * Sin(-rotSpeed)
    dirY = oldDirX * Sin(-rotSpeed) + dirY * Cos(-rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(-rotSpeed) - planeY * Sin(-rotSpeed)
    planeY = oldPlaneX * Sin(-rotSpeed) + planeY * Cos(-rotSpeed)
   
  EndIf
 
  ; Rotate left? (Both camera and camera plane needs to be rotated)
  If KeyboardPushed(#PB_Key_Left)
 
    Global oldDirX.f = dirX
    dirX = dirX * Cos(rotSpeed) - dirY * Sin(rotSpeed)
    dirY = oldDirX * Sin(rotSpeed) + dirY * Cos(rotSpeed)
    Global oldPlaneX.f = planeX
    planeX = planeX * Cos(rotSpeed) - planeY * Sin(rotSpeed)
    planeY = oldPlaneX * Sin(rotSpeed) + planeY * Cos(rotSpeed)

  EndIf
 
 
Until  KeyboardPushed(#PB_Key_Escape)


; Render textured walls
Procedure RenderTexturedWalls()

  ; Misc vars
  Protected x.l = 0
 
  ; Draw some boxes
  Box(0,0,screenWidth, screenHeight/2, RGB(10,10,255))
  Box(0,screenHeight/2,screenWidth,screenHeight/2 , RGB(50,50,50))
 
  ; Render wall strips for each vertical screen row (640 in this case. 320 in good old Wolfenstein)
  For x.l = 0 To screenWidth-1
 
      ; Calculate ray position and direction
      Protected cameraX.f = 2 * x / screenWidth-1
      Protected rayPosX.f = posX
      Protected rayPosY.f = posY
      Protected rayDirX.f = dirX + planeX * cameraX
      Protected rayDirY.f = dirY + planeY * cameraX
     
      ; Determine map tile we're in
      Protected mapX.l = Int(rayPosX)
      Protected mapY.l = Int(rayPosY)
     
      ; Length of ray from current position to next x or y-side
      Protected sideDistX.f = 0
      Protected sideDistY.f = 0
           
      ; Length of ray from one x or y-side to next x or y-side
      Protected deltaDistX.f = Sqr(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
      Protected deltaDistY.f = Sqr(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
      Protected perpWallDist.f

      ; What direction to step in x or y-direction (either +1 or -1)
      Protected stepX.l
      Protected stepY.l

      ; Was a wall hit, and if so which side
      Protected hit.l = 0
      Protected side.l

      ; Calculate step and initial sideDist
      If rayDirX < 0
     
        stepX = -1
        sideDistX = (rayPosX - mapX) * deltaDistX
       
      Else
     
        stepX = 1
        sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
       
      EndIf
     
      If (rayDirY < 0)
     
        stepY = -1
        sideDistY = (rayPosY - mapY) * deltaDistY
       
      Else
     
        stepY = 1
        sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
       
      EndIf

      ; Perform DDA algorithm for true hit detection
      While hit = 0
       
        ; Jump to next map square depending on direction
        If sideDistX < sideDistY
       
          sideDistX = sideDistX + deltaDistX
          mapX = mapX + stepX
          side = 0
       
        Else
       
          sideDistY = sideDistY + deltaDistY
          mapY = mapY + stepY
          side = 1
         
        EndIf
       
        ; See if ray has hit a wall
        If worldMap(mapX, mapY) > 0
            hit = 1
            ; Calculate distance projected onto camera direction (includes fisheye removal)
            If side = 0
                perpWallDist = Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
            Else
                perpWallDist = Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
            EndIf
            ; Calculate height of line to draw onto screen
            Protected lineHeight.l = Abs(Int(screenHeight / perpWallDist))
           
            ; Calculate lowest and highest pixel for current line strip
            Protected drawStart.l = -lineHeight / 2 + (screenHeight / 2)
            If drawStart < 0
                drawStart = 0
            EndIf
            
            Protected drawEnd.l = lineHeight / 2 + (screenHeight / 2)
            If drawEnd >= screenHeight
                drawEnd = screenHeight - 1
            EndIf
     
            ; Using plot here. Since plot is optimized it's fine for a tutorial since I gave up on the direct pixel access in PB for now :)
       
            ; Determine exact point where wall was hit for texture generation
            Protected wallX.f
            If side = 1
                wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
            Else
                wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
            EndIf
            
            wallX = wallX - Int(wallX); What no floor? Also added abs because of this. Line should be 'wallX = wallX - Floor(wallX)'
            wallX = Abs(wallX) ; Should not be here, but since we got no floor :)
       
            ; Calculate x coordinate on the texture
            Protected texX.l = Int(wallX * texWidth);
            If side = 0 And rayDirX > 0
                texX = texWidth - texX - 1
            EndIf
            
            If side = 1 And rayDirY < 0
                texX = texWidth - texX - 1
            EndIf
     
            ; Render textured strip
            Protected y.l = 0
            For y = drawStart To drawEnd
                If PixelZ(x, y)  > perpWallDist Or PixelZ(x, y) = 0
                    Protected d.l = y * 256 - screenHeight * 128 + lineHeight * 128; Avoid floats
       
                    Protected texY.l = ((d * texHeight) / lineHeight) / 256;
                    Protected color.l = tex(texX, texY)
       
                    If color.l <> 16711935
                        ; Again shift to darken color depending on side
                        If side = 1
                            color = (color >> 1) & 8355711
                        EndIf
                        ; Draw pixel
       
                        Light = ((perpWallDist*255)/25)
                        If Light < 0 : Light = 0 : EndIf
                        If Light > 255 : Light = 255 : EndIf
                        Factor.f = (255 - Light)/255
       
                        Red_ = Red(color) *Factor
                        Green_ = Green(color)*Factor
                        Blue_ = Blue(color)*Factor
                    
                        Plot(x,y, RGB(Red_,Green_,Blue_ )  )
                        PixelZ(x, y) = perpWallDist
                    Else
                        If worldMap(mapX, mapY) <> 1
                            Hit = 0
                            
                        EndIf    
                    EndIf
               EndIf    
            
                
            Next
        EndIf   
      Wend
  Next


For a = 0 To screenWidth - 1
    For b =-0 To screenHeight - 1
        PixelZ(a, b) = 0
    Next
Next
EndProcedure

; Map data
DataSection

  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
  Data.b  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

EndDataSection
I want to add a "skybox" but can't figure out how to determine the Angle (in degrees) the camera is facing. Also, I'm having a problem figuring out how to move left/right without changing angles. Also is there a faster way to clear an array than the way i'm doing it at the end of the RenderTexturedWalls procedure? Like Redim but without preserving the data?