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.
Raycaster.cpp
- Committer:
- taylorza
- Date:
- 2014-09-21
- Revision:
- 0:303768292f44
- Child:
- 1:fdbc2be25831
File content as of revision 0:303768292f44:
#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); } }