Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <cmath>
- #include <string>
- #include <vector>
- #include <iostream>
- #include "palrender.h"
- #define screenWidth 320
- #define screenHeight 200
- #define texWidth 64
- #define texHeight 64
- #define mapWidth 24
- #define mapHeight 24
- unsigned char texture[11][4096];
- struct wallType
- {
- int texture[4];
- bool solid[4];
- };
- double posX = 22.0, posY = 11.5; //x and y start position
- double dirX = -1.0, dirY = 0.0; //initial direction vector
- double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane
- double time = 0; //time of current frame
- double oldTime = 0; //time of previous frame
- double moveSpeed = (1.0/60.0) * 3.0; //the constant value is in squares/second
- double rotSpeed = (1.0/60.0) * 2.0; //the constant value is in radians/second
- int worldMap[mapWidth][mapHeight] =
- {
- {8,8,8,8,8,8,8,8,8,8,8,4,4,6,4,4,6,4,6,4,4,4,6,4},
- {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4},
- {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,6},
- {8,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6},
- {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,4},
- {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,6,6,0,6,4,6},
- {8,8,8,8,0,8,8,8,8,8,8,4,4,4,4,4,4,6,0,0,0,0,0,6},
- {7,7,7,7,0,7,7,7,7,0,8,0,8,0,8,0,8,4,0,4,0,6,0,6},
- {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,0,0,0,0,0,6},
- {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,4},
- {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,6,0,6},
- {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,4,6,0,6,6,6},
- {7,7,7,7,0,7,7,7,7,8,8,4,0,6,8,4,8,3,3,3,0,3,3,3},
- {2,2,2,2,0,2,2,2,2,4,6,4,0,0,6,0,6,3,0,0,0,0,0,3},
- {2,2,0,0,0,0,0,2,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3},
- {2,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3},
- {1,0,0,0,0,0,0,0,1,4,4,4,4,4,6,0,6,3,3,0,0,0,3,3},
- {2,0,0,0,0,0,0,0,2,2,2,1,2,2,2,6,6,0,0,5,0,5,0,5},
- {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5},
- {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5},
- {1,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5},
- {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5},
- {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5},
- {2,2,2,2,1,2,2,2,2,2,2,1,2,2,2,5,5,5,5,5,5,5,5,5}
- };
- int lightMap[mapWidth][mapHeight] =
- {
- {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
- {0,1,1,2,3,4,5,4,3,2,0,0,6,7,7,7,7,7,7,7,7,7,7,0},
- {0,1,0,3,4,5,6,5,4,0,0,0,6,7,7,7,7,7,7,7,7,7,7,0},
- {0,2,3,4,5,6,7,6,5,4,3,2,6,7,7,7,7,7,7,7,7,7,7,0},
- {0,1,0,0,4,5,6,5,4,0,0,0,6,7,7,7,7,7,7,7,7,7,7,0},
- {0,1,1,2,3,4,5,4,1,1,0,0,5,6,6,6,6,0,0,0,6,0,0,0},
- {0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,5,4,3,0},
- {0,0,0,0,5,0,0,0,0,1,0,1,0,1,0,1,0,0,1,0,4,0,2,0},
- {0,0,4,5,6,5,2,3,0,0,1,0,6,0,4,0,0,0,1,2,3,2,1,0},
- {0,4,5,6,7,6,5,4,3,4,5,6,7,6,5,3,0,0,1,1,2,1,1,0},
- {0,4,5,6,7,6,5,4,3,4,5,6,7,6,5,3,0,0,1,0,3,0,1,0},
- {0,0,4,5,6,5,4,3,0,0,1,0,6,0,4,0,0,0,0,0,4,0,0,0},
- {0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0},
- {0,0,0,0,4,0,0,0,0,0,0,0,4,3,0,1,0,0,4,5,6,5,4,0},
- {0,0,1,2,3,2,1,0,0,0,1,2,3,2,1,1,0,0,5,6,7,6,5,0},
- {0,1,2,3,4,3,2,1,0,0,1,1,2,1,1,1,0,0,4,5,6,5,4,0},
- {0,2,3,4,5,4,3,2,0,0,0,0,0,0,0,1,0,0,0,4,5,4,0,0},
- {0,3,4,5,6,5,4,3,0,0,0,0,0,0,0,0,0,1,1,0,4,0,1,0},
- {0,0,4,6,7,6,5,0,0,0,4,5,4,0,0,1,0,1,0,2,3,2,0,0},
- {0,3,4,5,6,5,4,3,0,4,5,6,5,4,0,0,1,0,1,0,2,0,1,0},
- {0,2,3,4,5,4,3,3,4,5,6,7,6,5,4,3,2,1,1,1,1,1,1,0},
- {0,1,2,3,4,3,2,2,0,4,5,6,5,4,0,0,1,0,1,0,1,0,1,0},
- {0,0,1,2,3,2,1,0,0,0,4,5,4,0,0,1,0,1,0,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,0,0,0,0}
- };
- struct Sprite
- {
- double x;
- double y;
- int texture;
- };
- #define numSprites 19
- Sprite sprite[numSprites] =
- {
- {20.5, 11.5, 10}, //green light in front of playerstart
- //green lights in every room
- {18.5,4.5, 10},
- {10.0,4.5, 10},
- {10.0,12.5,10},
- {3.5, 6.5, 10},
- {3.5, 20.5,10},
- {3.5, 14.5,10},
- {14.5,20.5,10},
- //row of pillars in front of wall: fisheye test
- {18.5, 10.5, 9},
- {18.5, 11.5, 9},
- {18.5, 12.5, 9},
- //some barrels around the map
- {21.5, 1.5, 8},
- {15.5, 1.5, 8},
- {16.0, 1.8, 8},
- {16.2, 1.2, 8},
- {3.5, 2.5, 8},
- {9.5, 15.5, 8},
- {10.0, 15.1,8},
- {10.5, 15.8,8},
- };
- //1D Zbuffer
- double ZBuffer[screenWidth][screenHeight];
- //arrays used to sort the sprites
- int spriteOrder[numSprites];
- double spriteDistance[numSprites];
- //function used to sort the sprites
- void combSort(int* order, double* dist, int amount);
- void MakeTextures (int slot)
- {
- BITMAP * texspr = engine->GetSpriteGraphic (slot);
- unsigned char **texbuffer = engine->GetRawBitmapSurface (texspr);
- for(int x = 0; x < texWidth; x++)
- for(int y = 0; y < texHeight; y++)
- {
- for (int num=0;num<11;num++)
- {
- texture[num][texWidth * y + x] = texbuffer [y][x+texWidth*num]; //flat red texture with black cross
- }
- }
- }
- void Raycast_Render (int slot)
- {
- long w=0,h=0;
- BITMAP *screen = engine->GetSpriteGraphic (slot);
- engine->GetBitmapDimensions (screen,&w,&h,NULL);
- unsigned char** buffer = engine->GetRawBitmapSurface (screen);
- for (int x = 0;x<w;x++)
- {
- for (int y=0;y<h;y++)
- {
- ZBuffer[x][y] = 0;
- }
- }
- //start the main loop
- for(int x = 0; x < w; x++)
- {
- //calculate ray position and direction
- double cameraX = 2 * x / double(w) - 1; //x-coordinate in camera space
- 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 = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
- double deltaDistY = 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 prevmapX=0;
- int prevmapY=0;
- 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
- bool deeper = true;
- bool opposite = false;
- double wallX; //where exactly the wall was hit
- int drawStart;
- int drawEnd;
- while (hit == 0 && deeper == true)
- {
- if (opposite)
- {
- rayDirX = rayDirX * (-1);
- rayDirY = rayDirY * (-1);
- stepX = stepX * (-1);
- stepY = stepY * (-1);
- }
- else if (sideDistX < sideDistY) //jump to next map square, OR in x-direction, OR in y-direction
- {
- 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; //Set this to true so that by default, it's impossible to hang the engine.
- deeper = false; //Set this to false so that we don't go deeper than we need to.
- //Calculate distance of perpendicular ray (oblique distance will give fisheye effect!)
- if (side == 0) perpWallDist = fabs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX);
- else perpWallDist = fabs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY);
- //Calculate height of line to draw on screen
- int lineHeight = abs(int(h / perpWallDist));
- //calculate lowest and highest pixel to fill in current stripe
- drawStart = -lineHeight / 2 + h /2;
- if(drawStart < 0) drawStart = 0;
- drawEnd = lineHeight / 2 + h / 2;
- if(drawEnd >= h) drawEnd = h - 1;
- //texturing calculations
- int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used!
- //calculate value of wallX
- if (side == 1) wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX;
- else wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY;
- wallX -= floor((wallX));
- //x coordinate on the texture
- int wall_light=0;
- int texX = int(wallX * double(texWidth));
- if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1;
- if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1;
- if (rayDirX > 0 && side == 0) wall_light = 255 / (8-lightMap [(int)mapX-1][(int)mapY]);
- if (rayDirX < 0 && side == 0) wall_light = 255 / (8-lightMap [(int)mapX+1][(int)mapY]);
- if (rayDirY > 0 && side == 1) wall_light = 255 / (8-lightMap [(int)mapX][(int)mapY-1]);
- if (rayDirY < 0 && side == 1) wall_light = 255 / (8-lightMap [(int)mapX][(int)mapY+1]);
- for(int y = drawStart; y < drawEnd; y++)
- {
- if (ZBuffer[x][y] > perpWallDist || ZBuffer[x][y] == 0) //We can draw.
- {
- int d = y * 256 - h * 128 + lineHeight * 128; //256 and 128 factors to avoid floats
- int texY = ((d * texHeight) / lineHeight) / 256;
- int color = texture[texNum][texWidth * texY + texX];
- if (color > 0)
- {
- color = MixColorAlpha (color,GetColor565(0,0,10),wall_light);
- buffer[y][x] = color;
- //SET THE ZBUFFER FOR THE SPRITE CASTING
- ZBuffer[x][y] = perpWallDist; //perpendicular distance is used
- }
- else
- {
- //We found transparency, we have to draw deeper.
- deeper = true;
- hit = 0;
- }
- }
- }
- if (opposite)
- {
- opposite = false;
- rayDirX = rayDirX * (-1);
- rayDirY = rayDirY * (-1);
- stepX = stepX * (-1);
- stepY = stepY * (-1);
- }
- else if (!opposite && deeper && (prevmapX != mapX && prevmapY != mapY))
- {
- opposite = true;
- prevmapX = mapX;
- prevmapY = mapY;
- }
- //End of wall drawing functions.
- }
- //End of loop.
- }
- //FLOOR CASTING
- double floorXWall, floorYWall; //x, y position of the floor texel at the bottom of the wall
- //4 different wall directions possible
- if(side == 0 && rayDirX > 0)
- {
- floorXWall = mapX;
- floorYWall = mapY + wallX;
- }
- else if(side == 0 && rayDirX < 0)
- {
- floorXWall = mapX + 1.0;
- floorYWall = mapY + wallX;
- }
- else if(side == 1 && rayDirY > 0)
- {
- floorXWall = mapX + wallX;
- floorYWall = mapY;
- }
- else
- {
- floorXWall = mapX + wallX;
- floorYWall = mapY + 1.0;
- }
- double distWall, distPlayer, currentDist;
- distWall = perpWallDist;
- distPlayer = 0.0;
- if (drawEnd < 0) drawEnd = h; //becomes < 0 when the integer overflows
- //draw the floor from drawEnd to the bottom of the screen
- for(int y = drawEnd; y < h; y++)
- {
- currentDist = h / (2.0 * y - h); //you could make a small lookup table for this instead
- if (currentDist < ZBuffer[x][y] || ZBuffer[x][y] == 0)
- {
- double weight = (currentDist - distPlayer) / (distWall - distPlayer);
- double currentFloorX = weight * floorXWall + (1.0 - weight) * posX;
- double currentFloorY = weight * floorYWall + (1.0 - weight) * posY;
- int floorTexX, floorTexY;
- int lighting = 255 / (8-lightMap [(int)currentFloorX][(int)currentFloorY]);
- floorTexX = int(currentFloorX * texWidth) % texWidth;
- floorTexY = int(currentFloorY * texHeight) % texHeight;
- //floor
- buffer[y][x] = MixColorAlpha(texture[3][texWidth * floorTexY + floorTexX],GetColor565(0,0,10),lighting);
- //ceiling (symmetrical!)
- buffer[h-y][x] = MixColorAlpha(texture[6][texWidth * floorTexY + floorTexX],GetColor565(0,0,10),lighting);
- //SET THE ZBUFFER FOR THE SPRITE CASTING
- ZBuffer[x][y] = currentDist; //perpendicular distance is used
- ZBuffer[x][h-y] = currentDist; //perpendicular distance is used
- }
- }
- }
- //SPRITE CASTING
- //sort sprites from far to close
- for(int i = 0; i < numSprites; i++)
- {
- spriteOrder[i] = i;
- spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x) + (posY - sprite[i].y) * (posY - sprite[i].y)); //sqrt not taken, unneeded
- }
- combSort(spriteOrder, spriteDistance, numSprites);
- //after sorting the sprites, do the projection and draw them
- for(int i = 0; i < numSprites; i++)
- {
- //translate sprite position to relative to camera
- double spriteX = sprite[spriteOrder[i]].x - posX;
- double spriteY = sprite[spriteOrder[i]].y - posY;
- //transform sprite with the inverse camera matrix
- // [ planeX dirX ] -1 [ dirY -dirX ]
- // [ ] = 1/(planeX*dirY-dirX*planeY) * [ ]
- // [ planeY dirY ] [ -planeY planeX ]
- double invDet = 1.0 / (planeX * dirY - dirX * planeY); //required for correct matrix multiplication
- double transformX = invDet * (dirY * spriteX - dirX * spriteY);
- double transformY = invDet * (-planeY * spriteX + planeX * spriteY); //this is actually the depth inside the screen, that what Z is in 3D
- int spriteScreenX = int((w / 2) * (1 + transformX / transformY));
- //parameters for scaling and moving the sprites
- #define uDiv 1
- #define vDiv 1
- #define vMove 0.0
- int vMoveScreen = int(vMove / transformY);
- //calculate height of the sprite on screen
- int spriteHeight = abs(int(h / (transformY))) / vDiv; //using "transformY" instead of the real distance prevents fisheye
- //calculate lowest and highest pixel to fill in current stripe
- int drawStartY = -spriteHeight / 2 + h / 2 + vMoveScreen;
- if(drawStartY < 0) drawStartY = 0;
- int drawEndY = spriteHeight / 2 + h / 2 + vMoveScreen;
- if(drawEndY >= h) drawEndY = h - 1;
- //calculate width of the sprite
- int spriteWidth = abs( int (h / (transformY))) / uDiv;
- int drawStartX = -spriteWidth / 2 + spriteScreenX;
- if(drawStartX < 0) drawStartX = 0;
- int drawEndX = spriteWidth / 2 + spriteScreenX;
- if(drawEndX >= w) drawEndX = w - 1;
- int spr_light = 255 / (8-lightMap [(int)sprite[spriteOrder[i]].x][(int)sprite[spriteOrder[i]].y]);
- //loop through every vertical stripe of the sprite on screen
- for(int stripe = drawStartX; stripe < drawEndX; stripe++)
- {
- int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth / spriteWidth) / 256;
- //the conditions in the if are:
- //1) it's in front of camera plane so you don't see things behind you
- //2) it's on the screen (left)
- //3) it's on the screen (right)
- //4) ZBuffer, with perpendicular distance
- if(transformY > 0 && stripe > 0 && stripe < w)
- for(int y = drawStartY; y < drawEndY; y++) //for every pixel of the current stripe
- {
- if (transformY < ZBuffer[stripe][y])
- {
- int d = (y-vMoveScreen) * 256 - h * 128 + spriteHeight * 128; //256 and 128 factors to avoid floats
- int texY = ((d * texHeight) / spriteHeight) / 256;
- unsigned char color = texture[sprite[spriteOrder[i]].texture][texWidth * texY + texX]; //get current color from the texture
- if (color !=0)
- {
- color = MixColorAlpha (color,GetColor565(0,0,10),spr_light);
- buffer[y][stripe] = color; //paint pixel if it isn't black, black is the invisible color
- }
- }
- }
- }
- }
- engine->ReleaseBitmapSurface (screen);
- engine->NotifySpriteUpdated (slot);
- }
- void MoveForward ()
- {
- if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed;
- if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed;
- }
- void MoveBackward ()
- {
- if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed;
- if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed;
- }
- void RotateLeft ()
- {
- double oldDirX = dirX;
- dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed);
- dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed);
- double oldPlaneX = planeX;
- planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed);
- planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed);
- }
- void RotateRight ()
- {
- double oldDirX = dirX;
- dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed);
- dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed);
- double oldPlaneX = planeX;
- planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed);
- planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed);
- }
- //sort algorithm
- void combSort(int* order, double* dist, int amount)
- {
- int gap = amount;
- bool swapped = false;
- while(gap > 1 || swapped)
- {
- //shrink factor 1.3
- gap = (gap * 10) / 13;
- if(gap == 9 || gap == 10) gap = 11;
- if (gap < 1) gap = 1;
- swapped = false;
- for (int i = 0; i < amount - gap; i++)
- {
- int j = i + gap;
- if (dist[i] < dist[j])
- {
- std::swap(dist[i], dist[j]);
- std::swap(order[i], order[j]);
- swapped = true;
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement