Ray casting engine implemented on the mBuino platform using the ST7735 LCD controller.

Dependencies:   LCD_ST7735 mbed

Ray casting engine written to test performance of the LCD_ST7735 library I wrote as a learning exercise on the mbed platform.

Revision:
0:303768292f44
Child:
1:fdbc2be25831
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Raycaster.cpp	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,236 @@
+#include "mbed.h"
+#include "common.h"
+#include "LCD_ST7735.h"
+#include "Raycaster.h"
+
+Raycaster::Raycaster(int left, int top, int width, int height, 
+                     int viewerDistance, int viewerHeight, 
+                     const uint8_t *pMap, int mapWidth, int mapHeight,
+                     const uint16_t *pPalette) :
+    _display(P0_15, // backlight
+             P0_10, // reset
+             P0_18, // ds
+             P0_21, // mosi
+             P0_22, // miso
+             P1_15, // clk
+             P0_19),// cs
+    _pMap(pMap),
+    _mapWidth(mapWidth),
+    _mapHeight(mapHeight),
+    _pPalette(pPalette),
+    _left(left),    
+    _width(width),
+    _top(top),
+    _height(height)    
+{
+    _display.setOrientation(LCD_ST7735::Rotate90, false);
+    _display.clearScreen();
+    
+    _right = left + width;
+    _halfWidth = width >> 1;
+    _horizCenter = left + _halfWidth;
+    
+    _bottom = top + height;
+    _halfHeight = height >> 1;
+    _vertCenter = top + _halfHeight;
+    
+    _viewerDistance = viewerDistance;
+    _viewerHeight = viewerHeight;
+  
+    _viewVolume = 60 * PI / 180;
+    _halfViewVolume = _viewVolume / 2;
+    
+    _ainc = _viewVolume / _width;
+    
+    _viewDistanceTimesHeight = _viewerDistance * _viewerHeight;    
+    _heightRatio = (_viewerDistance << CELL_SIZE_SHIFT);
+    _pSlivers = new Sliver[_width >> 1];
+}
+
+void Raycaster::setCellPosition(int x, int y)
+{
+    _playerX = x << CELL_SIZE_SHIFT;
+    _playerY = y << CELL_SIZE_SHIFT;
+    _playerViewAngle = 0;
+}
+
+void Raycaster::rotate(float radians)
+{
+    _playerViewAngle += radians;
+    if (_playerViewAngle > PI2) _playerViewAngle -= PI2;
+    if (_playerViewAngle < 0) _playerViewAngle += PI2;
+}
+
+void Raycaster::move(int distance)
+{
+    int mx;
+    int my;
+    
+    // Calculate the change in x and y coordinates
+    float dx = sin(_playerViewAngle) * distance;
+    float dy = cos(_playerViewAngle) * -distance;
+    
+    // Check for collisions with walls
+    float nx = _playerX + (dx * 4);
+    float ny = _playerY + (dy * 4);
+    
+    // Check for wall in the x direction and move if open
+    mx = ((int)nx) >> CELL_SIZE_SHIFT;
+    my = ((int)_playerY) >> CELL_SIZE_SHIFT;
+    if (_pMap[mx + (my  * _mapWidth)] == 0)
+    {
+        _playerX += dx;
+    }
+    
+    // Check for wall in the y direction and move if open      
+    mx = ((int)_playerX) >> CELL_SIZE_SHIFT;
+    my = ((int)ny) >> CELL_SIZE_SHIFT;
+    if (_pMap[mx + (my * _mapWidth)] == 0)
+    {
+        _playerY += dy;
+    }
+}
+
+void Raycaster::renderFrame()
+{
+    int xd, yd;
+    int grid_x, grid_y;
+    float xcross_x, xcross_y;
+    float ycross_x, ycross_y;
+    int xmaze, ymaze;
+    float distance;
+    int tmcolumn;
+    uint8_t cellValue;
+    
+    int xview = FLOAT2INT(_playerX);
+    int yview = FLOAT2INT(_playerY);
+    
+    float columnAngle = _halfViewVolume;    
+    float ainc2 = _ainc * 2;
+    Sliver *pSliver = _pSlivers;
+    for (int column = 0; column < _width; column += 2, columnAngle -= ainc2, pSliver++)
+    {
+        float radians = _playerViewAngle - columnAngle;
+        
+        int xdiff = FLOAT2INT(CELL_SIZE * sin(radians));
+        int ydiff = FLOAT2INT(-CELL_SIZE * cos(radians));
+    
+        if (xdiff == 0) xdiff = 1;
+  
+        float slope = (float)ydiff / xdiff;        
+        if (slope == 0.0f) slope = 0.001f;
+
+        int x = xview;
+        int y = yview;       
+    
+        for(;;)
+        {
+            if (xdiff > 0)
+            {
+                grid_x = ((int)x & CELL_BIT_MASK) + CELL_SIZE;
+            }
+            else 
+            {
+                grid_x = ((int)x & CELL_BIT_MASK) - 1;
+            }
+      
+            if (ydiff > 0) 
+            {
+                grid_y = ((int)y & CELL_BIT_MASK) + CELL_SIZE;
+            }
+            else 
+            {
+                grid_y = ((int)y & CELL_BIT_MASK) - 1;
+            }
+      
+            xcross_x = grid_x;
+            xcross_y = y + slope * (grid_x - x);
+      
+            ycross_x = x + (grid_y - y) / slope;      
+            ycross_y = grid_y;
+      
+            xd = xcross_x - x;
+            yd = xcross_y - y;
+            int xdist = (xd * xd) + (yd * yd);
+      
+            xd = ycross_x - x;
+            yd = ycross_y - y;
+            int ydist = (xd * xd) + (yd * yd);
+      
+            if (xdist < ydist)
+            {
+                xmaze = (int)xcross_x >> CELL_SIZE_SHIFT;
+                ymaze = (int)xcross_y >> CELL_SIZE_SHIFT;
+                
+                x = xcross_x;
+                y = xcross_y; 
+                
+                tmcolumn = (int)y & CELL_SIZE_MASK;        
+            } 
+            else
+            {
+                xmaze = (int)ycross_x >> CELL_SIZE_SHIFT;
+                ymaze = (int)ycross_y >> CELL_SIZE_SHIFT;
+                
+                x = ycross_x;
+                y = ycross_y;
+                
+                tmcolumn = (int)x & CELL_SIZE_MASK;        
+            }
+
+            cellValue = _pMap[xmaze + (ymaze * _mapWidth)];
+            if (cellValue != 0) break;            
+        }
+    
+        xd = x - xview;
+        yd = y - yview;
+        distance = sqrt(((xd * xd) + (yd * yd)) * cos(columnAngle));
+        if (distance == 0) distance = 1;
+    
+        int height = _heightRatio / distance;
+        if (height == 0) height = 1;
+        
+        int bot = _viewDistanceTimesHeight / distance + _vertCenter;
+        int top = bot - height;
+        
+        int t = tmcolumn;
+        int dheight = height;
+        int iheight = CELL_SIZE;
+        float yratio = (float)CELL_SIZE / height;
+        
+        if (top < _top)
+        {
+            int clipBy = _top - top;
+            int clipRatio = (int)(clipBy * yratio);
+            dheight-= clipBy;
+            t += clipRatio << CELL_SIZE_SHIFT;
+            iheight -= clipRatio;      
+            top = _top;
+        }
+
+        if (bot >= _bottom)
+        {
+            int clipBy = bot - _bottom;
+            dheight -= clipBy;
+            iheight -= (int)(clipBy * yratio);
+            bot = _bottom;
+        }
+        
+        uint16_t color = 0xf800;
+        
+        color = _pPalette[cellValue];
+                
+        pSliver->top = top;
+        pSliver->bottom = bot;
+        pSliver->color = color;
+    } 
+    
+    int x = _left;
+    pSliver = _pSlivers;
+    for(int column = 0; column < _width; column += 2, x += 2, pSliver++)
+    {
+        _display.fillRect(x, _top, x + 1, pSliver->top, *_pPalette);
+        _display.fillRect(x, pSliver->top, x + 1, pSliver->bottom, pSliver->color);
+        _display.fillRect(x, pSliver->bottom, x + 1, _bottom, *_pPalette);                   
+    }    
+}
\ No newline at end of file