UltimateZoneBuilder/Source/Core/Rendering/Plotter.cs
2020-05-21 14:20:02 +02:00

427 lines
14 KiB
C#
Executable file

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using CodeImp.DoomBuilder.Geometry;
#endregion
namespace CodeImp.DoomBuilder.Rendering
{
internal unsafe sealed class Plotter : IDisposable
{
#region ================== Constants
private const int DASH_INTERVAL = 16; //mxd
#endregion
#region ================== Variables
// Memory
private PixelColor[] pixels;
private int width;
private int height;
private int visiblewidth;
private int visibleheight;
// GL
public Texture Texture { get; private set; }
#endregion
#region ================== Properties
public int VisibleWidth { get { return visiblewidth; } }
public int VisibleHeight { get { return visibleheight; } }
public int Width { get { return width; } }
public int Height { get { return height; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public Plotter(int width, int height)
{
// Initialize
Texture = new Texture(width, height, TextureFormat.Bgra8);
this.pixels = new PixelColor[width*height];
this.width = width;
this.height = height;
this.visiblewidth = width;
this.visibleheight = height;
}
public void Dispose()
{
if (Texture != null)
{
Texture.Dispose();
Texture = null;
}
}
#endregion
#region ================== Pixel Rendering
private int TransformY(int y)
{
return height - y;
}
// This clears all pixels black
public void Clear()
{
// Clear memory
fixed(PixelColor* pixel = pixels)
{
uint* op = (uint*)pixel;
for (int i = 0; i < pixels.Length; i++)
{
*op = 0;
op++;
}
}
}
// This draws a pixel normally
public void DrawPixelSolid(int x, int y, ref PixelColor c)
{
y = TransformY(y);
// Draw pixel when within range
if ((x >= 0) && (x < visiblewidth) && (y >= 0) && (y < visibleheight))
pixels[y * width + x] = c;
}
// This draws a pixel normally
public void DrawVertexSolid(int x, int y, int size, ref PixelColor c, ref PixelColor l, ref PixelColor d)
{
y = TransformY(y);
int x1 = x - size;
int x2 = x + size;
int y1 = y - size;
int y2 = y + size;
// Do unchecked?
if ((x1 >= 0) && (x2 < visiblewidth) && (y1 >= 0) && (y2 < visibleheight))
{
// Filled square
for (int yp = y1; yp <= y2; yp++)
for (int xp = x1; xp <= x2; xp++)
pixels[yp * width + xp] = c;
// Vertical edges
for (int yp = y1 + 1; yp <= y2 - 1; yp++)
{
pixels[yp * width + x1] = l;
pixels[yp * width + x2] = d;
}
// Horizontal edges
for (int xp = x1 + 1; xp <= x2 - 1; xp++)
{
pixels[y1 * width + xp] = l;
pixels[y2 * width + xp] = d;
}
// Corners
pixels[y2 * width + x2] = d;
pixels[y1 * width + x1] = l;
}
/*
else
{
// Filled square
for(yp = y - size; yp <= y + size; yp++)
for(xp = x - size; xp <= x + size; xp++)
DrawPixelSolid(xp, yp, c);
// Vertical edges
for(yp = y - size + 1; yp <= y + size - 1; yp++)
{
DrawPixelSolid(x - size, yp, l);
DrawPixelSolid(x + size, yp, d);
}
// Horizontal edges
for(xp = x - size + 1; xp <= x + size - 1; xp++)
{
DrawPixelSolid(xp, y - size, l);
DrawPixelSolid(xp, y + size, d);
}
// Corners
DrawPixelSolid(x + size, y + size, d);
DrawPixelSolid(x - size, y - size, l);
}
*/
}
// This draws a dotted grid line horizontally
public void DrawGridLineH(int y, int x1, int x2, ref PixelColor c)
{
y = TransformY(y);
int numpixels = visiblewidth >> 1;
int offset = y & 0x01;
int ywidth = y * width;
x1 = General.Clamp(x1 >> 1, 0, numpixels - 1);
x2 = General.Clamp(x2 >> 1, 0, numpixels - 1);
if ((y >= 0) && (y < height))
{
// Draw all pixels on this line
for (int i = x1; i < x2; i++) pixels[ywidth + ((i << 1) | offset)] = c;
}
}
// This draws a dotted grid line vertically
public void DrawGridLineV(int x, int y1, int y2, ref PixelColor c)
{
y1 = TransformY(y1);
y2 = TransformY(y2);
int numpixels = visibleheight >> 1;
int offset = x & 0x01;
y1 = General.Clamp(y1 >> 1, 0, numpixels - 1);
y2 = General.Clamp(y2 >> 1, 0, numpixels - 1);
if ((x >= 0) && (x < width))
{
// Draw all pixels on this line
for (int i = y2; i < y1; i++) pixels[((i << 1) | offset) * width + x] = c;
}
}
// This draws a pixel alpha blended
public void DrawPixelAlpha(int x, int y, ref PixelColor c)
{
y = TransformY(y);
fixed (PixelColor* pixels = this.pixels)
{
// Draw only when within range
if ((x >= 0) && (x < visiblewidth) && (y >= 0) && (y < visibleheight))
{
// Get the target pixel
PixelColor* p = pixels + (y * width + x);
// Not drawn on target yet?
if (*(int*)p == 0)
{
// Simply apply color to pixel
*p = c;
}
else
{
// Blend with pixel
float a = c.a * 0.003921568627450980392156862745098f;
if (p->a + c.a > 255) p->a = 255; else p->a += c.a;
p->r = (byte)(p->r * (1f - a) + c.r * a);
p->g = (byte)(p->g * (1f - a) + c.g * a);
p->b = (byte)(p->b * (1f - a) + c.b * a);
}
}
}
}
// This draws a line normally
// See: http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
public void DrawLineSolid(int x1, int y1, int x2, int y2, ref PixelColor c, uint mask = 0xffffffff)
{
y1 = TransformY(y1);
y2 = TransformY(y2);
// Check if the line is outside the screen for sure.
// This is quickly done by checking in which area both points are. When this
// is above, below, right or left of the screen, then skip drawing the line.
if (((x1 < 0) && (x2 < 0)) ||
((x1 > visiblewidth) && (x2 > visiblewidth)) ||
((y1 < 0) && (y2 < 0)) ||
((y1 > visibleheight) && (y2 > visibleheight))) return;
// Distance of the line
int dx = x2 - x1;
int dy = y2 - y1;
// Positive (absolute) distance
int dxabs = Math.Abs(dx);
int dyabs = Math.Abs(dy);
// Half distance
int x = dyabs >> 1;
int y = dxabs >> 1;
// Direction
int sdx = Math.Sign(dx);
int sdy = Math.Sign(dy);
// Start position
int px = x1;
int py = y1;
// When the line is completely inside screen,
// then do an unchecked draw, because all of its pixels are
// guaranteed to be within the memory range
if ((x1 >= 0) && (x2 >= 0) && (x1 < visiblewidth) && (x2 < visiblewidth) &&
(y1 >= 0) && (y2 >= 0) && (y1 < visibleheight) && (y2 < visibleheight))
{
// Draw first pixel
pixels[py * width + px] = c;
// Check if the line is more horizontal than vertical
if (dxabs >= dyabs)
{
for (int i = 0; i < dxabs; i++)
{
y += dyabs;
if (y >= dxabs)
{
y -= dxabs;
py += sdy;
}
px += sdx;
// Draw pixel
if ((mask & (1 << (i & 0x7))) != 0)
{
pixels[py * width + px] = c;
}
}
}
// Else the line is more vertical than horizontal
else
{
for (int i = 0; i < dyabs; i++)
{
x += dxabs;
if (x >= dyabs)
{
x -= dyabs;
px += sdx;
}
py += sdy;
// Draw pixel
if ((mask & (1 << (i & 0x7))) != 0)
{
pixels[py * width + px] = c;
}
}
}
}
else
{
// Draw first pixel
if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight))
pixels[py * width + px] = c;
// Check if the line is more horizontal than vertical
if (dxabs >= dyabs)
{
for (int i = 0; i < dxabs; i++)
{
y += dyabs;
if (y >= dxabs)
{
y -= dxabs;
py += sdy;
}
px += sdx;
// Draw pixel
if ((mask & (1 << (i & 0x7))) != 0)
{
if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight))
pixels[py * width + px] = c;
}
}
}
// Else the line is more vertical than horizontal
else
{
for (int i = 0; i < dyabs; i++)
{
x += dxabs;
if (x >= dyabs)
{
x -= dyabs;
px += sdx;
}
py += sdy;
// Draw pixel
if ((mask & (1 << (i & 0x7))) != 0)
{
if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight))
pixels[py * width + px] = c;
}
}
}
}
}
//mxd
public void DrawLine3DFloor(int x1, int y1, int x2, int y2, ref PixelColor c, PixelColor c2)
{
Line2D line = new Line2D(x1, y1, x2, y2);
double length = line.GetLength();
if (length < DASH_INTERVAL * 2)
{
DrawLineSolid(x1, y1, x2, y2, ref c2);
}
else
{
double d1 = DASH_INTERVAL / length;
double d2 = 1.0f - d1;
Vector2D p1 = line.GetCoordinatesAt(d1);
Vector2D p2 = line.GetCoordinatesAt(d2);
DrawLineSolid(x1, y1, (int)p1.x, (int)p1.y, ref c2);
DrawLineSolid((int)p1.x, (int)p1.y, (int)p2.x, (int)p2.y, ref c);
DrawLineSolid((int)p2.x, (int)p2.y, x2, y2, ref c2);
}
}
#endregion
#region ================== Drawing to rendertarget
public void DrawContents(RenderDevice graphics)
{
// set pixels of texture
// convert from pixelcolor to uint
fixed (PixelColor* pixels = this.pixels)
{
uint* uintpixels = (uint*)pixels;
uint* targetpixels = (uint*)graphics.MapPBO(Texture);
for (int i = 0; i < this.pixels.Length; i++)
*targetpixels++ = *uintpixels++;
graphics.UnmapPBO(Texture);
}
}
#endregion
}
}