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.

Raycaster.cpp

Committer:
taylorza
Date:
2014-10-25
Revision:
3:e32f5c25a352
Parent:
1:fdbc2be25831

File content as of revision 3:e32f5c25a352:

#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];
}

Raycaster::~Raycaster()
{
    if (_pSlivers != NULL)
    {
        delete[] _pSlivers;
        _pSlivers = NULL;
    }
}

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);                   
    }    
}