/// <summary> /// Draws a bilboard rectangle with depth. Rendering is clipped against a Y buffer. /// </summary> /// <param name="dst">Target PixelBuffer.</param> /// <param name="centerX">X Coordinate of the rectangle's center</param> /// <param name="distance">Distance from the camera to render.</param> /// <param name="width">Width of the rectangle at distance = 1</param> /// <param name="height">Height of the rectangle at distance = 1</param> /// <param name="y_buffer">A ray buffer to do distance clipping against.</param> /// <param name="color">The color to render the rectangle.</param> public static void rectDepth(PixelBuffer dst, int centerX, float distance, int width, int height, RayData[] y_buffer, Rgba color) { width = (int)(width / distance); height = (int)(height / distance); int hw = (int)((width >> 1)); int hh = (int)((height >> 1)); clipRect.x = centerX - hw; clipRect.y = (dst.height >> 1) - hh; clipRect.w = width; clipRect.h = height; rect(dst, clipRect.x, clipRect.y, clipRect.w, clipRect.h, clippedColor); clipRect.left = Math.Max(clipRect.x, 0); clipRect.right = Math.Min(clipRect.x + clipRect.w, dst.width - 1); clipRect.top = clipRect.y; clipRect.bot = clipRect.y + clipRect.h; // clip left for (int i = clipRect.left; i < clipRect.right; i++) { if (y_buffer[i].dis <= distance) { clipRect.left = i; } else { break; } } // clip right for (int i = clipRect.left + 1; i < clipRect.right; i++) { if (y_buffer[i].dis <= distance) { clipRect.right = i; break; } } rect(dst, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bot - clipRect.top, color); }
/// <summary> /// Draw a wall column using a texture for pixel colors and use fog calculations. /// </summary> /// <param name="dst">Target PixelBuffer</param> /// <param name="x">X coordinate to render the wall on.</param> /// <param name="z">View height (but does nothing at the moment) </param> /// <param name="distance">Distance of the wall from the camera</param> /// <param name="textureOffset">Offset of the source texture to render from</param> /// <param name="src">Source texture to render from</param> public static void wallColumn(PixelBuffer dst, int x, float z, float distance, float textureOffset, PixelBuffer src, Rgba fogColor) { // Calculate initial source texture position. int fp_Src = (int)(src.height * textureOffset); fp_Src = fp_Src * src.width; fp_Src = fp_Src << 8; // Calculate the height of the column render. int colHeight = dst.height; if (distance != 0) { colHeight = (int)(dst.height / distance); } // Calculate view offset int viewOfs = (int)(colHeight * (z - 1)); // Calculate base step ratio for source texture read. int fp_srcStep = (src.width << 8) / colHeight; // Handle columns too tall for the screen. if (colHeight > dst.height || distance == 0) { // Adjust the source texture data offset. fp_Src += fp_srcStep * ((colHeight - dst.height) >> 1); // Clip the column height to screen height. colHeight = dst.height; } // Calculate the destination data offset for the write. int dstIdx = (x * BPP) + ((((dst.height + viewOfs) - colHeight) >> 1) * dst.stride); // Calcualte shading values // Fixed point rgba maths int fp_r; int fp_g; int fp_b; int fp_fogCol = (int)(distance * 16f); fp_fogCol += fogColor.a; if (fp_fogCol > 0x0100) { fp_fogCol = 0x0100; } //Console.WriteLine(fp_fogCol.ToString("X8")); int fog_r = fp_fogCol * (fogColor.r); int fog_g = fp_fogCol * (fogColor.g); int fog_b = fp_fogCol * (fogColor.b); int fp_srcCol = 0x0100 - fp_fogCol; int srcIdx; if (fp_srcStep >= 0x10000) { // Draw the column. for (int i = 0; i < colHeight; i++) { srcIdx = (fp_Src >> 8) << 2; if (dstIdx < 0 || dstIdx >= dst.height) { break; } fp_r = (src.pixels[srcIdx] * fp_srcCol) + fog_r; fp_g = (src.pixels[srcIdx + 1] * fp_srcCol) + fog_g; fp_b = (src.pixels[srcIdx + 2] * fp_srcCol) + fog_b; dst.pixels[dstIdx] = (byte)(fp_r >> 8); dst.pixels[dstIdx + 1] = (byte)(fp_g >> 8); dst.pixels[dstIdx + 2] = (byte)(fp_b >> 8); dst.pixels[dstIdx + 3] = 255; fp_Src += fp_srcStep; dstIdx += dst.stride; } } else { // Compressed Column Drawing int fp_nextSrc; byte r, g, b; // Draw the column. for (int i = 0; i < colHeight;) { fp_nextSrc = (fp_Src + 0x100) & 0xFFFFF00; srcIdx = (fp_Src >> 8) << 2; fp_r = ((src.pixels[srcIdx] * fp_srcCol) + fog_r) >> 8; fp_g = ((src.pixels[srcIdx + 1] * fp_srcCol) + fog_g) >> 8; fp_b = ((src.pixels[srcIdx + 2] * fp_srcCol) + fog_b) >> 8; r = (byte)fp_r; g = (byte)fp_g; b = (byte)fp_b; while (fp_Src < fp_nextSrc && i < colHeight) { // HACK: Nearsided renderer should really not have to do this. if (dstIdx >= 0 && dstIdx < dst.pixels.Length) { dst.pixels[dstIdx] = r; dst.pixels[dstIdx + 1] = g; dst.pixels[dstIdx + 2] = b; dst.pixels[dstIdx + 3] = 255; } fp_Src += fp_srcStep; dstIdx += dst.stride; i++; } } } }
/// <summary> /// Draw a rectangle. /// </summary> /// <param name="x">Left edge coordinate of the rectangle</param> /// <param name="y">Top edge coordinate of teh rectangle</param> /// <param name="width">Width in pixels</param> /// <param name="height">Height in pixels</param> /// <param name="color">The desired RGBA color</param> public static void rect(PixelBuffer dst, int x, int y, int width, int height, Rgba color) { // Find clipping rectangle. int left = Math.Max(x, 0); int right = Math.Min(x + width, dst.width - 1); int top = Math.Max(y, 0); int bot = Math.Min(y + height, dst.height - 1); int clipH = bot - top; int clipW = right - left; if (clipH <= 0 || clipW <= 0) { return; } int dy = top * dst.stride; int dstA = dy + (left << 2); int dstB = dy + (right << 2); // Draw pixel with alpha blending. int fp8_dstBlend = 0xFF - color.a; int ar = color.r * color.a; int ag = color.g * color.a; int ab = color.b * color.a; // Draw cols. for (int i = 0; i < clipH; i++) { if (left == x) { dst.pixels[dstA + 0] = (byte)(((dst.pixels[dstA + 0] * fp8_dstBlend) + ar) >> 8); dst.pixels[dstA + 1] = (byte)(((dst.pixels[dstA + 1] * fp8_dstBlend) + ag) >> 8); dst.pixels[dstA + 2] = (byte)(((dst.pixels[dstA + 2] * fp8_dstBlend) + ab) >> 8); dst.pixels[dstA + 3] = 255; } if (right == x + width) { dst.pixels[dstB + 0] = (byte)(((dst.pixels[dstB + 0] * fp8_dstBlend) + ar) >> 8); dst.pixels[dstB + 1] = (byte)(((dst.pixels[dstB + 1] * fp8_dstBlend) + ag) >> 8); dst.pixels[dstB + 2] = (byte)(((dst.pixels[dstB + 2] * fp8_dstBlend) + ab) >> 8); dst.pixels[dstB + 3] = 255; } dstA += dst.stride; dstB += dst.stride; } dstB = dy + (left << 2); // Draw rows for (int i = 0; i < clipW; i++) { if (bot == (y + height)) { dst.pixels[dstA + 0] = (byte)(((dst.pixels[dstA + 0] * fp8_dstBlend) + ar) >> 8); dst.pixels[dstA + 1] = (byte)(((dst.pixels[dstA + 1] * fp8_dstBlend) + ag) >> 8); dst.pixels[dstA + 2] = (byte)(((dst.pixels[dstA + 2] * fp8_dstBlend) + ab) >> 8); dst.pixels[dstA + 3] = 255; } if (top == y) { dst.pixels[dstB + 0] = (byte)(((dst.pixels[dstB + 0] * fp8_dstBlend) + ar) >> 8); dst.pixels[dstB + 1] = (byte)(((dst.pixels[dstB + 1] * fp8_dstBlend) + ag) >> 8); dst.pixels[dstB + 2] = (byte)(((dst.pixels[dstB + 2] * fp8_dstBlend) + ab) >> 8); dst.pixels[dstB + 3] = 255; } dstA += BPP; dstB += BPP; } }