mirror of
https://github.com/watercolor-games/shiftos-pong.git
synced 2025-01-22 17:52:15 +00:00
429 lines
18 KiB
C#
429 lines
18 KiB
C#
|
using System;
|
|||
|
using Microsoft.Xna.Framework;
|
|||
|
using Microsoft.Xna.Framework.Graphics;
|
|||
|
|
|||
|
namespace Plex.Engine.GraphicsSubsystem
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Encapsulates a <see cref="GraphicsDevice"/> and <see cref="SpriteBatch"/> and contains methods for easily rendering various objects using those encapsulated objects. This class cannot be inherited.
|
|||
|
/// </summary>
|
|||
|
/// <remarks>
|
|||
|
/// <para>The <see cref="GraphicsContext"/> class employs scissor testing in all of its draw calls. This makes it so that any data rendering outside the scissor rectangle (defined by the <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/> and <see cref="Height"/> properties) will be clipped and not rendered to the screen.</para>
|
|||
|
/// <para>Also, apart from the <see cref="X"/> and <see cref="Y"/> properties of the graphics context, any X/Y coordinate pairs are relative to the coordinates of the scissor rectangle. So, the coordinates (0,5) refer to <see cref="X"/>+0,<see cref="Y"/>+5.</para>
|
|||
|
/// </remarks>
|
|||
|
/// <seealso cref="RasterizerState.ScissorTestEnable"/>
|
|||
|
/// <seealso cref="GraphicsDevice.ScissorRectangle"/>
|
|||
|
/// <seealso cref="GraphicsDevice"/>
|
|||
|
/// <seealso cref="SpriteBatch"/>
|
|||
|
/// <threadsafety static="true" instance="false"/>
|
|||
|
public sealed class GraphicsContext
|
|||
|
{
|
|||
|
private static Texture2D white = null;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Retrieves the sprite batch associated with this graphics context.
|
|||
|
/// </summary>
|
|||
|
public SpriteBatch Batch
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return _spritebatch;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Retrieves the graphics device associated with this graphics context.
|
|||
|
/// </summary>
|
|||
|
public GraphicsDevice Device
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return _graphicsDevice;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the X coordinate of the scissor rectangle.
|
|||
|
/// </summary>
|
|||
|
public int X
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Device.ScissorRectangle.X;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
Device.ScissorRectangle = new Rectangle(value, Y, Width, Height);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the Y coordinate of the scissor rectangle.
|
|||
|
/// </summary>
|
|||
|
public int Y
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Device.ScissorRectangle.Y;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
Device.ScissorRectangle = new Rectangle(X, value, Width, Height);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the width of the scissor rectangle.
|
|||
|
/// </summary>
|
|||
|
public int Width
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Device.ScissorRectangle.Width;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
Device.ScissorRectangle = new Rectangle(X, Y, value, Height);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the height of the scissor rectangle.
|
|||
|
/// </summary>
|
|||
|
public int Height
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Device.ScissorRectangle.Height;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
Device.ScissorRectangle = new Rectangle(X, Y, Width, value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw an outlined polygon.
|
|||
|
/// </summary>
|
|||
|
/// <param name="c">The color of the polygon's outlines</param>
|
|||
|
/// <param name="locs">The various X and Y coordinates relative to the scissor rectangle of the polygon. The size of this array must be a multiple of 2.</param>
|
|||
|
/// <exception cref="Exception">The <paramref name="locs"/> array does not have a length which is a multiple of 2.</exception>
|
|||
|
public void DrawPolygon(Color c, params int[] locs)
|
|||
|
{
|
|||
|
if ((locs.Length % 2) != 0)
|
|||
|
throw new Exception("The locs argument count must be a multiple of 2.");
|
|||
|
for(int i = 0; i < locs.Length; i+= 2)
|
|||
|
{
|
|||
|
int x = locs[i];
|
|||
|
int y = locs[i + 1];
|
|||
|
int x1 = locs[0];
|
|||
|
int y1 = locs[1];
|
|||
|
|
|||
|
if (i < locs.Length - 2)
|
|||
|
{
|
|||
|
x1 = locs[i + 2];
|
|||
|
y1 = locs[i + 3];
|
|||
|
}
|
|||
|
DrawLine(x, y, x1, y1, 1, c);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private GraphicsDevice _graphicsDevice;
|
|||
|
private SpriteBatch _spritebatch;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates a new instance of the <see cref="GraphicsContext"/> class.
|
|||
|
/// </summary>
|
|||
|
/// <param name="device">The graphics device where rendering will take place.</param>
|
|||
|
/// <param name="batch">The sprite batch to associate with the graphics context.</param>
|
|||
|
/// <param name="x">The starting X coordinate of the scissor rectangle.</param>
|
|||
|
/// <param name="y">The starting Y coordinate of the scissor rectangle.</param>
|
|||
|
/// <param name="width">The starting width of the scissor rectangle.</param>
|
|||
|
/// <param name="height">The starting height of the scissor rectangle.</param>
|
|||
|
public GraphicsContext(GraphicsDevice device, SpriteBatch batch, int x, int y, int width, int height)
|
|||
|
{
|
|||
|
if (device == null || batch == null)
|
|||
|
throw new ArgumentNullException();
|
|||
|
|
|||
|
_graphicsDevice = device;
|
|||
|
_spritebatch = batch;
|
|||
|
if(white == null)
|
|||
|
{
|
|||
|
white = new Texture2D(_graphicsDevice, 1, 1);
|
|||
|
white.SetData<byte>(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF });
|
|||
|
}
|
|||
|
Width = width;
|
|||
|
Height = height;
|
|||
|
X = x;
|
|||
|
Y = y;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Clears the canvas with the specified color.
|
|||
|
/// </summary>
|
|||
|
/// <param name="c">The color to render</param>
|
|||
|
public void Clear(Color c)
|
|||
|
{
|
|||
|
DrawRectangle(0, 0, Width, Height, c);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a line between two separate points on the canvas
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the first point</param>
|
|||
|
/// <param name="y">The Y coordinate of the first point</param>
|
|||
|
/// <param name="x1">The X coordinate of the second point</param>
|
|||
|
/// <param name="y1">The Y coordinate of the second point</param>
|
|||
|
/// <param name="thickness">The thickness of the line</param>
|
|||
|
/// <param name="tex2">The line's texture</param>
|
|||
|
public void DrawLine(int x, int y, int x1, int y1, int thickness, Texture2D tex2)
|
|||
|
{
|
|||
|
DrawLine(x, y, x1, y1, thickness, tex2, Color.White);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a line with a tint between two separate points on the canvas
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the first point</param>
|
|||
|
/// <param name="y">The Y coordinate of the first point</param>
|
|||
|
/// <param name="x1">The X coordinate of the second point</param>
|
|||
|
/// <param name="y1">The Y coordinate of the second point</param>
|
|||
|
/// <param name="thickness">The thickness of the line</param>
|
|||
|
/// <param name="tex2">The line's texture</param>
|
|||
|
/// <param name="tint">The tint of the texture</param>
|
|||
|
public void DrawLine(int x, int y, int x1, int y1, int thickness, Texture2D tex2, Color tint)
|
|||
|
{
|
|||
|
if (tint.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
x += X;
|
|||
|
y += Y;
|
|||
|
x1 += X;
|
|||
|
y1 += Y;
|
|||
|
int distance = (int)Vector2.Distance(new Vector2(x, y), new Vector2(x1, y1));
|
|||
|
float rotation = GetRotation(x, y, x1, y1);
|
|||
|
_spritebatch.Draw(tex2, new Rectangle(x, y, distance, thickness), null, tint, rotation, Vector2.Zero, SpriteEffects.None, 0);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a line with a tint between two separate points on the canvas
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the first point</param>
|
|||
|
/// <param name="y">The Y coordinate of the first point</param>
|
|||
|
/// <param name="x1">The X coordinate of the second point</param>
|
|||
|
/// <param name="y1">The Y coordinate of the second point</param>
|
|||
|
/// <param name="thickness">The thickness of the line</param>
|
|||
|
/// <param name="color">The color of the line</param>
|
|||
|
public void DrawLine(int x, int y, int x1, int y1, int thickness, Color color)
|
|||
|
{
|
|||
|
if (color.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
x += X;
|
|||
|
y += Y;
|
|||
|
x1 += X;
|
|||
|
y1 += Y;
|
|||
|
int distance = (int)Vector2.Distance(new Vector2(x, y), new Vector2(x1, y1));
|
|||
|
float rotation = GetRotation(x, y, x1, y1);
|
|||
|
_spritebatch.Draw(white, new Rectangle(x, y, distance, thickness), null, color, rotation, Vector2.Zero, SpriteEffects.None, 0);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a rectangle with the specified color to the canvas.
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the rectangle</param>
|
|||
|
/// <param name="y">The Y coordinate of the rectangle</param>
|
|||
|
/// <param name="width">The width of the rectangle</param>
|
|||
|
/// <param name="height">The height of the rectangle</param>
|
|||
|
/// <param name="color">The color of the rectangle</param>
|
|||
|
public void DrawRectangle(int x, int y, int width, int height, Color color)
|
|||
|
{
|
|||
|
if (color.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
x += X;
|
|||
|
y += Y;
|
|||
|
_spritebatch.Draw(white, new Rectangle(x, y, width, height), color);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Begin a draw call.
|
|||
|
/// </summary>
|
|||
|
public void BeginDraw()
|
|||
|
{
|
|||
|
_spritebatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,
|
|||
|
SamplerState.LinearClamp, Device.DepthStencilState,
|
|||
|
RasterizerState);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// End the current draw call.
|
|||
|
/// </summary>
|
|||
|
public void EndDraw()
|
|||
|
{
|
|||
|
_spritebatch.End();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a circle to the canvas.
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the circle</param>
|
|||
|
/// <param name="y">The Y coordinate of the circle</param>
|
|||
|
/// <param name="radius">The radius of the circle</param>
|
|||
|
/// <param name="color">The color of the circle</param>
|
|||
|
public void DrawCircle(int x, int y, int radius, Color color)
|
|||
|
{
|
|||
|
if (color.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
float step = (float) Math.PI / (radius * 4);
|
|||
|
var rect = new Rectangle(x, y, radius, 1);
|
|||
|
for (float theta = 0; theta < 2 * Math.PI; theta += step)
|
|||
|
_spritebatch.Draw(white, rect, null, color, theta, Vector2.Zero, SpriteEffects.None, 0);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a rectangle with the specified texture and tint to the canvas.
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the rectangle</param>
|
|||
|
/// <param name="y">The Y coordinate of the rectangle</param>
|
|||
|
/// <param name="width">The width of the rectangle</param>
|
|||
|
/// <param name="height">The height of the rectangle</param>
|
|||
|
/// <param name="tex2">The texture of the rectangle</param>
|
|||
|
/// <param name="layout">The tint of the rectangle</param>
|
|||
|
public void DrawRectangle(int x, int y, int width, int height, Texture2D tex2, ImageLayout layout = ImageLayout.Stretch)
|
|||
|
{
|
|||
|
DrawRectangle(x, y, width, height, tex2, Color.White, layout);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Retrieves a new <see cref="RasterizerState"/> preferred to be used by the graphics context.
|
|||
|
/// </summary>
|
|||
|
public readonly RasterizerState RasterizerState = new RasterizerState { ScissorTestEnable = true, MultiSampleAntiAlias = true };
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a rectangle with the specified texture, tint and <see cref="System.Windows.Forms.ImageLayout"/> to the canvas.
|
|||
|
/// </summary>
|
|||
|
/// <param name="x">The X coordinate of the rectangle</param>
|
|||
|
/// <param name="y">The Y coordinate of the rectangle</param>
|
|||
|
/// <param name="width">The width of the rectangle</param>
|
|||
|
/// <param name="height">The height of the rectangle</param>
|
|||
|
/// <param name="tex2">The texture of the rectangle</param>
|
|||
|
/// <param name="tint">The tint of the texture</param>
|
|||
|
/// <param name="layout">The layout of the texture</param>
|
|||
|
/// <param name="opaque">Whether the rectangle should be opaque regardless of the texture data or tint's alpha value.</param>
|
|||
|
/// <param name="premultiplied">Whether the texture data is already pre-multiplied.</param>
|
|||
|
public void DrawRectangle(int x, int y, int width, int height, Texture2D tex2, Color tint, ImageLayout layout = ImageLayout.Stretch, bool opaque = false, bool premultiplied=true)
|
|||
|
{
|
|||
|
if (tint.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
if (tex2 == null)
|
|||
|
return;
|
|||
|
x += X;
|
|||
|
y += Y;
|
|||
|
_spritebatch.End();
|
|||
|
var state = SamplerState.LinearClamp;
|
|||
|
if (layout == ImageLayout.Tile)
|
|||
|
state = SamplerState.LinearWrap;
|
|||
|
if (opaque)
|
|||
|
{
|
|||
|
_spritebatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
|
|||
|
state, Device.DepthStencilState,
|
|||
|
RasterizerState);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (premultiplied)
|
|||
|
{
|
|||
|
_spritebatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,
|
|||
|
state, Device.DepthStencilState,
|
|||
|
RasterizerState);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_spritebatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
|
|||
|
state, Device.DepthStencilState,
|
|||
|
RasterizerState);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
switch (layout)
|
|||
|
{
|
|||
|
case ImageLayout.Tile:
|
|||
|
_spritebatch.Draw(tex2, new Vector2(x,y), new Rectangle(0, 0, width, height), Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0);
|
|||
|
break;
|
|||
|
case ImageLayout.Stretch:
|
|||
|
_spritebatch.Draw(tex2, new Rectangle(x, y, width, height), tint);
|
|||
|
break;
|
|||
|
case ImageLayout.None:
|
|||
|
_spritebatch.Draw(tex2, new Rectangle(x, y, tex2.Width, tex2.Height), tint);
|
|||
|
break;
|
|||
|
case ImageLayout.Center:
|
|||
|
_spritebatch.Draw(tex2, new Rectangle(x+((width - tex2.Width) / 2), y+((height - tex2.Height) / 2), tex2.Width, tex2.Height), tint);
|
|||
|
break;
|
|||
|
case ImageLayout.Zoom:
|
|||
|
float scale = Math.Min(width / (float)tex2.Width, height / (float)tex2.Height);
|
|||
|
|
|||
|
var scaleWidth = (int)(tex2.Width * scale);
|
|||
|
var scaleHeight = (int)(tex2.Height * scale);
|
|||
|
|
|||
|
_spritebatch.Draw(tex2, new Rectangle(x+(((int)width - scaleWidth) / 2), y+(((int)height - scaleHeight) / 2), scaleWidth, scaleHeight), tint);
|
|||
|
break;
|
|||
|
;
|
|||
|
}
|
|||
|
_spritebatch.End();
|
|||
|
BeginDraw();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Measure a string. Note that this method is a stub and just calls <see cref="TextRenderer.MeasureText(string, System.Drawing.Font, int, TextAlignment, WrapMode)"/>. This stub will be removed soon.
|
|||
|
/// </summary>
|
|||
|
/// <param name="text">The text to measure</param>
|
|||
|
/// <param name="font">The font to measure with</param>
|
|||
|
/// <param name="wrapWidth">The maximum width text can be before it is wrapped</param>
|
|||
|
/// <param name="wrapMode">The wrap mode of the text</param>
|
|||
|
/// <returns>The size of the text in pixels</returns>
|
|||
|
public static Vector2 MeasureString(string text, SpriteFont font, int wrapWidth = int.MaxValue, WrapMode wrapMode = WrapMode.Words)
|
|||
|
{
|
|||
|
return TextRenderer.MeasureText(text, font, wrapWidth, wrapMode);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw a string of text.
|
|||
|
/// </summary>
|
|||
|
/// <param name="text">The text to render</param>
|
|||
|
/// <param name="x">The X coordinate of the text</param>
|
|||
|
/// <param name="y">The Y coordinate of the text</param>
|
|||
|
/// <param name="color">The color of the text</param>
|
|||
|
/// <param name="font">The font of the text</param>
|
|||
|
/// <param name="alignment">The alignment of the text</param>
|
|||
|
/// <param name="wrapWidth">The maximum width text can be before it is wrapped.</param>
|
|||
|
/// <param name="wrapMode">The wrap mode of the text</param>
|
|||
|
public void DrawString(string text, int x, int y, Color color, SpriteFont font, TextAlignment alignment, int wrapWidth = int.MaxValue, WrapMode wrapMode = WrapMode.Words)
|
|||
|
{
|
|||
|
x += X;
|
|||
|
y += Y;
|
|||
|
if (color.A == 0)
|
|||
|
return; //no sense rendering if you CAN'T SEE IT
|
|||
|
if (string.IsNullOrEmpty(text))
|
|||
|
return;
|
|||
|
TextRenderer.DrawText(this, x, y, text, font, color, wrapWidth, alignment, wrapMode);
|
|||
|
}
|
|||
|
|
|||
|
private float GetRotation(float x, float y, float x2, float y2)
|
|||
|
{
|
|||
|
float adj = x - x2;
|
|||
|
float opp = y - y2;
|
|||
|
return (float) Math.Atan2(opp, adj) - (float) Math.PI;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public enum ImageLayout
|
|||
|
{
|
|||
|
Tile,
|
|||
|
Stretch,
|
|||
|
None,
|
|||
|
Zoom,
|
|||
|
Center
|
|||
|
}
|
|||
|
}
|