Object Orientated, with Textures. Using Pixmap to display out.

Example:
Download the complete example with *.exe file:
http://vertex.art-fx.org/raycaster/raycaster.zip
cu olli
Const FACE_FRONT : Byte = 1 Const FACE_BACK : Byte = 2 Const FACE_LEFT : Byte = 3 Const FACE_RIGHT : Byte = 4 Type TRaycaster Field TileSize : Int Field TileCount : Int Field Tiles : TTexture[] Field MapSize : Int[2] Field Map : Int[,] Field Camera : TCamera Method New() Self.TileSize = 64 Self.TileCount = 0 Self.MapSize = [0, 0] Self.Camera = Null End Method Method LoadTile:Int(URL:Object) Local Texture:TTexture Texture = TTexture.Load(URL, Self.TileSize) If Not Texture Then Return -1 Self.TileCount :+ 1 Tiles = Tiles[..Self.TileCount] Tiles[Self.TileCount-1] = Texture Return Self.TileCount End Method Method SetMapSize(Columns:Int, Rows:Int) Self.MapSize[0] = Columns Self.MapSize[1] = Rows Self.Map = New Int[Columns, Rows] End Method Method SetTile(Column:Int, Row:Int, TileNr:Int) If (Column < 0 Or Column => Self.MapSize[0]) Or .. (Row < 0 Or Row => Self.MapSize[1]) Or .. (TileNr < 0 Or TileNr > Self.TileCount) Then Return Self.Map[Column, Row] = TileNr End Method Method SetCamera(Camera:TCamera) Self.Camera = Camera End Method Method Render() If Self.Camera Then Self.Camera.Render(Self) End Method End Type Type TCamera Field FOV : Float Field Viewport : Int[4] Field ClearColor : Byte[4] Field Position : Float[2] Field Yaw : Float Field Screen : TPixmap Method New() Self.SetFOV(60.0) Self.SetViewport(0, 0, GraphicsWidth(), GraphicsHeight()) Self.SetClearColor(0, 0, 0) Self.SetPosition(0.0, 0.0) Self.SetRotation(0.0) End Method Method SetFOV(FOV:Float) Self.FOV = FOV End Method Method SetViewport(X:Int, Y:Int, Width:Int, Height:Int) Self.Viewport[0] = X Self.Viewport[1] = Y Self.Viewport[2] = Width Self.Viewport[3] = Height Self.Screen = CreatePixmap(Width, Height, PF_RGBA8888) End Method Method SetClearColor(Red:Byte, Green:Byte, Blue:Byte, Alpha:Byte=255) Self.ClearColor[0] = Red Self.ClearColor[1] = Green Self.ClearColor[2] = Blue Self.CLearColor[3] = Alpha End Method Method SetRotation(Yaw:Float) Self.Yaw = Yaw End Method Method Turn(DeltaYaw:Float) Self.Yaw :+ DeltaYaw End Method Method SetPosition(X:Float, Z:Float) Self.Position[0] = X Self.Position[1] = Z End Method Method Translate(X:Float, Z:Float) Self.Position[0] :+ X Self.Position[1] :+ Z End Method Method Move(Speed:Float) Self.Position[0] :+ (Cos(Self.Yaw)*Speed) Self.Position[1] :+ (Sin(Self.Yaw)*Speed) End Method Method Clear() Local X:Int, Y:Int, Offset:Byte Ptr Offset = Self.Screen.Pixels For Y = 0 Until Self.Viewport[3] For X = 0 Until Self.Viewport[2] Offset[0] = Self.ClearColor[0] Offset[1] = Self.ClearColor[1] Offset[2] = Self.ClearColor[2] Offset[3] = Self.ClearColor[3] Offset :+ 4 Next Next End Method Method Render(Raycaster:TRaycaster) Local DeltaAngle:Float, Ray:TRay, Column:Int Local Dividend:Float, Height:Int Local Texture:TTexture, TextureColumn:Float ' Clear Background Self.Clear() ' Calculate Angle for one Screen-Column DeltaAngle = Self.FOV/Float(Self.Viewport[2]) ' Emit Rays Ray = New TRay Ray.Angle = Self.Yaw - Self.FOV/2.0 ' Dividend to calculate the projected Height Dividend = Float(Raycaster.TileSize) * .. Sin(90.0-Self.FOV/2.0)*Float(Self.Viewport[2]/2) For Column = 0 Until Self.Viewport[2] Ray.Z = Self.Position[1] Ray.Emit(Raycaster) If Ray.Tile > 0 Then Texture = Raycaster.Tiles[Ray.Tile-1] Select Ray.Face Case FACE_FRONT TextureColumn = Ray.X Mod Float(Raycaster.TileSize) Case FACE_BACK TextureColumn = Float(Raycaster.TileSize) - (Ray.X .. Mod Float(Raycaster.TileSize)) - 1.0 Case FACE_LEFT TextureColumn = Float(Raycaster.TileSize) - (Ray.Z .. Mod Float(Raycaster.TileSize)) - 1 Case FACE_RIGHT TextureColumn = Ray.Z Mod Float(Raycaster.TileSize) End Select ' Correct the Fisheye-Effect Height = Dividend/(Ray.Distance*Cos(Self.Yaw - Ray.Angle)) ' Draw scaled Comlumn Texture.DrawColumn(Column, TextureColumn, Height, Self) EndIf Ray.Angle :+ DeltaAngle Next ' Display the Result DrawPixmap(Self.Screen, Self.Viewport[0], Self.Viewport[1]) End Method End Type Type TRay Field Angle : Float Field X : Float Field Z : Float Field Tile : Int Field Distance : Float Field Face : Byte Method Emit(Raycaster:TRaycaster) Local DeltaX:Float, DeltaZ:Float, DX:Float, DZ:Float, TanAngle:Float Local Quit:Int, Face:Byte, MapColumn:Int, MapRow:Int, Tile:Int, Distance:Float Local OldX:Float, OldZ:Float, OldTile:Int, OldDistance:Float, OldFace:Byte ' 0° => Angle <= 360° Self.Angle = Self.Angle Mod 360.0 If Self.Angle < 0.0 Then Self.Angle = 360.0+Self.Angle TanAngle = Tan(Self.Angle) ' Find horizontal intersection Quit = False If Self.Angle > 0.0 And Self.Angle < 180.0 Then DeltaZ = Float(Raycaster.TileSize) DeltaX = DeltaZ/TanAngle Self.Z = Float((Int(Raycaster.Camera.Position[1]) / .. Raycaster.TileSize)*Raycaster.TileSize + Raycaster.TileSize) DZ = Self.Z - Raycaster.Camera.Position[1] DX = DZ/TanAngle Self.X = Raycaster.Camera.Position[0] + DX Face = FACE_BACK ElseIf Self.Angle > 180.0 And Self.Angle < 360.0 Then DeltaZ = -Float(Raycaster.TileSize) DeltaX = DeltaZ/TanAngle Self.Z = Float((Int(Raycaster.Camera.Position[1]) / .. Raycaster.TileSize)*Raycaster.TileSize) DZ = Self.Z - Raycaster.Camera.Position[1] DX = DZ/TanAngle Self.X = Raycaster.Camera.Position[0] + DX FACE = FACE_FRONT ElseIf Self.Angle = 0.0 Or Self.Angle = 180.0 Quit = True ElseIf Self.Angle = 90.0 Then DeltaZ = Float(Raycaster.TileSize) DeltaX = 0.0 Self.Z = Float((Int(Raycaster.Camera.Position[1]) / .. Raycaster.TileSize)*Raycaster.TileSize + Raycaster.TileSize) Self.X = Raycaster.Camera.Position[0] FACE = FACE_BACK ElseIf Self.Angle = 270.0 Then DeltaZ = -Float(Raycaster.TileSize) DeltaX = 0.0 Self.Z = Float((Int(Raycaster.Camera.Position[1]) / .. Raycaster.TileSize)*Raycaster.TileSize) Self.X = Raycaster.Camera.Position[0] FACE = FACE_FRONT EndIf Tile = 0 While Not Quit MapColumn = Int(Self.X)/Raycaster.TileSize If Self.Angle => 0.0 And Self.Angle <= 180.0 Then MapRow = Int(Self.Z)/Raycaster.TileSize ElseIf Self.Angle > 180.0 And Self.Angle < 360.0 Then MapRow = Int(Self.Z)/Raycaster.TileSize - 1 EndIf If MapColumn < 0 Or MapColumn > Raycaster.MapSize[0] - 1 Or .. MapRow < 0 Or MapRow > Raycaster.MapSize[1] - 1 Then Exit Tile = Raycaster.Map[MapColumn, MapRow] If Tile > 0 Then Exit Self.X :+ DeltaX Self.Z :+ DeltaZ Wend If Tile > 0 Then DX = Raycaster.Camera.Position[0] - Self.X DZ = Raycaster.Camera.Position[1] - Self.Z Distance = Sqr(DX*DX + DZ*DZ) EndIf OldX = Self.X OldZ = Self.Z OldDistance = Distance OldTile = Tile OldFace = Face ' Find vertical intersection Quit = False If (Self.Angle > 270.0 And Self.Angle < 360.0) Or .. (Self.Angle > 0.0 And Self.Angle < 90.0) Then DeltaX = Float(Raycaster.TileSize) DeltaZ = TanAngle*DeltaX Self.X = Float(Int(Raycaster.Camera.Position[0])/Raycaster.TileSize * .. Raycaster.TileSize + Raycaster.TileSize) DX = Self.X - Raycaster.Camera.Position[0] DZ = TanAngle*DX Self.Z = Raycaster.Camera.Position[1]+DZ Face = FACE_RIGHT ElseIf Self.Angle > 90.0 And Self.Angle < 270.0 Then DeltaX = -Float(Raycaster.TileSize) DeltaZ = TanAngle*DeltaX Self.X = Float(Int(Raycaster.Camera.Position[0])/Raycaster.TileSize * .. Raycaster.TileSize) DX = Self.X - Raycaster.Camera.Position[0] DZ = TanAngle*DX Self.Z = Raycaster.Camera.Position[1]+DZ Face = FACE_LEFT ElseIf Self.Angle = 90.0 Or Self.Angle = 270.0 Then Quit = True ElseIf Self.Angle = 0.0 Then DeltaX = Float(Raycaster.TileSize) DeltaZ = 0.0 Self.X = Float(Int(Raycaster.Camera.Position[0])/Raycaster.TileSize * .. Raycaster.TileSize + Raycaster.TileSize) Self.Z = Raycaster.Camera.Position[1] Face = FACE_RIGHT ElseIf Self.Angle = 180.0 DeltaX = -Float(Raycaster.TileSize) DeltaZ = 0.0 Self.X = Float(Int(Raycaster.Camera.Position[0])/Raycaster.TileSize * .. Raycaster.TileSize) Self.Z = Raycaster.Camera.Position[1] Face = FACE_LEFT EndIf Tile = 0 While Not Quit MapRow = Int(Self.Z)/Raycaster.TileSize If (Self.Angle > 270.0 And Self.Angle < 360.0) Or .. (Self.Angle => 0.0 And Self.Angle < 90.0) Then MapColumn = Int(Self.X)/Raycaster.TileSize ElseIf Self.Angle => 90.0 And Self.Angle <= 270.0 Then MapColumn = Int(Self.X)/Raycaster.TileSize - 1 EndIf If MapColumn < 0 Or MapColumn > Raycaster.MapSize[0] - 1 Or .. MapRow < 0 Or MapRow > Raycaster.MapSize[1] - 1 Then Exit Tile = Raycaster.Map[MapColumn, MapRow] If Tile > 0 Then Exit Self.X :+ DeltaX Self.Z :+ DeltaZ Wend ' Find the shortest Distance If Tile > 0 Then DX = Raycaster.Camera.Position[0] - Self.X DZ = Raycaster.Camera.Position[1] - Self.Z Distance = Sqr(DX*DX + DZ*DZ) If OldTile > 0 Then If Distance <= OldDistance Then Self.Distance = Distance Self.Tile = Tile Self.Face = Face Else Self.Distance = OldDistance Self.X = OldX Self.Z = OldZ Self.Tile = OldTile Self.Face = OldFace EndIf Else Self.Distance = Distance Self.Tile = Tile Self.Face = Face EndIf Else Self.Distance = OldDistance Self.X = OldX Self.Z = OldZ Self.Tile = OldTile Self.Face = OldFace EndIf End Method End Type Type TTexture Field Height : Int Field Texels : Int[,] Method New() Self.Height = 0 Self.Texels = Null End Method Method Delete() If Self.Texels Then MemFree(Self.Texels) End Method Method DrawColumn(X:Int, Column:Float, Height:Int, Camera:TCamera) Local DeltaY:Float, StartY:Int, Y:Int Local Texel:Int, TempY:Int, Offset:Byte Ptr DeltaY = Float(Self.Height)/Float(Height) If Height > Camera.Viewport[3] Then StartY = (Height - Camera.Viewport[3])/2 Height = Camera.Viewport[3] Else StartY = 0 EndIf TempY = Camera.Viewport[3]/2 - Height/2 - StartY For Y = StartY Until Height+StartY Row = Int(Float(Y)*DeltaY) Offset = Camera.Screen.Pixels + .. (TempY+Y)*Camera.Viewport[2]*4 + X*4 Texel = Self.Texels[Row, Column] Offset[2] = (Texel Shr 16) & $FF Offset[1] = (Texel Shr 8) & $FF Offset[0] = Texel & $FF Next End Method Function Load:TTexture(URL:Object, TileSize:Int) Local Pixmap:TPixmap, Texture:TTexture Pixmap = LoadPixmap(URL) If Not Pixmap Then Return Null Pixmap = ResizePixmap(Pixmap, TileSize, Pixmap.Height) ?Win32 Pixmap = Pixmap.Convert(PF_RGBA8888) ?MacOS Pixmap = Pixmap.Convert(PF_BGRA8888) ?Linux Pixmap = Pixmap.Convert(PF_RGBA8888) ? Texture = New TTexture Texture.Height = Pixmap.Height Texture.Texels = New Int[Texture.Height, TileSize] MemCopy(Texture.Texels, Pixmap.PixelPtr(0, 0), TileSize*Texture.Height*4) Return Texture End Function End Type
Example:
SuperStrict Framework BRL.Max2D Import BRL.GLMax2D Import BRL.StandardIO Import BRL.PolledInput Import BRL.PNGLoader Include "Raycaster.bmx" Global Raycaster : TRaycaster Global Camera : TCamera Global MapWidth : Int Global MapDepth : Int Global Column : Int Global Row : Int Global Tile : Int Global FrameCount : Int Global Start : Int Global FPS : Int Graphics(320, 240, 16, 0) ' Init Raycaster Raycaster = New TRaycaster ' Load Tileset Raycaster.LoadTile("Tile1.png") Raycaster.LoadTile("Tile2.png") Raycaster.LoadTile("Tile3.png") Raycaster.LoadTile("Tile4.png") Raycaster.LoadTile("Tile5.png") Raycaster.LoadTile("Tile6.png") Raycaster.LoadTile("Tile7.png") Raycaster.LoadTile("Tile8.png") ' Read Map RestoreData Level ReadData MapWidth, MapDepth Raycaster.SetMapSize(MapWidth, MapDepth) For Row = 0 Until MapDepth For Column = 0 Until MapWidth ReadData Tile Raycaster.SetTile(Column, Row, Tile) Next Next ' Add Camera Camera = New TCamera Camera.SetClearColor(30, 10, 0) Camera.SetPosition(672.0, 672.0) Raycaster.SetCamera(Camera) While Not KeyDown(KEY_ESCAPE) If KeyDown(KEY_LEFT) Then Camera.Turn(-1.0) If KeyDown(KEY_RIGHT) Then Camera.Turn( 1.0) If KeyDown(KEY_UP) Then Camera.Move( 1.0) If KeyDown(KEY_DOWN) Then Camera.Move(-1.0) Raycaster.Render() SetColor(255, 255, 255) DrawText(FPS+" FPS", 10, 10) Flip() FrameCount :+ 1 If MilliSecs() - Start > 999 Then FPS = FrameCount FrameCount = 0 Start = MilliSecs() EndIf Wend EndGraphics() End #Level DefData 20, 20 DefData 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 DefData 1, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 3, 3, 6, 6, 6, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 3, 3, 6, 6, 6, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 6, 6, 6, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 2, 2, 0, 3, 3, 6, 6, 6, 6, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1 DefData 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 1 DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 DefData 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 1
Download the complete example with *.exe file:
http://vertex.art-fx.org/raycaster/raycaster.zip
cu olli