Ray casting engine implemented on the mBuino platform using the ST7735 LCD controller.
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, committed 2014-09-21
- 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
--- /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