2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
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;
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
#ifndef FPS
|
|
|
|
#define FPS 30
|
|
|
|
#endif
|
|
|
|
|
2018-09-08 15:53:11 +02:00
|
|
|
#ifndef PLAYER_SPEED
|
|
|
|
#define PLAYER_SPEED (2 * UNITS_PER_SQUARE)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef PLAYER_ROTATION_SPEED
|
|
|
|
#define PLAYER_ROTATION_SPEED (UNITS_PER_SQUARE / 2)
|
|
|
|
#endif
|
|
|
|
|
2018-09-10 18:23:53 +02:00
|
|
|
#ifndef PLAYER_JUMP_SPEED
|
|
|
|
#define PLAYER_JUMP_SPEED 500
|
|
|
|
#endif
|
|
|
|
|
2018-09-08 15:53:11 +02:00
|
|
|
#ifndef GRAVITY_ACCELERATION
|
|
|
|
#define GRAVITY_ACCELERATION ((3 * UNITS_PER_SQUARE) / 2)
|
|
|
|
#endif
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
#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.
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
unsigned short palette[256];
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
/**
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
Adds given intensity to a color.
|
|
|
|
|
|
|
|
@param color input color
|
|
|
|
@param intensity intensity to add, 3 bit (0 to 7)
|
|
|
|
@return new color
|
|
|
|
*/
|
2018-09-10 11:26:20 +02:00
|
|
|
inline uint8_t addIntensity(uint8_t color, int16_t intensity)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-10 11:26:20 +02:00
|
|
|
inline uint8_t addRGB(uint8_t color, int16_t red, int16_t green, int16_t blue)
|
|
|
|
{
|
|
|
|
int8_t r = color & 0b00000111;
|
|
|
|
int8_t g = (color & 0b00111000) >> 3;
|
|
|
|
int8_t b = (color & 0b11000000) >> 6;
|
|
|
|
|
|
|
|
r = clamp(r + red,0,7);
|
|
|
|
g = clamp(g + green,0,7);
|
|
|
|
b = clamp(b + blue,0,3);
|
|
|
|
|
|
|
|
return rgbToIndex(r,g,b);
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
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 =
|
2018-09-09 19:11:44 +02:00
|
|
|
image[1] * ((image[0] * x) / UNITS_PER_SQUARE) + (image[0] * y) /
|
2018-09-08 14:30:03 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-10 10:03:47 +02:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
class Player
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Camera mCamera;
|
|
|
|
Unit mVericalSpeed;
|
2018-09-09 16:39:08 +02:00
|
|
|
bool mRunning;
|
2018-09-08 14:55:14 +02:00
|
|
|
|
|
|
|
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;
|
2018-09-09 16:39:08 +02:00
|
|
|
mRunning = false;
|
2018-09-08 14:55:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
void update(int16_t moveDirection, bool strafe, int16_t turnDirection, bool jump,
|
|
|
|
int16_t shearDirection, ArrayFunction floorHeightFunction,
|
|
|
|
ArrayFunction ceilingHeightFunction, bool computeHeight, uint32_t dt)
|
|
|
|
{
|
|
|
|
Vector2D moveOffset;
|
|
|
|
|
|
|
|
moveOffset.x = 0;
|
|
|
|
moveOffset.y = 0;
|
|
|
|
|
|
|
|
if (moveDirection != 0)
|
|
|
|
{
|
|
|
|
int16_t horizontalStep = (dt * PLAYER_SPEED * (mRunning ? 2 : 1)) / 1000 *
|
|
|
|
(moveDirection > 0 ? 1 : -1);
|
|
|
|
|
|
|
|
moveOffset = angleToDirection(mCamera.direction + (strafe ? UNITS_PER_SQUARE / 4 : 0));
|
|
|
|
|
|
|
|
moveOffset.x = (moveOffset.x * horizontalStep) / UNITS_PER_SQUARE;
|
|
|
|
moveOffset.y = (moveOffset.y * horizontalStep) / UNITS_PER_SQUARE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (turnDirection != 0)
|
|
|
|
{
|
|
|
|
int16_t rotationStep = (dt * PLAYER_ROTATION_SPEED) / 1000;
|
|
|
|
mCamera.direction = wrap(mCamera.direction + turnDirection * rotationStep,UNITS_PER_SQUARE);
|
|
|
|
}
|
|
|
|
|
|
|
|
Unit prevHeight = mCamera.height;
|
|
|
|
|
|
|
|
moveCameraWithCollision(&mCamera,moveOffset,mVericalSpeed,
|
|
|
|
floorHeightFunction, ceilingHeightFunction, computeHeight ? 1 : 0);
|
|
|
|
|
|
|
|
Unit heightDiff = mCamera.height - prevHeight;
|
|
|
|
|
|
|
|
if (heightDiff == 0)
|
|
|
|
mVericalSpeed = 0; // hit floor/ceiling
|
|
|
|
|
|
|
|
if (jump && mVericalSpeed == 0)
|
|
|
|
{
|
|
|
|
int16_t camX = divRoundDown(mCamera.position.x,UNITS_PER_SQUARE);
|
|
|
|
int16_t camY = divRoundDown(mCamera.position.y,UNITS_PER_SQUARE);
|
|
|
|
|
|
|
|
if (mCamera.height - CAMERA_COLL_HEIGHT_BELOW -
|
|
|
|
floorHeightFunction(camX,camY) < 2)
|
|
|
|
mVericalSpeed = PLAYER_JUMP_SPEED; // jump
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shearDirection != 0)
|
|
|
|
mCamera.shear = clamp(mCamera.shear + shearDirection * 10,
|
|
|
|
-1 * mCamera.resolution.y, mCamera.resolution.y);
|
|
|
|
else
|
|
|
|
mCamera.shear /= 2;
|
|
|
|
|
|
|
|
if (computeHeight)
|
|
|
|
mVericalSpeed -= (dt * GRAVITY_ACCELERATION) / 1000; // gravity
|
|
|
|
}
|
2018-09-08 14:55:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2018-09-09 17:52:30 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-09-10 08:03:09 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
#endif
|