I am trying to write a simple class to render lines. I seem to be having an issue with origin. I came across this when I was trying to write the class the first time, and the line rendered in an unexpected place (off screen a bit).
If you look at the attached code, I have a simple class that generates a rectangle, and rotates it (to draw a line of a given thickness). You can see I have 2 textures there. One is just a 1x1 white pixel that is generated at load time. I use this little trick a lot to render simple rectangles and semi-opaque backdrops and such, since you can easily just change the color via spriteBatch.Draw and “stretch” the pixel to whatever size you need.
However, I’ve found that if I try to use my 1x1 pixel in this example, with a set origin, then the render doesn’t appear where you’d expect. Instead, the origin seems to move the render dramatically in the reversed direction. As far as I can tell, this is because the origin’s values are simply higher than the textures.
I don’t understand why this is happening, or what I can do to be able to not use a loaded texture just to draw simple lines.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace LineDraw
{
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public static Texture2D plainTexture;
public static Texture2D red800x800Texture;
Line line = new Line(new Vector2(100, 100), new Vector2(300, 300));
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
plainTexture = new Texture2D(graphics.GraphicsDevice, 1, 1);
plainTexture.SetData(new Color[] { Color.White });
red800x800Texture = Content.Load<Texture2D>("800x800_red");
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Immediate);
line.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
class Helpers
{
public static double GetAngleInRadians(Vector2 origin, Vector2 target)
{
var n = (Math.Atan2(origin.Y - target.Y, origin.X - target.X)) * 180 / Math.PI;
n -= 180;
// keep in normal radians range
double radians = MathHelper.ToRadians((float)(n % 360));
KeepRadiansIn2Pi(ref radians);
return radians;
}
public static void KeepRadiansIn2Pi(ref double radians)
{
while (radians > 2 * Math.PI)
radians -= 2 * Math.PI;
while (radians < 0)
radians += 2 * Math.PI;
}
}
class Line
{
private Vector2 _p1, _p2;
public Vector2 Point1
{
get { return _p1; }
set
{
_p1 = value;
CalcValues();
}
}
public Vector2 Point2
{
get { return _p2; }
set
{
_p2 = value;
CalcValues();
}
}
private int _thickness;
public int Thickness
{
get { return _thickness; }
set
{
_thickness = value;
CalcValues();
}
}
private Rectangle lineRect;
private Vector2 origin;
private float rotation;
public Color color;
public Line(Vector2 point1, Vector2 point2, Color? color = null, int thickness = 5)
{
_p1 = point1;
_p2 = point2;
_thickness = thickness;
CalcValues();
this.color = color.GetValueOrDefault(Color.Red);
}
private void CalcValues()
{
float length = Vector2.Distance(Point1, Point2);
rotation = (float)Helpers.GetAngleInRadians(Point1, Point2);
lineRect = new Rectangle(
x: (int)Math.Round(Point1.X),
y: (int)Math.Round(Point1.Y),
width: (int)Math.Round(length) + _thickness,
height: _thickness
);
origin = new Vector2((float)_thickness / 2, (float)_thickness / 2);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(
texture: Game1.red800x800Texture,
destinationRectangle: lineRect,
origin: origin,
rotation: rotation,
color: color
);
}
}
}