Example #1
0
 public Renderer(int screenWidth, int screenHeight)
 {
     ScreenWidth  = screenWidth;
     ScreenHeight = screenHeight;
     WallSlices   = new WallSlice[screenWidth];
     WallSlicesY  = new WallSlice[screenWidth];
 }
Example #2
0
        private void DrawDirectionalSprite(SpriteBatch spriteBatch, Camera camera, Map map, GameObject sprite)
        {
            //translate sprite position to relative to camera
            double spriteX    = sprite.Position.X - camera.Position.X;
            double spriteY    = sprite.Position.Y - camera.Position.Y;
            int    spriteMapX = (int)sprite.Position.X;
            int    spriteMapY = (int)sprite.Position.Y;

            Texture2D tex       = sprite.Texture;
            int       texWidth  = tex.Width;
            int       texHeight = tex.Height;

            double objectOffset = 0;

            if (sprite.Orientation == Orientation.None)
            {
                string errorMessage = "";
                errorMessage = String.Format("The orientation of the Sprite in the location [{0}],[{1}] has not been defined!", sprite.Position.X.ToString(), sprite.Position.Y.ToString());
                throw new Exception(errorMessage);
            }
            Orientation spriteOrientation = sprite.Orientation;


            //Find the sprite offset, that is how deep is the sprite inside the block depending on the orientation X or Y
            if (spriteOrientation == Orientation.Horizontal)
            {
                if (camera.Plane.X > 0)
                {
                    objectOffset = (sprite.Position.Y - (int)sprite.Position.Y);
                }
                else
                {
                    objectOffset = 1 - (sprite.Position.Y - (int)sprite.Position.Y);
                }
            }
            else
            {
                if (camera.Plane.Y < 0)
                {
                    objectOffset = (sprite.Position.X - (int)sprite.Position.X);
                }
                else
                {
                    objectOffset = 1 - (sprite.Position.X - (int)sprite.Position.X);
                }
            }


            double transformY      = 0;
            double transformStartY = 0;
            double transformEndY   = 0;
            int    drawStartX      = 0;
            int    drawEndX        = 0;

            int drawCenterX = ComputeScreenX(camera, spriteX, spriteY, out transformY);

            // add or substract 0.5 because the position of the sprite is the middle
            if (spriteOrientation == Orientation.Horizontal)
            {
                drawStartX = ComputeScreenX(camera, spriteX - 0.5, spriteY, out transformStartY);
                drawEndX   = ComputeScreenX(camera, spriteX + 0.5, spriteY, out transformEndY);
            }
            else
            {
                drawStartX = ComputeScreenX(camera, spriteX, spriteY - 0.5, out transformStartY);
                drawEndX   = ComputeScreenX(camera, spriteX, spriteY + 0.5, out transformEndY);
            }

            //validate sprite's start and end
            if (drawStartX < 0)
            {
                drawStartX = 0;
            }
            if (drawStartX > ScreenWidth)
            {
                drawStartX = ScreenWidth;
            }

            if (drawEndX < 0)
            {
                drawEndX = 0;
            }
            if (drawEndX > ScreenWidth)
            {
                drawEndX = ScreenWidth;
            }

            //switch variables depending on the orientation of the camera
            if (drawStartX > drawEndX)
            {
                int tempX = drawStartX;
                drawStartX = drawEndX;
                drawEndX   = tempX;
            }

            //temporal slices for the sprite
            WallSlice[] spriteSlices = new WallSlice[drawEndX - drawStartX];

            //loop through every vertical stripe of the sprite on screen
            for (int x = drawStartX; x < drawEndX; x++)
            {
                //x-coordinate in camera space
                double cameraX = 2 * x / (double)ScreenWidth - 1;

                double rayPosX = camera.Position.X;
                double rayPosY = camera.Position.Y;
                double rayDirX = camera.Direction.X + camera.Plane.X * cameraX;
                double rayDirY = camera.Direction.Y + camera.Plane.Y * 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 = 0;
                double sideDistY = 0;

                //length of ray from one x or y-side to next x or y-side
                double deltaDistX   = Math.Sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
                double deltaDistY   = Math.Sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
                double perpWallDist = 0;

                //what direction to step in x or y-direction (either +1 or -1)
                int stepX = 0;
                int stepY = 0;

                int side = 0; //was a NS or a EW wall hit?

                if (spriteOrientation == Orientation.Horizontal)
                {
                    side = 1;
                }
                else
                {
                    side = 0;
                }

                //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;
                }

                mapX = spriteMapX;
                mapY = spriteMapY;

                //Calculate distance projected on camera direction (oblique distance will give fisheye effect!)
                if (side == 0)
                {
                    perpWallDist = Math.Abs((mapX - rayPosX + (1 - stepX) / 2.0) / rayDirX) + Math.Abs(objectOffset / rayDirX);
                }
                else
                {
                    perpWallDist = Math.Abs((mapY - rayPosY + (1 - stepY) / 2.0) / rayDirY) + Math.Abs(objectOffset / rayDirY);
                }

                //Calculate height of line to draw on screen
                int lineHeight = (int)Math.Abs(ScreenHeight / perpWallDist);

                //calculate lowest and highest pixel to fill in current stripe
                int drawStart = -lineHeight / 2 + ScreenHeight / 2;
                int drawEnd   = lineHeight / 2 + ScreenHeight / 2;

                //calculate value of wallX
                //where exactly the wall was hit
                double wallX;
                if (side == 0)
                {
                    //vertical
                    wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2.0) / rayDirX + Math.Abs(objectOffset / rayDirX)) * rayDirY;
                }
                else
                {
                    //horizontal
                    wallX = rayPosX + (((mapY - rayPosY + (1 - stepY) / 2.0) / rayDirY) + Math.Abs(objectOffset / rayDirY)) * rayDirX;
                }

                wallX -= Math.Floor(wallX);

                double spriteCenter = 0;

                //Find the sprite center relative to the block containg it
                if (spriteOrientation == Orientation.Horizontal)
                {
                    spriteCenter = (sprite.Position.X - (int)sprite.Position.X);
                }
                else
                {
                    spriteCenter = (sprite.Position.Y - (int)sprite.Position.Y);
                }

                //Map correctly the texture depending on center of the sprite.
                //By default the center is an offset of 0.5 in X or Y axis
                if (spriteCenter > 0.5)
                {
                    wallX = wallX - (spriteCenter - 0.5);
                }
                else if (spriteCenter < 0.5)
                {
                    wallX = wallX - (spriteCenter + 0.5);
                }

                if (wallX < 0)
                {
                    wallX = 1 + wallX;
                }

                //x coordinate on the texture
                int texX = (int)(wallX * (double)texWidth);

                spriteSlices[x - drawStartX].Depth    = perpWallDist;
                spriteSlices[x - drawStartX].Height   = lineHeight;
                spriteSlices[x - drawStartX].TextureX = texX;
                spriteSlices[x - drawStartX].Texture  = tex;
            }

            //Draw the stripes
            for (int stripe = drawStartX; stripe < drawEndX; stripe++)
            {
                int    drawStart   = -spriteSlices[stripe - drawStartX].Height / 2 + ScreenHeight / 2;
                int    drawEnd     = spriteSlices[stripe - drawStartX].Height / 2 + ScreenHeight / 2;
                double spriteDepth = spriteSlices[stripe - drawStartX].Depth;
                //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 (spriteDepth > objectOffset && stripe > 0 && stripe < ScreenWidth && spriteDepth < WallSlices[stripe].Depth && transformY > objectOffset)
                {
                    spriteBatch.Draw(
                        tex,
                        new Rectangle(stripe, drawStart, 1, drawEnd - drawStart),
                        new Rectangle(spriteSlices[stripe - drawStartX].TextureX, 0, 1, spriteSlices[stripe - drawStartX].Texture.Height),
                        Color.White);
                }
            }
        }