I am trying to make a port of wolfenstein 3d for the prizm that doesn't require overclocking the cpu. The current version of the raycast 3d engine I am using is too slow for that, and I am having trouble trying to optimize it as I am pretty new to C, and 3d graphics.
This is the engine code:
Code:
and here is the header I put together for math routines:
Code:
Most of this is just code from the examples here that I made compatible with the prizm.
This is the engine code:
Code:
#include "color.h"
#include "keyboard.hpp"
#include "keyboard_syscalls.h"
#include "display.h"
#include "display_syscalls.h"
#include "rtc.h"
#include "RTC_syscalls.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
//#include "raycastTextures.h"
//#include "half.h"
void plot(int x0, int y0, int color);
void drawLine(int x, int y1, int y2, int color);
void fillArea(int x, int y, int width, int height, int color);
void keyupdate(void);
int keydownlast(int basic_keycode);
int keydownhold(int basic_keycode);
void keymenu(void);
static const unsigned short* keyboard_register = (unsigned short*)0xA44B0000;
static unsigned short lastkey[8];
static unsigned short holdkey[8];
#define cosf(x) sinf((PIOVERTWO)+(x))
#define true 1
#define false 0
#define mapWidth 24
#define mapHeight 24
#define BORDER_COLOR COLOR_ALICEBLUE
#define CEILING_COLOR COLOR_GRAY
#define FLOOR_COLOR COLOR_LIGHTGRAY
#define w LCD_WIDTH_PX
#define h LCD_HEIGHT_PX
int worldMap[mapWidth][mapHeight]=
{
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
{1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1},
{1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
/////////////////////////////////////////////////////////////////////////////////////
int main(void)
{
enum { mode_splash, mode_menu, mode_play } mode = mode_play; // Game modes
_Bool running = true;
float posX = 21, posY = 12; //x and y start position
float dirX = -1, dirY = 0; //initial direction vector
float planeX = 0, planeY = 0.66; //the 2d raycaster version of camera plane
float time = 0; //time of current frame
float oldTime = 0; //time of previous frame
while (running)
{
keyupdate(); // Get key data
switch(mode)
{
case mode_splash: // Splash screen
break;
case mode_menu: // Menus
break;
case mode_play: // Main game
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
while(running)
{
int x = 0;
for(x = 0; x < w; x++)
{
//calculate ray position and direction
float cameraX = 2 * x / (float)w - 1; //x-coordinate in camera space
float rayPosX = posX;
float rayPosY = posY;
float rayDirX = dirX + planeX * cameraX;
float rayDirY = dirY + planeY * cameraX;
//which box of the map we're in
int mapX = (int)rayPosX;
int mapY = (int)rayPosY;
//length of ray from current position to next x or y-side
float sideDistX;
float sideDistY;
//length of ray from one x or y-side to next x or y-side
float deltaDistX = sqrtf(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
float deltaDistY = sqrtf(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
float perpWallDist;
//what direction to step in x or y-direction (either +1 or -1)
int stepX;
int stepY;
int hit = 0; //was there a wall hit?
int side; //was a NS or a EW wall hit?
//calculate step and initial sideDist
if (rayDirX < 0)
{
stepX = -1;
sideDistX = (rayPosX - mapX) * deltaDistX;
}
else
{
stepX = 1;
sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX;
}
if (rayDirY < 0)
{
stepY = -1;
sideDistY = (rayPosY - mapY) * deltaDistY;
}
else
{
stepY = 1;
sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY;
}
//perform DDA
while (hit == 0)
{
//jump to next map square, OR in x-direction, OR in y-direction
if (sideDistX < sideDistY)
{
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
}
else
{
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
//Check if ray has hit a wall
if (worldMap[mapX][mapY] > 0) hit = 1;
}
//Calculate distance projected on camera direction (oblique distance will give fisheye effect!)
if (side == 0)
perpWallDist = absf((mapX - rayPosX + (1 - stepX) / 2) / rayDirX);
else
perpWallDist = absf((mapY - rayPosY + (1 - stepY) / 2) / rayDirY);
//Calculate height of line to draw on screen
int lineHeight = absf((int)(h / perpWallDist));
//calculate lowest and highest pixel to fill in current stripe
int drawStart = -lineHeight / 2 + h / 2;
if(drawStart < 0)
{
drawStart = 0;
}
int drawEnd = lineHeight / 2 + h / 2;
if(drawEnd >= h)
{
drawEnd = h - 1;
}
//choose wall color
color_t color;
switch(worldMap[mapX][mapY])
{
case 1: color = COLOR_RED; break; //red
case 2: color = COLOR_GREEN; break; //green
case 3: color = COLOR_BLUE; break; //blue
case 4: color = COLOR_WHITE; break; //white
default: color = COLOR_YELLOW; break; //yellow
}
//give x and y sides different brightness
//if (side == 1) {color = color / 2;}
//draw the pixels of the stripe as a vertical line
drawLine(x, drawStart, drawEnd, color);
}
//timing for input and FPS counter
oldTime = time;
time = RTC_GetTicks();
float frameTime = (time - oldTime) / 1000.0; //frameTime is the time this frame has taken, in seconds
/*
print(1.0 / frameTime); //FPS counter
*/
Bdisp_PutDisp_DD();// Draw Screen
//speed modifiers
float moveSpeed = frameTime * 5.0; //the constant value is in squares/second
float rotSpeed = frameTime * 3.0; //the constant value is in radians/second
keyupdate(); // Update key data
//move forward if no wall in front of you
if (keydownlast(KEY_PRGM_UP))
{
if(worldMap[(int)(posX + dirX * moveSpeed)][(int)posY] == false) posX += dirX * moveSpeed;
if(worldMap[(int)posX][(int)(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed;
}
//move backwards if no wall behind you
if (keydownlast(KEY_PRGM_DOWN))
{
if(worldMap[(int)(posX - dirX * moveSpeed)][(int)posY] == false) posX -= dirX * moveSpeed;
if(worldMap[(int)posX][(int)(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed;
}
//rotate to the right
if (keydownlast(KEY_PRGM_RIGHT))
{
//both camera direction and camera plane must be rotated
float oldDirX = dirX;
dirX = dirX * cosf(-rotSpeed) - dirY * sinf(-rotSpeed);
dirY = oldDirX * sinf(-rotSpeed) + dirY * cosf(-rotSpeed);
float oldPlaneX = planeX;
planeX = planeX * cosf(-rotSpeed) - planeY * sinf(-rotSpeed);
planeY = oldPlaneX * sinf(-rotSpeed) + planeY * cosf(-rotSpeed);
}
//rotate to the left
if (keydownlast(KEY_PRGM_LEFT))
{
//both camera direction and camera plane must be rotated
float oldDirX = dirX;
dirX = dirX * cosf(rotSpeed) - dirY * sinf(rotSpeed);
dirY = oldDirX * sinf(rotSpeed) + dirY * cosf(rotSpeed);
float oldPlaneX = planeX;
planeX = planeX * cosf(rotSpeed) - planeY * sinf(rotSpeed);
planeY = oldPlaneX * sinf(rotSpeed) + planeY * cosf(rotSpeed);
}
if(keydownlast(KEY_PRGM_MENU))
{
running = false;
}
fillArea(0,0,LCD_WIDTH_PX,(LCD_HEIGHT_PX / 2), CEILING_COLOR); // Clear screen
fillArea(0,(LCD_HEIGHT_PX / 2),LCD_WIDTH_PX,(LCD_HEIGHT_PX / 2), FLOOR_COLOR); //
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
break;
default:
break;
}
if(keydownlast(KEY_PRGM_MENU))
{
running = false;
keymenu();
}
Bdisp_PutDisp_DD();
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////
void plot(int x0, int y0, int color) {
char* VRAM = (char*)0xA8000000;
VRAM += 2*(y0*LCD_WIDTH_PX + x0);
*(VRAM++) = (color&0x0000FF00)>>8;
*(VRAM++) = (color&0x000000FF);
return;
}
void drawLine(int x, int y1, int y2, int color) {
int i;
for(i = y1; i<y2; i++)
{
plot (x,i,color);
}
}
void fillArea(int x, int y, int width, int height, int color) {
//only use lower two bytes of color
int j,i;
char* VRAM = (char*)0xA8000000;
VRAM += 2*(LCD_WIDTH_PX*y + x);
for(j=y; j<y+height; j++) {
for(i=x; i<x+width; i++) {
*(VRAM++) = (color&0x0000FF00)>>8;
*(VRAM++) = (color&0x000000FF);
}
VRAM += 2*(LCD_WIDTH_PX-width);
}
}
and here is the header I put together for math routines:
Code:
#ifndef _MATH_H
#define _MATH_H
#include "stdlib.h"
//#define cosf(x) sinf((PIOVERTWO)+(x))
#define PIOVERTWO 1.570796327f
#define PI 3.141592654f
#define PISQUARED 9.869604401f
#define EXTRA_PRECISION
float absf(float);
float sqrtf(float);
float sinf(float x)
{
while (x > PI) x-=(2.f*PI);
while (x < -PI) x+=(2.f*PI);
const float B = 4/PI;
const float C = -4/(PISQUARED);
float y = B * x + C * x * absf(x);
#ifdef EXTRA_PRECISION
// const float Q = 0.7775;
const float P = 0.224008178776;
y = P * (y * absf(y) - y) + y; // Q * y + P * y * abs(y)
#endif
return y;
}
float tanf(float x) {
float y = sinf(x);
return y/((PIOVERTWO)-y);
}
float sqrtf( float x)
{
union
{
int i;
float x;
} u;
u.x = x;
u.i = (1<<29) + (u.i >> 1) - (1<<22);
// Two Babylonian Steps (simplified from:)
// u.x = 0.5f * (u.x + x/u.x);
// u.x = 0.5f * (u.x + x/u.x);
u.x = u.x + x/u.x;
u.x = 0.25f*u.x + x/u.x;
return u.x;
}
float absf(float x)
{
return sqrtf(x*x);
}
#endif
Most of this is just code from the examples here that I made compatible with the prizm.