public void render(object e) { //screen.floodFill(0, 0, 0x0); var screenData = screen.@lock(); int y; DoMovement(); foreach (var s in Sprites) { s.ScanComplete = false; s.ScanValid = false; } double rayDirXLeft = dirX - planeX; double rayDirYLeft = dirY - planeY; var rayDirLeft = new PointDouble { X = rayDirXLeft, Y = rayDirYLeft }.GetRotation(); double rayDirXRight = dirX + planeX; double rayDirYRight = dirY + planeY; var rayDirRight = new PointDouble { X = rayDirXRight, Y = rayDirYRight }.GetRotation(); var DelayDrawSprites = new List<SpriteDrawRequest>(); var x = 0; while (x < w) { //calculate ray position and direction double cameraX = 2.0 * (double)x / (double)w - 1.0; //x-coordinate in camera space double rayPosX = posX; double rayPosY = posY; double rayDirX = dirX + planeX * cameraX; double rayDirY = dirY + planeY * cameraX; var rayDir = new PointDouble { X = rayDirX, Y = rayDirY }.GetRotation(); //which box of the map we're in var mapX = (rayPosX).Floor(); var mapY = (rayPosY).Floor(); //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 var deltaDistX = Math.Sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX)); var deltaDistY = Math.Sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY)); //what direction to step in x or y-direction (either +1 or -1) int stepX; int stepY; //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; } double hit = 0; //was there a wall hit? var side = default(int); //was a NS or a EW wall hit? while (hit == 0) { //perform DDA //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; } if (worldMap[mapX, mapY] > 0) { hit = 1; //Check if ray has hit a wall } } //Calculate distance projected on camera direction (oblique distance will give fisheye effect!) double perpWallDist; if (side == 0) { perpWallDist = Math.Abs((mapX - rayPosX + (1 - stepX) / 2.0) / rayDirX); } else { perpWallDist = Math.Abs((mapY - rayPosY + (1 - stepY) / 2.0) / rayDirY); } if (perpWallDist == 0) { x++; continue; } //Calculate height of line to draw on screen var lineHeight = Math.Abs((h / perpWallDist).Floor()); //calculate lowest and highest pixel to fill in current stripe var drawStart = (-lineHeight / 2 + h / 2).Floor().Max(0); var drawEnd = (lineHeight / 2 + h / 2).Floor(); if (drawEnd >= h) drawEnd = h; var texNum = worldMap[mapX, mapY] - 1; //1 subtracted from it so that texture 0 can be used! //if (texNum != 3) texNum = 3; //texNum = 0; //calculate value of wallX double wallX; //where exactly the wall was hit if (side == 1) { wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX; } else { wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY; } wallX -= Math.Floor((wallX)); //x coordinate on the texture var texX = Math.Abs((wallX * texWidth).Floor()); if (side == 0) if (rayDirX > 0) texX = texWidth - texX - 1; if (side == 1) if (rayDirY < 0) texX = texWidth - texX - 1; var hT = h * 128; var lhT = lineHeight * 128; y = drawStart; while (y < drawEnd) { var d = y * 256 - hT + lhT; //256 and 128 factors to avoid floats var texY = Math.Abs(((d * texHeight) / lineHeight) / 256); var color = textures[texNum][texX][texY]; //var xxx = perpWallDist; #region apply fog var color_r = (color >> 16) & 0xff; var color_g = (color >> 8) & 0xff; var color_b = color & 0xff; var fog = 1 - (perpWallDist / 10).Min(); if (side == 0) fog = (fog * 2).Min(); color_r = (uint)(color_r * fog); color_g = (uint)(color_g * fog); color_b = (uint)(color_b * fog); color = (color_r << 16) + (color_g << 8) + color_b; #endregion //color = 0xff0000; screen.setPixel(x, y, color); y += 1; } //SET THE ZBUFFER FOR THE SPRITE CASTING ZBuffer[x] = perpWallDist; //perpendicular distance is used //floor casting double floorXWall; double floorYWall; //x, y position of the floor texel at the bottom of the wall //4 different wall directions possible if (side == 0) { if (rayDirX > 0) { floorXWall = mapX; floorYWall = mapY + wallX; } else { floorXWall = mapX + 1.0; floorYWall = mapY + wallX; } } else { if (rayDirY > 0) { floorXWall = mapX + wallX; floorYWall = mapY; } else { floorXWall = mapX + wallX; floorYWall = mapY + 1.0; } } var distWall = perpWallDist; var distPlayer = 0.0; var currentDist = 0.0; if (drawEnd < 0) drawEnd = h; //becomes < 0 when the integer overflows //draw the floor from drawEnd to the bottom of the screen #region draw flood y = drawEnd; double weight; double currentFloorX; double currentFloorY; int floorTexX; int floorTexY; while (y < h) { currentDist = (double)h / ((double)2 * (double)y - (double)h); //you could make a small lookup table for this instead //currentDist = floorVals[int(y-80)]; weight = (currentDist - distPlayer) / (distWall - distPlayer); currentFloorX = weight * floorXWall + (1.0 - weight) * posX; currentFloorY = weight * floorYWall + (1.0 - weight) * posY; floorTexX = Math.Abs((currentFloorX * texWidth).Floor() % texWidth); floorTexY = Math.Abs((currentFloorY * texHeight).Floor() % texHeight); #region floor try { var color = textures[1][floorTexX][floorTexY]; #region apply fog var color_r = (color >> 16) & 0xff; var color_g = (color >> 8) & 0xff; var color_b = color & 0xff; var fog = 1 - (currentDist / 10).Min(); fog = (fog * 2).Min(); color_r = (uint)(color_r * fog); color_g = (uint)(color_g * fog); color_b = (uint)(color_b * fog); color = (color_r << 16) + (color_g << 8) + color_b; #endregion screen.setPixel(x, y, color); //floor } catch { //trace("err"); } #endregion #region draw ceiling try { var color = textures[2][floorTexX][floorTexY]; #region apply fog var color_r = (color >> 16) & 0xff; var color_g = (color >> 8) & 0xff; var color_b = color & 0xff; var fog = 1 - (currentDist / 10).Min(); fog = (fog * 2).Min(); color_r = (uint)(color_r * fog); color_g = (uint)(color_g * fog); color_b = (uint)(color_b * fog); color = (color_r << 16) + (color_g << 8) + color_b; #endregion screen.setPixel(x, h - y - 1, color); //ceiling (symmetrical!) } catch { //trace("err"); } #endregion // draw sprites here y++; } #endregion foreach (var s in Sprites) { var DeltaToSprite = new PointDouble { X = s.X - posX, Y = s.Y - posY, }; var DirectionToSprite = Math.Abs(DeltaToSprite.GetRotation() - rayDir); if (!DeltaToSprite.GetRotation().IsInView(rayDirLeft, rayDirRight)) { continue; } if (s.ScanComplete) { // painted } else { //var ScanDirDelta = DirectionToSprite - rayDir; if (x == 0) s.ScanDir = DirectionToSprite; else { if (s.ScanDir < DirectionToSprite) { var ScanDir = s.ScanDir; DelayDrawSprites.Add( new SpriteDrawRequest { Sprite = s, distance = DeltaToSprite.GetDistance(), z = (1 / DeltaToSprite.GetDistance()) * 4, x = x, Text = new { distance = DeltaToSprite.GetDistance() //dir = DeltaToSprite.GetRotation().RadiansToDegrees(), rayDirLeft = rayDirLeft.RadiansToDegrees(), rayDirRight = rayDirRight.RadiansToDegrees() }.ToString().Replace(",", "\n") } ); //for (int _y = 0; _y < (120 * z).ToInt32(); _y++) //{ // screen.setPixel(x + 8, 120 + _y, 0xffffff); //ceili // screen.setPixel(x, 120 + _y, 0xffffff); //ceili // screen.setPixel(x, 120 - _y, 0xffffff); //ceili // screen.setPixel(x + 8, 120 - _y, 0xffffff); //ceili //} s.ScanComplete = true; } else { s.ScanDir = DirectionToSprite; } } } } x++; if (x > 4) render_DebugTrace_Assign_Active = false; } // draw clipped sprites foreach (var r in DelayDrawSprites.Where(i => !i.Sprite.DrawAsImage).OrderBy(i => i.z)) { // hardcoded for now var AsTextureWhichHasBits = textures[4]; var ixstart = r.Rectangle.Left; for (int ix = ixstart.Max(0); ix < r.Rectangle.Right.Min(w); ix++) { if (ZBuffer[ix] > r.distance) { var target_x = (ix - ixstart) * 64 / r.Rectangle.Width; var iystart = r.Rectangle.Top; for (int iy = iystart.Max(0); iy < r.Rectangle.Bottom.Min(h); iy++) { var target_y = (iy - iystart) * 64 / r.Rectangle.Height; var color = AsTextureWhichHasBits[target_x][target_y]; #region apply fog var color_a = (color >> 24) & 0xff; var color_r = (color >> 16) & 0xff; var color_g = (color >> 8) & 0xff; var color_b = color & 0xff; var fog = 1 - (r.distance / 10).Min(); fog = (fog * 2).Min(); color_r = (uint)(color_r * fog); color_g = (uint)(color_g * fog); color_b = (uint)(color_b * fog); color = (color_r << 16) + (color_g << 8) + color_b; #endregion if (color_a == 0xff) screen.setPixel(ix, iy, color); } } } } counter++; if (getTimer() - 1000 >= time) { txtMain.Text = counter.ToString(); counter = 0; time = getTimer(); } //screenImage.bitmapData = screen; screen.UnlockBits(screenData); using (var g = Graphics.FromImage(screen)) using (var green = new SolidBrush(Color.FromArgb(0x7f, Color.Green))) using (var yellow = new SolidBrush(Color.FromArgb(0x7f, Color.Yellow))) using (var blue = new SolidBrush(Color.FromArgb(0x7f, Color.Blue))) using (var purple = new SolidBrush(Color.FromArgb(0x7f, Color.Purple))) using (var cyan = new SolidBrush(Color.FromArgb(0x7f, Color.Cyan))) { foreach (var r in DelayDrawSprites.OrderBy(i => i.z)) { if (r.Sprite.DrawAsImage) g.DrawImage(r.Sprite.Image, r.Rectangle); else g.DrawRectangle(Pens.Goldenrod, r.Rectangle); if (!string.IsNullOrEmpty(r.Text)) g.DrawString(r.Text, SystemFonts.DefaultFont, Brushes.Red, r.Rectangle.X, 120); } var colors = new[] { green, yellow, blue, purple, cyan }; g.DrawImage(PistolImage, (w - PistolImage.Width) / 2, h - PistolImage.Height - HudImage.Height); g.DrawImage(HudImage, 0, h - HudImage.Height); g.ScaleTransform(4, 4); // i want a minimap! for (int iy = 0; iy < worldMap.YLength; iy++) for (int ix = 0; ix < worldMap.XLength; ix++) { var cell = worldMap[ix, iy]; if (cell == 0) { } else { g.FillRectangle(colors[cell % colors.Length], ix, iy, 1, 1); } } g.DrawLine(Pens.Red, posX, posY, posX + dirX * 2, posY + dirY * 2); foreach (var s in Sprites) { g.DrawLine(Pens.Yellow, s.X, s.Y, s.X + 1, s.Y); } } }
public static double GetDistance(this PointDouble p) { return(Math.Sqrt(p.X * p.X + p.Y * p.Y)); }