/** General definitions for Pokitto raycasting demos. author: Miloslav "drummyfish" Ciz license: CC0 1.0 */ #ifndef RAYCAST_DEMO_GENERAL_HPP #define RAYCAST_DEMO_GENERAL_HPP #include "stdio.h" // for debugging raycastlibg #define VERTICAL_FOV UNITS_PER_SQUARE // redefine camera vertical FOV #include "raycastlib.h" #include "Pokitto.h" Pokitto::Core pokitto; #ifndef FPS #define FPS 30 #endif #ifndef PLAYER_SPEED #define PLAYER_SPEED (2 * UNITS_PER_SQUARE) #endif #ifndef PLAYER_ROTATION_SPEED #define PLAYER_ROTATION_SPEED (UNITS_PER_SQUARE / 2) #endif #ifndef GRAVITY_ACCELERATION #define GRAVITY_ACCELERATION ((3 * UNITS_PER_SQUARE) / 2) #endif #define SCREEN_WIDTH 110 #define SCREEN_HEIGHT 88 #define MIDDLE_ROW (SCREEN_HEIGHT / 2) #define MIDDLE_COLUMN (SCREEN_WIDTH / 2) #ifndef SUBSAMPLE #define SUBSAMPLE 2 #endif #define SUBSAMPLED_WIDTH (SCREEN_WIDTH / SUBSAMPLE) #define TRANSPARENT_COLOR 0b00000111 Unit zBuffer[SUBSAMPLED_WIDTH]; ///< 1D z-buffer for visibility determination. unsigned short palette[256]; /** Gets (the index of) color by specified RGB components. @param r red, 3 bits (0 to 7) @param g green, 3 bits (0 to 7) @param b blue, 2 bits (0 to 3) @return palette index of the color */ inline uint8_t rgbToIndex(uint8_t r, uint8_t g, uint8_t b) { return (r & 0b00000111) | ((g & 0b00000111) << 3) | ((b & 0b00000011) << 6); } /** Inits and loads a general 256 color palette. */ void initPalette() { for (uint8_t r = 0; r < 8; ++r) for (uint8_t g = 0; g < 8; ++g) for (uint8_t b = 0; b < 4; ++b) palette[rgbToIndex(r,g,b)] = pokitto.display.RGBto565(36 * r, 36 * g, 85 * b); pokitto.display.load565Palette(palette); } /** Adds given intensity to a color. @param color input color @param intensity intensity to add, 3 bit (0 to 7) @return new color */ inline uint8_t addIntensity(uint8_t color, int intensity) { uint8_t r = color & 0b00000111; uint8_t g = (color & 0b00111000) >> 3; uint8_t b = (color & 0b11000000) >> 6; if (intensity >= 0) { r += intensity; r = r > 7 ? 7 : r; g += intensity; g = g > 7 ? 7 : g; b += intensity / 2; b = b > 3 ? 3 : b; } else { intensity *= -1; r = (intensity > r) ? 0 : r - intensity; g = (intensity > g) ? 0 : g - intensity; intensity /= 2; b = intensity > b ? 0 : b - intensity; } return rgbToIndex(r,g,b); } /** Samples an image by normalized coordinates - each coordinate is in range 0 to UNITS_PER_SQUARE (from raycastlib). */ inline uint8_t sampleImage(const unsigned char *image, Unit x, Unit y) { // TODO: optimize x = wrap(x,UNITS_PER_SQUARE); y = wrap(y,UNITS_PER_SQUARE); int32_t index = image[1] * ((image[0] * x) / UNITS_PER_SQUARE) + (image[0] * y) / UNITS_PER_SQUARE; return image[2 + index]; } void inline drawSprite(const unsigned char *sprite, int16_t x, int16_t y, Unit depth, int16_t size) { // TODO: optimize x -= size / 2; y -= size / 2; Unit step = UNITS_PER_SQUARE / size; uint8_t c; int16_t jTo = size - max(0,y + size - 88); int16_t iTo = size - max(0,x + size - 110); for (Unit i = max(-1 * x,0); i < iTo; ++i) { int16_t xPos = x + i; if (zBuffer[xPos / SUBSAMPLE] <= depth) continue; for (Unit j = max(-1 * y,0); j < jTo; ++j) { c = sampleImage(sprite,(i * UNITS_PER_SQUARE) / size,(j * UNITS_PER_SQUARE) / size); if (c != TRANSPARENT_COLOR) pokitto.display.drawPixel(xPos,y + j,c); } } } void drawImage(const unsigned char *image, int16_t x, int16_t y) { // TODO: optimize for (int16_t i = 0; i < image[0]; ++i) { int16_t xPos = x + i; int16_t column = 2 + i * image[1]; for (int16_t j = 0; j < image[1]; ++j) { char c = image[column + j]; if (c != TRANSPARENT_COLOR) pokitto.display.drawPixel(xPos,y + j,image[column + j]); } } } class Player { public: Camera mCamera; Unit mVericalSpeed; bool mRunning; Player() { mCamera.position.x = 0; mCamera.position.y = 0; mCamera.direction = 0; mCamera.height = UNITS_PER_SQUARE * 3; mCamera.resolution.x = SCREEN_WIDTH / SUBSAMPLE; mCamera.resolution.y = SCREEN_HEIGHT; mCamera.shear = 0; mVericalSpeed = 0; mRunning = false; } void setPosition(Unit x, Unit y) { mCamera.position.x = x; mCamera.position.y = y; } void setPositionSquare(int16_t squareX, int16_t squareY) { setPosition( squareX * UNITS_PER_SQUARE + UNITS_PER_SQUARE / 2, squareY * UNITS_PER_SQUARE + UNITS_PER_SQUARE / 2); } }; class Sprite { public: Sprite(const unsigned char *image, int16_t squareX, int16_t squareY, Unit z, Unit pixelSize): mImage(image), mPixelSize(pixelSize) { mPosition.x = squareX * UNITS_PER_SQUARE + UNITS_PER_SQUARE / 2; mPosition.y = squareY * UNITS_PER_SQUARE + UNITS_PER_SQUARE / 2; mHeight = z * UNITS_PER_SQUARE + UNITS_PER_SQUARE / 2; } Sprite(): mImage(0), mHeight(0), mPixelSize(1) { mPosition.x = 0; mPosition.y = 0; } const unsigned char *mImage; Vector2D mPosition; Unit mHeight; Unit mPixelSize; }; void initGeneral() { pokitto.begin(); pokitto.setFrameRate(FPS); pokitto.display.setFont(fontTiny); pokitto.display.persistence = 1; initPalette(); for (uint8_t i = 0; i < SUBSAMPLED_WIDTH; ++i) zBuffer[i] = 0; } unsigned char computeAverageColor(const unsigned char *texture) { uint32_t sumR = 0; uint32_t sumG = 0; uint32_t sumB = 0; uint32_t pixels = texture[0] * texture[1]; for (uint16_t i = 0; i < pixels; ++i) { sumR += texture[2 + i] & 0b00000111; sumG += (texture[2 + i] & 0b00111000) >> 3; sumB += (texture[2 + i] & 0b11000000) >> 6; } return rgbToIndex(sumR / pixels,sumG / pixels,sumB / pixels); } #endif