/// <summary> /// Use raycasting to determine the distance to the nearest wall at specified angle /// </summary> /// <param name="angle">angle in degrees</param> /// <param name="objectsGrid">the grid array in which the objects are defined</param> /// <returns>location of the first object found</returns> private WorldObject FindObject(Angle angle, int[][] objectsGrid) { // Look for the nearest intersecting wall Intersection intersection = FindIntersection(angle, objectsGrid); if (intersection == null) { return null; } WorldObject obj = new WorldObject(intersection); // Calculate TextureX property which determines which part of the texture to map to this object Point gridLocation = CoordsToGrid(obj); // Set texture for the object obj.TextureIndex = objectsGrid[gridLocation.Y][gridLocation.X]; // Determine on which X-coordinate of the object the intersection was found obj.TextureX = obj.IntersectsWith == IntersectionAxe.Xaxe ? (int)(obj.X % wallSize) : (int)(obj.Y % wallSize); return obj; }
/// <summary> /// Draw walls that are found for the current vertical scanline /// </summary> /// <param name="scanline"></param> /// <param name="drawingBuffer"></param> private void DrawObject(int scanline, WorldObject obj, Pixel[] drawingBuffer) { // Remove distortion (counter fishbowl effect) Angle distortionRemovalAngle = new Angle(fieldOfView / 2); distortionRemovalAngle.Turn(-angleBetweenRays.Value * (double)scanline); obj.Distance = obj.Distance * Math.Cos(distortionRemovalAngle.ToRadians()); // Use distance to determine the size of the wall slice int wallHeight = (int)Math.Floor(wallSize / obj.Distance * distanceToViewPort); // The visible scale of a wall compared to its original size double wallScale = (double)wallHeight / wallSize; // If a wall slice is larger than the viewport height, we only need the visible section // skipPixels indicates how many pixels from top and bottom towards the center can be skipped during rendering int skipPixels = wallHeight > viewPort.Height ? (wallHeight - viewPort.Height) / 2 : 0; int scanlineStartY = centerOfViewPort.Y - (wallHeight - skipPixels * 2) / 2; // Draw wall slice (untextured) // GLDraw.DrawLine(new Point(scanline, centerOfViewPort.Y - (wallHeight - skipPixels * 2) / 2), new Point(scanline, centerOfViewPort.Y + (wallHeight - skipPixels * 2) / 2), wall.IntersectsWith == IntersectionAxe.Xaxe ? Color.DarkGray : Color.Gray); // Determine start indexes for texture read and viewport write buffers int drawingBufferStart = obj.TextureX * (int)wallSize; int viewPortBufferIndex = scanline * viewPort.Height; // Generate a scanline with a textured wall slice for (int y = 0 + skipPixels; y < wallHeight - skipPixels; y++) { // Determine index of texture pixel while taking zoom into account int textureCurrentPixelIndex = (int)(y / wallScale); // Current scanline pixel index int spxIndex = y - skipPixels; // Current viewPortBuffer index int drawingBufferIndex = viewPortBufferIndex + spxIndex; // Create a pixel in the scanline draw buffer Color pixelColor = textures[obj.TextureIndex][drawingBufferStart + textureCurrentPixelIndex].Color; if (pixelColor.ToArgb() != Color.Black.ToArgb()) { drawingBuffer[drawingBufferIndex] = new Pixel(); drawingBuffer[drawingBufferIndex].X = scanline; drawingBuffer[drawingBufferIndex].Y = scanlineStartY + spxIndex; drawingBuffer[drawingBufferIndex].Color = pixelColor; // Pixels on dark sides of walls need to be half the color value if (obj.IntersectsWith == IntersectionAxe.Xaxe) { Color color = drawingBuffer[drawingBufferIndex].Color; drawingBuffer[drawingBufferIndex].Color = Color.FromArgb(color.R / 2, color.G / 2, color.B / 2); } // Make walls in the distance darker if (obj.Distance > 250) { double colorDivider = obj.Distance / 250; colorDivider = (colorDivider > 3) ? 3 : colorDivider; Color color = drawingBuffer[drawingBufferIndex].Color; drawingBuffer[drawingBufferIndex].Color = Color.FromArgb((int)(color.R / colorDivider), (int)(color.G / colorDivider), (int)(color.B / colorDivider)); } } } }