#include <os.h>
// Useful structures
typedef struct
{
int x;
int y;
} ScreenPoint;
// Functions
// Miscellaneous defines
#define abs(x) (x < 0 ? -x : x)
#define map(x, in_min, in_max, out_min, out_max) ((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
#define nRC_248mul(a,b) ((a*b) >> 8)
#define nRC_248div(a,b) ((a << 8) / b)
// Trigonometry
#define nRC_Sin(angle) nRC_Cos(angle + 64)
int nRC_Cos(int angle)
{
// Beware ! All trigonometry is done in base 256 !
int table[256]={128,128,128,128,127,127,127,126,126,125,124,123,122,122,121,119,118,117,116,114,113,111,110,108,106,105,103,101,99,97,95,93,91,248,86,84,81,79,76,74,71,68,66,63,60,58,55,52,49,46,43,40,37,34,31,28,25,22,19,16,13,9,6,3,0,-3,-6,-9,-13,-16,-19,-22,-25,-28,-31,-34,-37,-40,-43,-46,-49,-52,-55,-58,-60,-63,-66,-68,-71,-74,-76,-79,-81,-84,-86,-248,-91,-93,-95,-97,-99,-101,-103,-105,-106,-108,-110,-111,-113,-114,-116,-117,-118,-119,-121,-122,-122,-123,-124,-125,-126,-126,-127,-127,-127,-128,-128,-128,-128,-128,-128,-128,-127,-127,-127,-126,-126,-125,-124,-123,-122,-122,-121,-119,-118,-117,-116,-114,-113,-111,-110,-108,-106,-105,-103,-101,-99,-97,-95,-93,-91,-248,-86,-84,-81,-79,-76,-74,-71,-68,-66,-63,-60,-58,-55,-52,-49,-46,-43,-40,-37,-34,-31,-28,-25,-22,-19,-16,-13,-9,-6,-3,0,3,6,9,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,60,63,66,68,71,74,76,79,81,84,86,248,91,93,95,97,99,101,103,105,106,108,110,111,113,114,116,117,118,119,121,122,122,123,124,125,126,126,127,127,127,128,128,128};
return table[angle & 0xff];
}
#define nRC_Tan(angle) nRC_248div(nRC_Sin(angle), nRC_Cos(angle))
// Drawing functions
void nRC_setPixel(ScreenPoint pixel, int color, char* buffer)
{
int temp;
if(pixel.x < 319 && pixel.x > 0 &&
pixel.y < 239 && pixel.y > 0)
{
if(is_cx) // color LCD, 16 bpp
{
buffer[temp = ((pixel.y * 320 + pixel.x) * 2)] = color & 0xff;
buffer[temp + 1] = color >> 8 & 0xff;
}
else // monochrome LCD, 4 bpp
{
temp = (pixel.y * 160 + pixel.x/2);
if(pixel.x & 1)
{
buffer[temp] = (buffer[temp] & 0xf0) | (color & 0x0f);
}
else
{
buffer[temp] = (buffer[temp] & 0x0f) | (color << 4 & 0xf0);
}
}
}
}
#define nRC_drawHorizontalLine(origin, end, constant, color, buffer) nRC_drawLineShared(origin, end, constant, color, buffer, 0)
#define nRC_drawVerticalLine(origin, end, constant, color, buffer) nRC_drawLineShared(origin, end, constant, color, buffer, 1)
void nRC_drawLineShared(int origin, int end, int constant, int color, char *buffer, int side)
{
ScreenPoint pixel;
int i,j;
if(constant > 0 && constant < (side ? 319 : 239))
{
i = min(origin, end);
j = max(origin, end);
if(!side)
{
pixel.y = constant;
for(pixel.x = i; pixel.x < j; pixel.x++) nRC_setPixel(pixel, color, buffer);
}
else
{
pixel.x = constant;
for(pixel.y = i; pixel.y < j; pixel.y++) nRC_setPixel(pixel, color, buffer);
}
}
}
void nRC_drawLine(ScreenPoint pt1, ScreenPoint pt2, int color, char* buffer)
{
int dx, dy, inx, iny, e;
dx = pt2.x - pt1.x;
dy = pt2.y - pt1.y;
inx = dx > 0 ? 1 : -1;
iny = dy > 0 ? 1 : -1;
dx = abs(dx);
dy = abs(dy);
if(dx >= dy) {
dy <<= 1;
e = dy - dx;
dx <<= 1;
while (pt1.x != pt2.x) {
nRC_setPixel(pt1, color, buffer);
if(e >= 0) {
pt1.y += iny;
e-= dx;
}
e += dy; pt1.x += inx;
}
} else {
dx <<= 1;
e = dx - dy;
dy <<= 1;
while (pt1.y != pt2.y) {
nRC_setPixel(pt1, color, buffer);
if(e >= 0) {
pt1.x += inx;
e -= dy;
}
e += dx; pt1.y += iny;
}
}
nRC_setPixel(pt1, color, buffer);
}
void nRC_fillTriangle(ScreenPoint pt1, ScreenPoint pt2, ScreenPoint pt3, int color, char *buffer)
{
int dx1, dx2, x1, x2, y, i;
ScreenPoint intermediate;
// Sort points from lowest to highest Y
if(pt1.y > pt2.y)
{
intermediate = pt1;
pt1 = pt2;
pt2 = intermediate;
}
if(pt2.y > pt3.y)
{
intermediate = pt2;
pt2 = pt3;
pt3 = intermediate;
}
if(pt1.y > pt2.y)
{
intermediate = pt1;
pt1 = pt2;
pt2 = intermediate;
}
// dx1 = (x2 - x1) / (y2 - y1)
dx1 = ((pt2.x - pt1.x) << 8) / ((pt2.y != pt1.y) ? pt2.y - pt1.y : 1);
// dx2 = (x3 - x1) / (y3 - y1)
dx2 = ((pt3.x - pt1.x) << 8) / ((pt3.y != pt1.y) ? pt3.y - pt1.y : 1);
x1 = x2 = pt1.x << 8;
y = pt1.y;
// X values are multiplied by 256 to handle a sort of a decimal part to make calculations, and thus slopes,
// more accurate. It's called a fixed .8 part, since it's only 8 bits.
for(i = 0; i < 2; i++)
{
do
{
nRC_drawHorizontalLine(x1 >> 8, x2 >> 8, y, color, buffer);
x1 += dx1;
x2 += dx2;
y++;
} while(y < pt2.y);
// dx1 = (x3 - x2) / (y3 - y2)
dx1 = ((pt3.x - pt2.x) << 8) / ((pt3.y != pt2.y) ? pt3.y - pt2.y : 1);
pt2.y = pt3.y;
}
}
inline void nRC_clearBuf(char *buffer)
{
memset(buffer, 0, SCREEN_BYTES_SIZE);
}
inline void nRC_dispBuf(char *buffer)
{
memcpy(SCREEN_BASE_ADDRESS, buffer, SCREEN_BYTES_SIZE);
}
// Ray casting
void nRC_rayCasting(int *map, ScreenPoint player, ScreenPoint mapDimensions, int fov, int angle, char *buffer)
{
int rayX, rayY, dx, dy, d1, d2, currentAngle, distFromProjPlane;
int deltaAngle, midFov;
int constantTan, flag, i = 0;
unsigned char currentUsableAngle;
deltaAngle = nRC_248div(fov, 320);
midFov = fov / 2;
currentAngle = ((angle - midFov) << 8) & 0xffff;
distFromProjPlane = 64*277;
for(i = 0; i < 320; i++)
{
currentAngle += deltaAngle;
currentAngle &= 0xffff;
currentUsableAngle = currentAngle >> 8;
constantTan = nRC_Tan(currentUsableAngle);
// Calculate distance from closest horizontal edge
if(currentUsableAngle & 127)
{
rayY = (currentUsableAngle < 128 ? ((player.y >> 6) + 1) << 6 : ((player.y >> 6) << 6) - 1);
rayX = (unsigned char)(currentUsableAngle + 64) & 127 ? nRC_248div(abs(rayY - player.y), constantTan) + player.x : player.x;
dy = currentUsableAngle < 128 ? 64 : -64;
dx = (unsigned char)(currentUsableAngle + 64) & 127 ? nRC_248div(64, constantTan) : 0;
//printf("rayX = %d ; rayY = %d ; dx = %d ; dy = %d\n", rayX, rayY, dx, dy);
//printf("currentUsableAngle = %d\n", currentUsableAngle);
if (map[(rayY >> 6) * mapDimensions.x + (rayX >> 6)])
{
d1 = nRC_248div(abs(player.y - rayY), (nRC_Sin(currentUsableAngle) ? nRC_Sin(currentUsableAngle) << 1 : 1));
goto skip1;
}
flag = 0;
while(1)
{
rayX += dx;
rayY += dy;
flag = rayX > 64 * mapDimensions.x || rayY > 64 * mapDimensions.y;
if (map[(rayY >> 6) * mapDimensions.x + (rayX >> 6)] || flag) break;
}
d1 = flag ? 50000000 : nRC_248div(abs(player.y - rayY), (nRC_Sin(currentUsableAngle) ? nRC_Sin(currentUsableAngle) << 1 : 1));
}
else d1 = 50000000;
skip1:
d1 = nRC_248mul(d1, nRC_Cos(angle - currentUsableAngle));
// At this point we have the distance from the closest horizontal edge in d1
// Now calculate distance from the closest vertical edge
if((unsigned char)(currentUsableAngle + 64) & 127)
{
rayX = (unsigned char)(currentUsableAngle - 64) < 128 ? ((player.x >> 6) << 6) - 1 : ((player.x >> 6) + 1) << 6;
rayY = nRC_248mul(abs(player.x - rayX), constantTan) + player.y;
dx = (unsigned char)(currentUsableAngle - 64) < 128 ? -64 : 64;
dy = nRC_248mul(64, constantTan);
if (map[(rayY >> 6) * mapDimensions.x + (rayX >> 6)])
{
d2 = nRC_248div(abs(player.x - rayX), (nRC_Cos(currentUsableAngle) ? nRC_Cos(currentUsableAngle) << 1 : 1));
goto skip2;
}
flag = 0;
while(1)
{
rayX += dx;
rayY += dy;
flag = rayX > 64 * mapDimensions.x || rayY > 64 * mapDimensions.y;
if (map[(rayY >> 6) * mapDimensions.x + (rayX >> 6)] || flag) break;
}
d2 = flag ? 50000000 : nRC_248div(abs(player.x - rayX), (nRC_Cos(currentUsableAngle) ? nRC_Cos(currentUsableAngle) << 1 : 1));
}
else d2 = 50000000;
skip2:
d2 = nRC_248mul(d2, nRC_Cos(angle - currentUsableAngle));
// Calculate the height of the current wall slice
d1 = distFromProjPlane / min(d1, d2);
nRC_drawVerticalLine((240 - d1) >> 1, (240 + d1) >> 1, i, 0xf800, buffer);
}
}