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

Dependencies:   LCD_ST7735 mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Raycaster.cpp Source File

Raycaster.cpp

00001 #include "mbed.h"
00002 #include "common.h"
00003 #include "LCD_ST7735.h"
00004 #include "Raycaster.h"
00005 
00006 Raycaster::Raycaster(int left, int top, int width, int height, 
00007                      int viewerDistance, int viewerHeight, 
00008                      const uint8_t *pMap, int mapWidth, int mapHeight,
00009                      const uint16_t *pPalette) :
00010     _display(P0_15, // backlight
00011              P0_10, // reset
00012              P0_18, // ds
00013              P0_21, // mosi
00014              P0_22, // miso
00015              P1_15, // clk
00016              P0_19),// cs
00017     _pMap(pMap),
00018     _mapWidth(mapWidth),
00019     _mapHeight(mapHeight),
00020     _pPalette(pPalette),
00021     _left(left),    
00022     _width(width),
00023     _top(top),
00024     _height(height)    
00025 {
00026     _display.setOrientation(LCD_ST7735::Rotate90, false);
00027     _display.clearScreen();
00028     
00029     _right = left + width;
00030     _halfWidth = width >> 1;
00031     _horizCenter = left + _halfWidth;
00032     
00033     _bottom = top + height;
00034     _halfHeight = height >> 1;
00035     _vertCenter = top + _halfHeight;
00036     
00037     _viewerDistance = viewerDistance;
00038     _viewerHeight = viewerHeight;
00039   
00040     _viewVolume = 60 * PI / 180;
00041     _halfViewVolume = _viewVolume / 2;
00042     
00043     _ainc = _viewVolume / _width;
00044     
00045     _viewDistanceTimesHeight = _viewerDistance * _viewerHeight;    
00046     _heightRatio = (_viewerDistance << CELL_SIZE_SHIFT);
00047     _pSlivers = new Sliver[_width >> 1];
00048 }
00049 
00050 Raycaster::~Raycaster()
00051 {
00052     if (_pSlivers != NULL)
00053     {
00054         delete[] _pSlivers;
00055         _pSlivers = NULL;
00056     }
00057 }
00058 
00059 void Raycaster::setCellPosition(int x, int y)
00060 {
00061     _playerX = x << CELL_SIZE_SHIFT;
00062     _playerY = y << CELL_SIZE_SHIFT;
00063     _playerViewAngle = 0;
00064 }
00065 
00066 void Raycaster::rotate(float radians)
00067 {
00068     _playerViewAngle += radians;
00069     if (_playerViewAngle > PI2) _playerViewAngle -= PI2;
00070     if (_playerViewAngle < 0) _playerViewAngle += PI2;
00071 }
00072 
00073 void Raycaster::move(int distance)
00074 {
00075     int mx;
00076     int my;
00077     
00078     // Calculate the change in x and y coordinates
00079     float dx = sin(_playerViewAngle) * distance;
00080     float dy = cos(_playerViewAngle) * -distance;
00081     
00082     // Check for collisions with walls
00083     float nx = _playerX + (dx * 4);
00084     float ny = _playerY + (dy * 4);
00085     
00086     // Check for wall in the x direction and move if open
00087     mx = ((int)nx) >> CELL_SIZE_SHIFT;
00088     my = ((int)_playerY) >> CELL_SIZE_SHIFT;
00089     if (_pMap[mx + (my  * _mapWidth)] == 0)
00090     {
00091         _playerX += dx;
00092     }
00093     
00094     // Check for wall in the y direction and move if open      
00095     mx = ((int)_playerX) >> CELL_SIZE_SHIFT;
00096     my = ((int)ny) >> CELL_SIZE_SHIFT;
00097     if (_pMap[mx + (my * _mapWidth)] == 0)
00098     {
00099         _playerY += dy;
00100     }
00101 }
00102 
00103 void Raycaster::renderFrame()
00104 {
00105     int xd, yd;
00106     int grid_x, grid_y;
00107     float xcross_x, xcross_y;
00108     float ycross_x, ycross_y;
00109     int xmaze, ymaze;
00110     float distance;
00111     int tmcolumn;
00112     uint8_t cellValue;
00113     
00114     int xview = FLOAT2INT(_playerX);
00115     int yview = FLOAT2INT(_playerY);
00116     
00117     float columnAngle = _halfViewVolume;    
00118     float ainc2 = _ainc * 2;
00119     Sliver *pSliver = _pSlivers;
00120     for (int column = 0; column < _width; column += 2, columnAngle -= ainc2, pSliver++)
00121     {
00122         float radians = _playerViewAngle - columnAngle;
00123         
00124         int xdiff = FLOAT2INT(CELL_SIZE * sin(radians));
00125         int ydiff = FLOAT2INT(-CELL_SIZE * cos(radians));
00126     
00127         if (xdiff == 0) xdiff = 1;
00128   
00129         float slope = (float)ydiff / xdiff;        
00130         if (slope == 0.0f) slope = 0.001f;
00131 
00132         int x = xview;
00133         int y = yview;       
00134     
00135         for(;;)
00136         {
00137             if (xdiff > 0)
00138             {
00139                 grid_x = ((int)x & CELL_BIT_MASK) + CELL_SIZE;
00140             }
00141             else 
00142             {
00143                 grid_x = ((int)x & CELL_BIT_MASK) - 1;
00144             }
00145       
00146             if (ydiff > 0) 
00147             {
00148                 grid_y = ((int)y & CELL_BIT_MASK) + CELL_SIZE;
00149             }
00150             else 
00151             {
00152                 grid_y = ((int)y & CELL_BIT_MASK) - 1;
00153             }
00154       
00155             xcross_x = grid_x;
00156             xcross_y = y + slope * (grid_x - x);
00157       
00158             ycross_x = x + (grid_y - y) / slope;      
00159             ycross_y = grid_y;
00160       
00161             xd = xcross_x - x;
00162             yd = xcross_y - y;
00163             int xdist = (xd * xd) + (yd * yd);
00164       
00165             xd = ycross_x - x;
00166             yd = ycross_y - y;
00167             int ydist = (xd * xd) + (yd * yd);
00168       
00169             if (xdist < ydist)
00170             {
00171                 xmaze = (int)xcross_x >> CELL_SIZE_SHIFT;
00172                 ymaze = (int)xcross_y >> CELL_SIZE_SHIFT;
00173                 
00174                 x = xcross_x;
00175                 y = xcross_y; 
00176                 
00177                 tmcolumn = (int)y & CELL_SIZE_MASK;        
00178             } 
00179             else
00180             {
00181                 xmaze = (int)ycross_x >> CELL_SIZE_SHIFT;
00182                 ymaze = (int)ycross_y >> CELL_SIZE_SHIFT;
00183                 
00184                 x = ycross_x;
00185                 y = ycross_y;
00186                 
00187                 tmcolumn = (int)x & CELL_SIZE_MASK;        
00188             }
00189 
00190             cellValue = _pMap[xmaze + (ymaze * _mapWidth)];
00191             if (cellValue != 0) break;            
00192         }
00193     
00194         xd = x - xview;
00195         yd = y - yview;
00196         distance = sqrt(((xd * xd) + (yd * yd)) * cos(columnAngle));
00197         if (distance == 0) distance = 1;
00198     
00199         int height = _heightRatio / distance;
00200         if (height == 0) height = 1;
00201         
00202         int bot = _viewDistanceTimesHeight / distance + _vertCenter;
00203         int top = bot - height;
00204         
00205         int t = tmcolumn;
00206         int dheight = height;
00207         int iheight = CELL_SIZE;
00208         float yratio = (float)CELL_SIZE / height;
00209         
00210         if (top < _top)
00211         {
00212             int clipBy = _top - top;
00213             int clipRatio = (int)(clipBy * yratio);
00214             dheight-= clipBy;
00215             t += clipRatio << CELL_SIZE_SHIFT;
00216             iheight -= clipRatio;      
00217             top = _top;
00218         }
00219 
00220         if (bot >= _bottom)
00221         {
00222             int clipBy = bot - _bottom;
00223             dheight -= clipBy;
00224             iheight -= (int)(clipBy * yratio);
00225             bot = _bottom;
00226         }
00227         
00228         uint16_t color = 0xf800;
00229         
00230         color = _pPalette[cellValue];
00231                 
00232         pSliver->top = top;
00233         pSliver->bottom = bot;
00234         pSliver->color = color;
00235     } 
00236     
00237     int x = _left;
00238     pSliver = _pSlivers;
00239     for(int column = 0; column < _width; column += 2, x += 2, pSliver++)
00240     {
00241         _display.fillRect(x, _top, x + 1, pSliver->top, *_pPalette);
00242         _display.fillRect(x, pSliver->top, x + 1, pSliver->bottom, pSliver->color);
00243         _display.fillRect(x, pSliver->bottom, x + 1, _bottom, *_pPalette);                   
00244     }    
00245 }