You will like this code. plug it in your NETMF emulator and you will have a 3D engine! The problem is that I get divide by zero exception when drawing the very center line. Can you fix this and you will win the challenge?
// Original code was found online by an artice done by a university professor over a year ago.
// I searched the web and not finding the original sources to attribute thsi code properly
// If you are teh one who wrote this code, THANK YOU. We owe you a beer!
// porting to C#/NETMF and further improvements are copyright GHI Electronics
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;
namespace engine3d
{
public class Program
{
static int w = 320;//width
static int h = 240;//height
const int mapWidth = 24;
const int mapHeight = 24;
static int[] worldMap = new int[mapWidth * mapHeight]{
2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,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,2,
1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,
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
};
static Bitmap LCD = new Bitmap(320, 240);
public static void Main()
{
double posX = 20, posY = 10; //x and y start position
double dirX = -1, dirY = 0; //initial direction vector
double planeX = 0, planeY = 0.66; //the 2d raycaster version of camera plane
Color color;
while (true)
{
LCD.Clear();
for (int x = 0; x < w; x++)
{
//calculate ray position and direction
double cameraX = 2 * x / (double)(w) - 1; //x-coordinate in camera space
if (cameraX == 0)////////// REMOVE this to see center line but then your get divide by zero exception
continue;
double rayPosX = posX;
double rayPosY = posY;
double rayDirX = dirX + planeX * cameraX;
double 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
double sideDistX;
double sideDistY;
//length of ray from one x or y-side to next x or y-side
double deltaDistX = System.Math.Sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
double deltaDistY = System.Math.Sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
double 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 = 0; //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 * mapWidth + mapY] > 0) hit = 1;
}
//Calculate distance projected on camera direction (oblique distance will give fisheye effect!)
if (side == 0)
perpWallDist = System.Math.Abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX);
else
perpWallDist = System.Math.Abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY);
//Calculate height of line to draw on screen
int lineHeight = System.Math.Abs((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
switch (worldMap[mapX * mapWidth + mapY])
{
//select the color
case 1: color = ColorUtility.ColorFromRGB(200, 0, 0); break; //red
case 2: color = ColorUtility.ColorFromRGB(0, 200, 0); break; //green
case 3: color = ColorUtility.ColorFromRGB(0, 0, 200); break; //blue
case 4: color = ColorUtility.ColorFromRGB(200, 200, 200); break; //white
default: color = ColorUtility.ColorFromRGB(200, 200, 0); break; //yellow
}
//give x and y sides different brightness
if (side == 1)
{
color = ColorUtility.ColorFromRGB(
(byte)(ColorUtility.GetRValue(color)/2),
(byte)(ColorUtility.GetGValue(color) / 2),
(byte)(ColorUtility.GetBValue(color) / 2));
}
//draw the pixels of the stripe as a vertical line
LCD.DrawLine(color, 1, x, drawStart, x, drawEnd);//wall
LCD.DrawLine(ColorUtility.ColorFromRGB(200, 200, 40), 1, x, drawEnd, x, h-1);//floor
//LCD.Flush();//add this to see line by line!
}
//move forward till hit a wall
// =========NOTE we will crash at the end but this is okay for now
posX+= -0.1;
//flush
LCD.Flush();
}
}
}
}