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.

Files at this revision

API Documentation at this revision

Comitter:
taylorza
Date:
Sun Sep 21 22:04:22 2014 +0000
Child:
1:fdbc2be25831
Commit message:
LCD_ST7735 Raycaster on mBuino mbed platform

Changed in this revision

LCD_ST7735.lib Show annotated file Show diff for this revision Revisions of this file
Raycaster.cpp Show annotated file Show diff for this revision Revisions of this file
Raycaster.h Show annotated file Show diff for this revision Revisions of this file
common.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LCD_ST7735.lib	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/taylorza/code/LCD_ST7735/#88d22437119c
--- /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Raycaster.h	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,59 @@
+#ifndef __RATYCASTER_H__
+#define __RATYCASTER_H__
+class Raycaster
+{
+    public:
+        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);
+            
+        void renderFrame();
+        
+        void setCellPosition(int x, int y);
+        void rotate(float radians);
+        void move(int distance);
+
+    private:
+        struct Sliver
+        {
+            uint8_t top;
+            uint8_t bottom;
+            uint16_t color;            
+        };                
+    private:
+        LCD_ST7735 _display;
+        
+        const uint8_t *_pMap;
+        int _mapWidth;
+        int _mapHeight;
+        const uint16_t *_pPalette;
+        int _viewDistanceTimesHeight;        
+        Sliver *_pSlivers;
+        
+        int _left;
+        int _right;
+        int _width;
+        int _halfWidth;
+        int _horizCenter;
+        
+        int _top;
+        int _bottom;
+        int _height;
+        int _halfHeight;
+        int _vertCenter;
+        
+        int _viewerDistance;
+        int _viewerHeight;
+        float _viewerDistanceHeightRatio;
+        float _viewVolume;
+        float _halfViewVolume;
+                
+        float _ainc;
+        int _heightRatio;
+        
+        float _playerX;
+        float _playerY;
+        float _playerViewAngle;
+};
+#endif //__RATYCASTER_H__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.h	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,17 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#ifndef PI
+#define PI                  3.14159265358979323846f
+#endif
+
+#define PI2                 6.28318530717958647692f
+
+#define CELL_SIZE           32
+#define CELL_SIZE_SHIFT     5
+#define CELL_SIZE_MASK      0x1f
+#define CELL_BIT_MASK       0xffe0
+
+#define FLOAT2INT(x)        ((int)((x) + 0.5))
+
+#endif //__COMMON_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,59 @@
+#include "mbed.h"
+#include "common.h"
+#include "LCD_ST7735.h"
+#include "Color565.h"
+#include "Raycaster.h"
+
+static const uint8_t g_map[] = 
+{
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+    1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,4,
+    1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,4,
+    1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,4,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,
+    1,1,3,4,3,3,3,0,0,2,2,2,2,2,2,4,
+    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,
+    1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,4,
+    1,1,1,1,0,0,1,0,0,1,0,0,3,3,3,4,
+    3,0,0,0,0,0,1,0,0,1,0,0,0,0,0,4,
+    3,0,0,1,1,1,0,0,0,0,1,1,1,0,0,4,
+    3,0,0,1,0,0,0,0,0,0,0,0,1,0,0,4,
+    3,0,0,1,0,0,2,4,4,2,0,0,1,0,0,4,
+    3,0,0,1,0,0,0,0,0,0,0,0,1,0,0,4, 
+    3,0,0,0,0,1,0,0,0,0,1,0,0,0,0,4,
+    3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,
+};
+
+static const uint16_t g_palette[] =
+{
+    Color565::Black,
+    Color565::Red,
+    Color565::Green,
+    Color565::Blue,
+    Color565::Yellow
+};
+    
+AnalogIn stickX(P0_11);
+AnalogIn stickY(P0_13);
+
+main()
+{
+    Raycaster rayCaster(0, 10, 160, 108, 
+                        96, CELL_SIZE / 2, 
+                        g_map, 16, 16, 
+                        g_palette);
+    
+    rayCaster.setCellPosition(8, 8);
+    
+    while (true) 
+    {
+        rayCaster.renderFrame();
+        
+        if (stickX < 0.3) rayCaster.rotate(-0.1);
+        else if (stickX > 0.7) rayCaster.rotate(0.1);;
+        
+        if (stickY < 0.3) rayCaster.move(4);
+        else if (stickY > 0.7) rayCaster.move(-4);
+    }    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sun Sep 21 22:04:22 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/552587b429a1
\ No newline at end of file