Useful code snippets and algorithms

Hey there. I did a search on here for any threads like this one which lists useful drop-in code snippets that are well-documented. Couldn’t find one so… decided to make one. Feel free to add your own :slight_smile:

SpriteFont letter wrapping extension method

/// <summary>
/// Performs letter-wrapping on a specified string using the specified <see cref="SpriteFont"></see> for measurement, making sure each line doesn't exceed the specified maximum width in pixels.
/// </summary>
/// <param name="font">A <see cref="SpriteFont"></see> used for character measurement.</param>
/// <param name="text">The text to wrap.</param>
/// <param name="lineWidth">The width in pixels that each line should be.</param>
/// <returns>The wrapped text.</returns>
public static string LetterWrap(this SpriteFont font, string text, float lineWidth)
{
    //If the text is null or whitespace, return the text. We don't need to wrap it.
    if(string.IsNullOrWhiteSpace(text))
        return;

    //We'll store the resulting string in here
    string result = "";
    //We'll store the current line width in here.
    float currLine = 0f;

    //Iterate through each character in the source text
    foreach(char c in text)
    {
        //If the character is a carriage return '\r', skip it. Only \n matters.
        if(c == '\r') continue;
        //If the character is a newline '\n', append it to the result, set the current line with to 0, then move to the next character.
        if(c == '\n')
        {
            currLine = 0f;
            result += c;
            continue;
        }

        //If the SpriteFont does not contain a definition for the character, throw.
        if(!font.Characters.Contains(c)) throw new InvalidOperationException("The text contains characters that are not resolvable in the specified SpriteFont. You must either remove the unresolvable characters from the text, or add the Unicode character '" + ((int)c).ToString() + "' to your font descriptor.");

        //Measure the character to get its width
        var width = font.MeasureString(c.ToString()).X;

        //If the current line width + this character's line width is greater than the maximum line width, append a newline.
        if(currLine + width > lineWidth)
        {
            result += "\n";
            currLine = 0; //reset the current line width, we're on a new line.
        }

        //Append the character to the result
        result += c;
        //Increase the current line width by the character's width
        currLine += width;
    }

    //Return our result
    return result;
}

Helper method for getting a velocity from two Vector2s:

(thanks to @Apostolique for showing me how to do this, thought I’d post it here as an extension method in case anyone else needs it)

/// <summary>
/// Computes the velocity of an object toward a target.
/// </summary>
/// <param name="source">The source vector, i.e where a missile was launched.</param>
/// <param name="destination">The destination vector, i.e the missile's target.</param>
/// <param name="speed">The speed of the theoretical missile.</param>
/// <returns>A normalized <see cref="Vector2"></see> containing the velocity of the source object towards its destination, multiplied by the source's speed.</returns>
public static Vector2 GetVelocity(this Vector2 source, Vector2 destination, float speed)
{
    //Calculate the distance between the source and destination.
    var distance = destination - source;

    //Now we have the direction in which the source object is travelling.

    //Now we want to normalize it. Thankfully MonoGame can do that for us.
    var normalized = Vector2.Normalize(distance);

    //Now we multiply it by the speed.
    return normalized * speed;
}

I’ll try to have XML documentation in any of my snippets and I’ll try to keep them as extension methods that you can just plop in a static class and use anywhere as if they were a part of the MonoGame framework itself.

I have tons of them scattered all over.
In the spirit of the last entry though.

This one wraps words to stay within a Rectangle.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Microsoft.Xna.Framework
{

    /// <summary>
    /// Wraps a stringbuilder to a bounding box.
    /// This class of course works for a no garbage MgStringBuilder as well.
    /// </summary>
    public static class MgTextBounder
    {
        private static SpriteFont tsf;
        private static Dictionary<char, SpriteFont.Glyph> _glyphs;
        private static SpriteFont.Glyph defaultGlyph;
        private static char defaultfontchar = ' ';

        /// <summary>
        /// Set the spritefont this needs to be done before the class is used.
        /// </summary>
        public static void SetSpriteFont(SpriteFont s)
        {
            tsf = s;
            _glyphs = tsf.GetGlyphs();
            defaultGlyph = new SpriteFont.Glyph();
            if (tsf.DefaultCharacter.HasValue)
            {
                defaultfontchar = (char)(tsf.DefaultCharacter.Value);
                defaultGlyph = _glyphs[defaultfontchar];
            }
        }
        
        /// <summary>
        /// This changes the ref stringbuilder by word wrapping it to a bounding box.
        /// </summary>
        public static void WordWrapTextWithinBounds(ref StringBuilder text, Vector2 scale, Rectangle boundRect)
        {
            var Spacing = tsf.Spacing;
            var lineHeight = tsf.LineSpacing * scale.Y;
            Vector2 offset = Vector2.Zero;
            float yextent = offset.Y + lineHeight;
            Vector2 redrawOffset = Vector2.Zero;
            Rectangle dest = new Rectangle();
            var currentGlyph = SpriteFont.Glyph.Empty;
            var firstGlyphOfLine = true;

            int lastWordBreakCharPos = 0;
            bool firstwordonline = true;
            Vector2 rewind = Vector2.Zero;

            //int i = -1;

            //Console.WriteLine(" text.StringBuilder.Length " + text.StringBuilder.Length.ToString() + "  ");

            //while (i < text.Length)
            //{ i++;
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                //Console.Write(" text[" + i + "]");
                //Console.Write(" = " + c + " ");

                if (c == '\r')
                    continue;
                if (c == '\n')
                {
                    offset.X = 0;
                    offset.Y += lineHeight;
                    yextent = offset.Y + lineHeight;
                    //Console.Write(" >> [" + i.ToString() + "]  newline is set.  lineHeight:" + lineHeight.ToString() + "   y offset:" + offset.Y.ToString() + " the y extent is " + yextent.ToString());
                    firstGlyphOfLine = true;
                    firstwordonline = true;
                    continue;
                }

                if (_glyphs.ContainsKey(c))
                    currentGlyph = _glyphs[c];
                else
                if (!tsf.DefaultCharacter.HasValue)
                    throw new ArgumentException("Text Contains a Unresolvable Character");
                else
                    currentGlyph = defaultGlyph;

                // Solves the problem- the first character on a line with a negative left side bearing.
                if (firstGlyphOfLine)
                {
                    offset.X = Math.Max(currentGlyph.LeftSideBearing, 0);
                    firstGlyphOfLine = false;
                }
                else
                    offset.X += Spacing + currentGlyph.LeftSideBearing;

                // matrix calculations unrolled varys max 8 mults and up to 10 add subs.
                var m = offset;
                m.X += currentGlyph.Cropping.X;
                m.Y += currentGlyph.Cropping.Y;

                dest = new Rectangle(
                    (int)(m.X * scale.X),
                    (int)(m.Y * scale.Y),
                    (int)(currentGlyph.BoundsInTexture.Width * scale.X),
                    (int)(currentGlyph.BoundsInTexture.Height * scale.Y)
                    );

                //Console.WriteLine("   >>> dest.Height " + dest.Height + "  , dest.Bottom " + dest.Bottom);

                // if char == a word break character white space
                if (c == ' ')
                {
                    lastWordBreakCharPos = i;
                    rewind = offset;
                    if (firstwordonline)
                        firstwordonline = false;
                }

                // Begin word wrapping calculations.
                if (yextent >= boundRect.Height)
                {
                    //Console.WriteLine(" >>  dest.Bottom " + dest.Bottom + " >= boundRect.Height " + boundRect.Height);
                    //Console.WriteLine(" >>  text.Length = i + 1; i = text.Length;");
                    text.Length = i;
                    i = text.Length;
                }
                else
                {
                    if (dest.Right > boundRect.Width)
                    {
                        if (text.Length > (i + 1))
                        {
                            if (firstwordonline == false)
                            {
                                if (text[lastWordBreakCharPos + 1] != '\n')
                                {
                                    //Console.WriteLine(" >>  (" + dest.Right + " > " + boundRect.Width + "),   text.Insert(lastWordBreakCharPos " + lastWordBreakCharPos + " + 1, newline); offset = rewind; i = lastWordBreakCharPos; ");
                                    text.Insert(lastWordBreakCharPos + 1, '\n');
                                    if (text[lastWordBreakCharPos + 1] != ' ')
                                    {
                                        offset = rewind;
                                        i = lastWordBreakCharPos;
                                    }
                                }
                            }
                            else // first word on the line true
                            {
                                if (text[i + 1] != '\n')
                                {
                                    //Console.WriteLine(" >>  text.Insert(i + 1, newline);'");
                                    text.Insert(i + 1, '\n');
                                }
                            }
                        }
                    }
                }
                offset.X += currentGlyph.Width + currentGlyph.RightSideBearing;
            }
            //return lastLineBreakCharPos;
        }

        /// <summary>
        /// This vesion copys to a new stringbuilder and then wraps it before returning it.
        /// </summary>
        public static StringBuilder WordWrapTextWithinBounds( StringBuilder textb, Vector2 scale, Rectangle boundRect)
        {
            var Spacing = tsf.Spacing;
            var lineHeight = tsf.LineSpacing * scale.Y;
            Vector2 offset = Vector2.Zero;
            float yextent = offset.Y + lineHeight;
            Vector2 redrawOffset = Vector2.Zero;
            Rectangle dest = new Rectangle();
            var currentGlyph = SpriteFont.Glyph.Empty;
            var firstGlyphOfLine = true;

            int lastWordBreakCharPos = 0;
            bool firstwordonline = true;
            Vector2 rewind = Vector2.Zero;

            //Console.WriteLine(" text.StringBuilder.Length " + text.StringBuilder.Length.ToString() + "  ");
            StringBuilder text = new StringBuilder();
            text.Append(textb);

            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                //Console.Write(" text[" + i + "]");
                //Console.Write(" = " + c + " ");

                if (c == '\r')
                    continue;
                if (c == '\n')
                {
                    offset.X = 0;
                    offset.Y += lineHeight;
                    yextent = offset.Y + lineHeight;
                    //Console.Write(" >> [" + i.ToString() + "]  newline is set.  lineHeight:" + lineHeight.ToString() + "   y offset:" + offset.Y.ToString() + " the y extent is " + yextent.ToString());
                    firstGlyphOfLine = true;
                    firstwordonline = true;
                    continue;
                }

                if (_glyphs.ContainsKey(c))
                    currentGlyph = _glyphs[c];
                else
                if (!tsf.DefaultCharacter.HasValue)
                    throw new ArgumentException("Text Contains a Unresolvable Character");
                else
                    currentGlyph = defaultGlyph;

                // Solves the problem- the first character on a line with a negative left side bearing.
                if (firstGlyphOfLine)
                {
                    offset.X = Math.Max(currentGlyph.LeftSideBearing, 0);
                    firstGlyphOfLine = false;
                }
                else
                    offset.X += Spacing + currentGlyph.LeftSideBearing;

                // matrix calculations unrolled varys max 8 mults and up to 10 add subs.
                var m = offset;
                m.X += currentGlyph.Cropping.X;
                m.Y += currentGlyph.Cropping.Y;

                dest = new Rectangle(
                    (int)(m.X * scale.X),
                    (int)(m.Y * scale.Y),
                    (int)(currentGlyph.BoundsInTexture.Width * scale.X),
                    (int)(currentGlyph.BoundsInTexture.Height * scale.Y)
                    );

                //Console.WriteLine("   >>> dest.Height " + dest.Height + "  , dest.Bottom " + dest.Bottom);

                // if char == a word break character white space
                if (c == ' ')
                {
                    lastWordBreakCharPos = i;
                    rewind = offset;
                    if (firstwordonline)
                        firstwordonline = false;
                }

                // Begin word wrapping calculations.
                if (yextent >= boundRect.Height)
                {
                    //Console.WriteLine(" >>  dest.Bottom " + dest.Bottom + " >= boundRect.Height " + boundRect.Height);
                    //Console.WriteLine(" >>  text.Length = i + 1; i = text.Length;");
                    text.Length = i;
                    i = text.Length;
                }
                else
                {
                    if (dest.Right > boundRect.Width)
                    {
                        if (text.Length > (i + 1))
                        {
                            if (firstwordonline == false)
                            {
                                if (text[lastWordBreakCharPos + 1] != '\n')
                                {
                                    //Console.WriteLine(" >>  (" + dest.Right + " > " + boundRect.Width + "),   text.Insert(lastWordBreakCharPos " + lastWordBreakCharPos + " + 1, newline); offset = rewind; i = lastWordBreakCharPos; ");
                                    text.Insert(lastWordBreakCharPos + 1, '\n');
                                    if (text[lastWordBreakCharPos + 1] != ' ')
                                    {
                                        offset = rewind;
                                        i = lastWordBreakCharPos;
                                    }
                                }
                            }
                            else // first word on the line true
                            {
                                if (text[i + 1] != '\n')
                                {
                                    //Console.WriteLine(" >>  text.Insert(i + 1, newline);'");
                                    text.Insert(i + 1, '\n');
                                }
                            }
                        }
                    }
                }
                offset.X += currentGlyph.Width + currentGlyph.RightSideBearing;
            }
            return text;
        }      
    }  
}

Here’s a class I almost always find I need…

XNA provides you Point and Vector2, int and float versions of coordinate data; however, they give you no float equivalent of Rectangle. I usually end up making a RectangleF and SizeF object for my games. Maybe others will find these useful too :slight_smile:

Oh, it’s worth noting that a SpriteBatch can be easily extended to take a RectangleF as an alternative to Rectangle. Simply use it to calculate the scale and then use the appropriate SpriteBatch.Draw overload.

SizeF

using Microsoft.Xna.Framework;

namespace ArbitraryPixel.Common.Drawing
{
    /// <summary>
    /// Represents a size structure, containing width and height.
    /// 
    /// This object is intended to reproduce the behaviour of System.Drawing.Size and
    /// Sytem.Drawing.SizeF, except use doubles for its internal storage.
    /// </summary>
    public struct SizeF
    {
        #region Private Members
        private float _width;
        private float _height;
        #endregion

        #region Static Members
        /// <summary>
        /// Represents an empty MTPSize object, where the width and height are zero.
        /// </summary>
        public static readonly SizeF Empty;
        #endregion

        #region Constructor(s)
        /// <summary>
        /// Constructs a new object.
        /// </summary>
        /// <param name="width">The desired width.</param>
        /// <param name="height">The desired height.</param>
        public SizeF(float width, float height)
        {
            _width = width;
            _height = height;
        }

        /// <summary>
        /// Constructs a new object.
        /// </summary>
        /// <param name="value">The value that both width and height will be set to.</param>
        public SizeF(float value)
        {
            _width = value;
            _height = value;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// The width for this size object.
        /// </summary>
        public float Width
        {
            get { return _width; }
            set { _width = value; }
        }

        /// <summary>
        /// The height for this object.
        /// </summary>
        public float Height
        {
            get { return _height; }
            set { _height = value; }
        }

        /// <summary>
        /// Get a point representing the centre point of this SizeF object.
        /// </summary>
        public Vector2 Centre
        {
            get { return new Vector2(_width / 2f, _height / 2f); }
        }
        #endregion

        #region Operators and Overrides
        /// <summary>
        /// Compare two MTPSize objects for equality.
        /// </summary>
        /// <param name="left">The left hand side of the comparison.</param>
        /// <param name="right">The right hand side of the comparison.</param>
        /// <returns>True if the left and right are equivalent; that is, they have equal values. False otherwise.</returns>
        public static bool operator ==(SizeF left, SizeF right)
        {
            return (left.Width == right.Width && left.Height == right.Height);
        }

        /// <summary>
        /// Compare two MTPSize objects for inequality.
        /// </summary>
        /// <param name="left">The left hand side of the comparison.</param>
        /// <param name="right">The right hand side of the comparison.</param>
        /// <returns>True if the left and right side are not equivalent; that is, they have different values. False otherwise.</returns>
        public static bool operator !=(SizeF left, SizeF right)
        {
            return !(left == right);
        }

        /// <summary>
        /// Creates a string representation of this object.
        /// </summary>
        /// <returns>The string representation of this object.</returns>
        public override string ToString()
        {
            // This is based on the string format of System.Drawing.Size and System.Drawing.SizeF.
            return string.Format("{{Width={0}, Height={1}}}", _width, _height);
        }

        /// <summary>
        /// Checks to see if an object is equal to this object.
        /// </summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>True if the supplied object is equal to this one, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            bool isEqual = false;

            if (obj is SizeF)
            {
                isEqual = ((SizeF)obj) == this;
            }

            return isEqual;
        }

        /// <summary>
        /// Get the hash code for this object.
        /// </summary>
        /// <returns>The hash code for this object.</returns>
        public override int GetHashCode()
        {
            int hash = 17;
            unchecked
            {
                hash = hash * 29 + _width.GetHashCode();
                hash = hash * 29 + _height.GetHashCode();
            }

            return hash;
        }

        /// <summary>
        /// Cast a SizeF object to a Vector2 object.
        /// </summary>
        /// <param name="size">The SizeF object to cast.</param>
        /// <returns>The SizeF object as an equivalent Vector2 object.</returns>
        public static implicit operator Vector2(SizeF size)
        {
            return new Vector2(size.Width, size.Height);
        }

        /// <summary>
        /// Cast a Vector2 object to a SizeF object.
        /// </summary>
        /// <param name="vector">The Vector2 object to cast.</param>
        /// <returns>The Vector2 object as an equivalent SizeF object.</returns>
        public static explicit operator SizeF(Vector2 vector)
        {
            return new SizeF(vector.X, vector.Y);
        }

        /// <summary>
        /// Cast a SizeF object to a Point object.
        /// </summary>
        /// <param name="size">The SizeF object to cast.</param>
        /// <returns>The SizeF object as an equivalent Point object.</returns>
        public static explicit operator Point(SizeF size)
        {
            return new Point((int)size.Width, (int)size.Height);
        }

        /// <summary>
        /// Cast a Point object to a SizeF object.
        /// </summary>
        /// <param name="p">The Point object to cast.</param>
        /// <returns>The Point object as an equivalent SizeF object.</returns>
        public static explicit operator SizeF(Point p)
        {
            return new SizeF(p.X, p.Y);
        }

        /// <summary>
        /// Multiply a SizeF object by a float.
        /// </summary>
        /// <param name="lhs">The SizeF object.</param>
        /// <param name="rhs">The float.</param>
        /// <returns>A SizeF object that is the result of the multiplication of the float by both the Width and Height. Effectively a scale operation.</returns>
        public static SizeF operator *(SizeF lhs, float rhs)
        {
            return new SizeF(lhs.Width * rhs, lhs.Height * rhs);
        }

        /// <summary>
        /// Multiply a SizeF object by another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the product of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator *(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width * rhs.Width, lhs.Height * rhs.Height);
        }

        /// <summary>
        /// Divide a SizeF object by a float.
        /// </summary>
        /// <param name="lhs">The SizeF object.</param>
        /// <param name="rhs">The float.</param>
        /// <returns>A new SizeF object that is the result of the division by the float of the Width and the Height of the SizeF object.</returns>
        public static SizeF operator /(SizeF lhs, float rhs)
        {
            return new SizeF(lhs.Width / rhs, lhs.Height / rhs);
        }

        /// <summary>
        /// Divide a SizeF object by another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the quotient of the supplied object's Width and Height respectively.</returns>
        public static SizeF operator /(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width / rhs.Width, lhs.Height / rhs.Height);
        }

        /// <summary>
        /// Add a SizeF object with another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the sum of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator +(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width + rhs.Width, lhs.Height + rhs.Height);
        }

        /// <summary>
        /// Sbtract a SizeF object from another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SIzeF object where the Width and Height are equal to the difference of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator -(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width - rhs.Width, lhs.Height - rhs.Height);
        }
        #endregion
    }
}

RectangleF

using Microsoft.Xna.Framework;
using System;

namespace ArbitraryPixel.Common.Drawing
{
    /// <summary>
    /// Represents a rectangle structure, having both a location, as well a width and height.
    /// The width and height of a rectangle are relative to the location; therefore, they can
    /// be negative.
    /// 
    /// This object is intended to reproduce the behaviour of System.Drawing.Rectangle and
    /// Sytem.Drawing.RectangleF, except use doubles for its internal storage.
    /// </summary>
    public struct RectangleF
    {
        #region Private Members
        private Vector2 _location;
        private SizeF _size;
        #endregion

        #region Constructor(s)
        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="location">The location of this rectangle.</param>
        /// <param name="size">The size of this rectangle.</param>
        public RectangleF(Vector2 location, SizeF size)
        {
            _location = location;
            _size = size;
        }

        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="p1">The first corner of this rectangle.</param>
        /// <param name="p2">The second corner of this rectangle.</param>
        public RectangleF(Vector2 p1, Vector2 p2)
            : this(p1, new SizeF(p2.X - p1.X, p2.Y - p1.Y))
        {
        }

        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="x">The X coordinate of the location.</param>
        /// <param name="y">The Y coordinate of the location.</param>
        /// <param name="width">The width of this rectangle.</param>
        /// <param name="height">The height of this rectangle.</param>
        public RectangleF(float x, float y, float width, float height)
            : this(new Vector2(x, y), new SizeF(width, height))
        {
        }
        #endregion

        #region Static Members
        /// <summary>
        /// Represents an empty rectangle, where the location and size are initialized to zero.
        /// </summary>
        public static readonly RectangleF Empty;
        #endregion

        #region Public Properties
        /// <summary>
        /// The location of this rectangle.
        /// </summary>
        public Vector2 Location
        {
            get { return _location; }
            set { _location = value; }
        }

        /// <summary>
        /// The size of this rectangle.
        /// </summary>
        public SizeF Size
        {
            get { return _size; }
            set { _size = value; }
        }

        /// <summary>
        /// The X coordinate of this rectangle's location.
        /// </summary>
        public float X
        {
            get { return _location.X; }
            set { _location.X = value; }
        }

        /// <summary>
        /// The Y coordinate of this rectangle's location.
        /// </summary>
        public float Y
        {
            get { return _location.Y; }
            set { _location.Y = value; }
        }

        /// <summary>
        /// The width of this rectangle.
        /// </summary>
        public float Width
        {
            get { return _size.Width; }
            set { _size.Width = value; }
        }

        /// <summary>
        /// The height of this rectangle.
        /// </summary>
        public float Height
        {
            get { return _size.Height; }
            set { _size.Height = value; }
        }

        /// <summary>
        /// The location of this rectangle's opposite corner (relative to its location).
        /// </summary>
        public Vector2 OppositeCorner
        {
            get { return new Vector2(_location.X + _size.Width, _location.Y + _size.Height); }
        }

        /// <summary>
        /// The Y coordinate for this rectangle's top edge.
        /// </summary>
        public float Top { get { return this.Y; } }

        /// <summary>
        /// The X coordinate for this rectangle's left edge.
        /// </summary>
        public float Left { get { return this.X; } }

        /// <summary>
        /// The X coordinate for this rectangle's right edge.
        /// </summary>
        public float Right { get { return this.X + this.Width; } }

        /// <summary>
        /// The Y coordinate for this rectangle's bottom edge.
        /// </summary>
        public float Bottom { get { return this.Y + this.Height; } }

        /// <summary>
        /// The centre point of this rectangle.
        /// </summary>
        public Vector2 Centre
        {
            get
            {
                return new Vector2(this.Left + this.Width / 2f, this.Top + this.Height / 2f);
            }
        }
        #endregion

        #region Operators and Overloads
        /// <summary>
        /// Compare two rectangles for equality.
        /// </summary>
        /// <param name="left">The left rectangle.</param>
        /// <param name="right">The right rectangle.</param>
        /// <returns>True if both rectangles have the same location and size, false otherwise.</returns>
        public static bool operator ==(RectangleF left, RectangleF right)
        {
            Vector2 aMin = new Vector2(Math.Min(left.Left, left.Right), Math.Min(left.Top, left.Bottom));
            Vector2 aMax = new Vector2(Math.Max(left.Left, left.Right), Math.Max(left.Top, left.Bottom));

            Vector2 bMin = new Vector2(Math.Min(right.Left, right.Right), Math.Min(right.Top, right.Bottom));
            Vector2 bMax = new Vector2(Math.Max(right.Left, right.Right), Math.Max(right.Top, right.Bottom));

            return (aMin == bMin && aMax == bMax);
        }

        /// <summary>
        /// Compare two rectangles for inequality.
        /// </summary>
        /// <param name="left">The left rectangle.</param>
        /// <param name="right">The right rectangle.</param>
        /// <returns>True if both rectangles have different locations or sizes, false otherwise.</returns>
        public static bool operator !=(RectangleF left, RectangleF right)
        {
            return !(left == right);
        }

        /// <summary>
        /// Creates a string representation of this object.
        /// </summary>
        /// <returns>The string representation of this object.</returns>
        public override string ToString()
        {
            // This is based on the string format of System.Drawing.Rectangle and System.Drawing.RectangleF.
            return string.Format("{{X={0}, Y={1}, Width={2}, Height={3}}}", this.X, this.Y, this.Width, this.Height);
        }

        /// <summary>
        /// Checks to see if an object is equal to this object.
        /// </summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>True if the supplied object is equal to this one, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            bool isEqual = false;

            if (obj is RectangleF)
            {
                isEqual = ((RectangleF)obj) == this;
            }

            return isEqual;
        }

        /// <summary>
        /// Get the hash code for this object.
        /// </summary>
        /// <returns>The hash code for this object.</returns>
        public override int GetHashCode()
        {
            int hash = 17;
            unchecked
            {
                hash = hash * 29 + _location.GetHashCode();
                hash = hash * 29 + _size.GetHashCode();
            }

            return hash;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Adjust the edges of this rectangle by the specified amounts.
        /// </summary>
        /// <param name="horizontalAmount">The amount to adjust the left and right edges.</param>
        /// <param name="verticalAmount">The amount to adjust the top and buttom edges.</param>
        public void Inflate(float horizontalAmount, float verticalAmount)
        {
            this.X -= horizontalAmount;
            this.Width += horizontalAmount * 2; ;

            this.Y -= verticalAmount;
            this.Height += verticalAmount * 2;
        }

        /// <summary>
        /// Tests to see if a point is contained within this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="point">The point to test.</param>
        /// <returns>True if the point is inside the rectangle, false otherwise.</returns>
        public bool Contains(Vector2 point)
        {
            Vector2 min = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 max = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            return (point.X >= min.X && point.X < max.X && point.Y >= min.Y && point.Y < max.Y);
        }

        /// <summary>
        /// Tests to see if a point is contained within this rectangle.
        /// </summary>
        /// <param name="x">The X coordinate of the point.</param>
        /// <param name="y">The Y coordinate of the point.</param>
        /// <returns>True if the point is inside the rectangle, false otherwise.</returns>
        public bool Contains(float x, float y)
        {
            return this.Contains(new Vector2(x, y));
        }

        /// <summary>
        /// Tests to see if a rectangle is contained within this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="rect">The rectangle to test.</param>
        /// <returns>True if the supplied rectangle is contained by this object, false otherwise.</returns>
        public bool Contains(RectangleF rect)
        {
            Vector2 thisMin = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 thisMax = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            Vector2 rectMin = new Vector2(Math.Min(rect.Left, rect.Right), Math.Min(rect.Top, rect.Bottom));
            Vector2 rectMax = new Vector2(Math.Max(rect.Left, rect.Right), Math.Max(rect.Top, rect.Bottom));

            return (rectMin.X >= thisMin.X && rectMax.X <= thisMax.X && rectMin.Y >= thisMin.Y && rectMax.Y <= thisMax.Y);
        }

        /// <summary>
        /// Tests to see if a rectangle intersects with this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="rect">The rectangle to test.</param>
        /// <returns>True if this object and the supplied rectangle intersect, false otherwise.</returns>
        public bool IntersectsWith(RectangleF rect)
        {
            Vector2 thisMin = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 thisMax = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            Vector2 rectMin = new Vector2(Math.Min(rect.Left, rect.Right), Math.Min(rect.Top, rect.Bottom));
            Vector2 rectMax = new Vector2(Math.Max(rect.Left, rect.Right), Math.Max(rect.Top, rect.Bottom));

            return !((rectMax.X <= thisMin.X || rectMin.X >= thisMax.X) || (rectMin.Y >= thisMax.Y || rectMax.Y <= thisMin.Y));
        }


        /// <summary>
        /// Generate a new rectangle that is the intersection of two rectangles; that is, the portion
        /// common to both rectangles (overlap).
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height values are negative.
        /// </summary>
        /// <param name="a">The first rectangle.</param>
        /// <param name="b">The second rectangle.</param>
        /// <returns>A rectangle representing the common area of the two rectangles.</returns>
        public static RectangleF Intersect(RectangleF a, RectangleF b)
        {
            RectangleF result = RectangleF.Empty;

            if (a.IntersectsWith(b))
            {
                Vector2 aMin = new Vector2(Math.Min(a.Left, a.Right), Math.Min(a.Top, a.Bottom));
                Vector2 aMax = new Vector2(Math.Max(a.Left, a.Right), Math.Max(a.Top, a.Bottom));

                Vector2 bMin = new Vector2(Math.Min(b.Left, b.Right), Math.Min(b.Top, b.Bottom));
                Vector2 bMax = new Vector2(Math.Max(b.Left, b.Right), Math.Max(b.Top, b.Bottom));

                result.X = Math.Max(aMin.X, bMin.X);
                result.Y = Math.Max(aMin.Y, bMin.Y);
                result.Width = Math.Min(aMax.X, bMax.X) - result.X;
                result.Height = Math.Min(aMax.Y, bMax.Y) - result.Y;
            }

            return result;
        }

        /// <summary>
        /// Generate a new rectangle that is the union of two rectangles; that is, the
        /// rectangle that would minimally contain both supplied rectangles.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height values are negative.
        /// </summary>
        /// <param name="a">The first rectangle.</param>
        /// <param name="b">The second rectangle.</param>
        /// <returns>A rectangle representing the union of the two rectangles.</returns>
        public static RectangleF Union(RectangleF a, RectangleF b)
        {
            RectangleF result = RectangleF.Empty;

            Vector2 aMin = new Vector2(Math.Min(a.Left, a.Right), Math.Min(a.Top, a.Bottom));
            Vector2 aMax = new Vector2(Math.Max(a.Left, a.Right), Math.Max(a.Top, a.Bottom));

            Vector2 bMin = new Vector2(Math.Min(b.Left, b.Right), Math.Min(b.Top, b.Bottom));
            Vector2 bMax = new Vector2(Math.Max(b.Left, b.Right), Math.Max(b.Top, b.Bottom));

            result.X = Math.Min(aMin.X, bMin.X);
            result.Y = Math.Min(aMin.Y, bMin.Y);
            result.Width = Math.Max(aMax.X, bMax.X) - result.X;
            result.Height = Math.Max(aMax.Y, bMax.Y) - result.Y;

            return result;
        }
        #endregion

        #region Casting
        /// <summary>
        /// Cast a RectangleF object to a Rectangle object.
        /// </summary>
        /// <param name="rect">The RectangleF object to cast.</param>
        /// <returns>A Rectangle object which is equivalent to the supplied RectangleF object.</returns>
        public static explicit operator Rectangle(RectangleF rect)
        {
            return new Rectangle((int)Math.Floor(rect.X), (int)Math.Floor(rect.Y), (int)Math.Floor(rect.Width), (int)Math.Floor(rect.Height));
        }

        /// <summary>
        /// Cast a Rectangle object to a RectangleF object.
        /// </summary>
        /// <param name="rect">The Rectangle object to cast.</param>
        /// <returns>A RectangleF object which is equivalent to the supplied Rectangle object.</returns>
        public static explicit operator RectangleF(Rectangle rect)
        {
            return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
        }
        #endregion
    }
}

Ok here is a few that im suprised people don’t ask for much around here.
I guess algorithms are a dime a dozen now days.

Heres a few 2d ones.

Intercept vector to a moving target.

        /// <summary>
        /// Direction to fire in order to intercept a target.
        /// </summary>
        public static Vector2   QuadricIntercept(Vector2 obj_position, float obj_speed, Vector2 target_position, Vector2 target_velocity)
        {
            float tvx = target_velocity.X;
            float tvy = target_velocity.Y;
            float pdx = target_position.X - obj_position.X;
            float pdy = target_position.Y - obj_position.Y;

            float pdlength = Vector2.Normalize(target_position - obj_position).Length();
            float d = pdx * pdx + pdy * pdy;
            float s = (tvx * tvx + tvy * tvy) - obj_speed * obj_speed;
            float q = (tvx * pdx + tvy * pdy);
            //float sd = ((tvx * tvx + tvy * tvy) - obj_speed * obj_speed) * (tvx * pdx + tvy * pdy);
            float disc = (q * q) - s * d; // get rid of the fluff
            float disclen = (float)Math.Sqrt(disc);

            float t1 = (-q + disclen) / s;
            float t2 = (-q - disclen) / s;

            float t = t1;
            if (t1 < 0.0f) { t = t2; }

            Vector2 aimpoint = Vector2.Zero;
            if (t > 0.0f)
            {
                aimpoint.X = t * tvx + target_position.X;
                aimpoint.Y = t * tvy + target_position.Y;
            }
            return aimpoint; // returns Vector2.Zero if no positive time to fire exists
        }

line to line collision

        public static bool LineSegementsIntersect(Vector2 p1, Vector2 p2, Vector2 q1, Vector2 q2, out Vector2 intersection)
        {
            intersection = new Vector2();

            var r_pdif = p2 - p1;
            var s_qdiff = q2 - q1;
            var rxs = Cross(r_pdif, s_qdiff);
            var qpxr = Cross((q1 - p1), r_pdif);

            // If r x s = 0 and (q - p) x r = 0, then the two lines are collinear.
            if (rxs == 0 && qpxr == 0)
            {
                return false;
            }

            // 3. If r x s = 0 and (q - p) x r != 0, then the two lines are parallel and non-intersecting.
            if (rxs == 0 && !(qpxr == 0))
                return false;

            // t = (q - p) x s / (r x s)
            var t = Cross((q1 - p1), s_qdiff) / rxs;
            // u = (q - p) x r / (r x s)
            var u = Cross((q1 - p1), r_pdif) / rxs;

            // 4. If r x s != 0 and 0 <= t <= 1 and 0 <= u <= 1
            // the two line segments meet at the point p + t r = q + u s.
            if (!(rxs == 0) && (0 <= t && t <= 1) && (0 <= u && u <= 1))
            {
                // We can calculate the intersection point using either t or u.
                intersection = p1 + t * r_pdif;
                return true;
            }
            // 5. Otherwise, the two line segments are not parallel but do not intersect.
            return false;
        }
        public static float Cross(Vector2 v, Vector2 v2)
        {
            return v.X * v2.Y - v.Y * v2.X;
        }

which way do you turn to get to a target.

        /// <summary>
        /// direction and to target should be normalized
        /// -1 means the target is counterclockwise +1 means target is clockwise.
        /// </summary>
        public static int TurnToTargetLeftOrRight(Vector2 from_direction, Vector2 to_target)
        {
            to_target.Normalize();
            float r = -from_direction.Y * to_target.X + from_direction.X * to_target.Y;
            float f = from_direction.X * to_target.X + from_direction.Y * to_target.Y;
            if (f > .999f) { return 0; }
            if (r < 0f) { return -1; }
            if (r > 0) { return 1; }
            return 0;
        }

Create a checkerboard algorithmically, just made this one earlier this week.
I make the same stuff over and over and lose it lol

        public static Texture2D CreateCheckerBoard(GraphicsDevice device, int w, int h, Color c0, Color c1)
        {
            Color[] data = new Color[w * h];
            for (int x = 0; x < w; x++)
            {
                for (int y = 0; y < h; y++)
                {
                    int index = y * w + x;
                    Color c = c0;
                    if ((y % 2 == 0))
                    {
                        if ((x % 2 == 0))
                            c = c0;
                        else
                            c = c1;
                    }
                    else
                    {
                        if ((x % 2 == 0))
                            c = c1;
                        else
                            c = c0;
                    }
                    data[index] = c;
                }
            }
            return TextureFromColorArray(device, data, w, h);
        }
        public static Texture2D TextureFromColorArray(GraphicsDevice device, Color[] data, int width, int height)
        {
            Texture2D tex = new Texture2D(device, width, height);
            tex.SetData<Color>(data);
            return tex;
        }

Here is a gist with two and I’m planning on releasing a wrapper/layer of sorts to allow for use of Geometry Shader, Hull Shader, Domain Shader, Compute Shader etc.

Enum utility for getting and caching the arrays of values and names:

/// <summary>
/// Enum utility class.
/// </summary>
public static class EnumUtility
{
    /// <summary>
    /// Gets the values for an Enum of a particular type in an array and caches it.
    /// </summary>
    /// <typeparam name="T">The Enum type.</typeparam>
    public static class GetValues<T> where T: Enum
    {
        /// <summary>
        /// The cached enum array containing all the values for the Enum type.
        /// </summary>
        public static T[] EnumValues { get; private set; } = null;

        static GetValues()
        {
            EnumValues = (T[])Enum.GetValues(typeof(T));
        }
    }

    /// <summary>
    /// Gets the names for an Enum of a particular type in an array and caches it.
    /// </summary>
    /// <typeparam name="T">The Enum type.</typeparam>
    public static class GetNames<T> where T: Enum
    {
        /// <summary>
        /// The cached string array containing all the names in the Enum type.
        /// </summary>
        public static string[] EnumNames { get; private set; } = null;

        static GetNames()
        {
            EnumNames = Enum.GetNames(typeof(T));
        }
    }
}

Bouncing a value between 0 and a max value. The second overload bounces it between a min and max value:

/// <summary>
/// Bounces a value between 0 and a max value.
/// </summary>
/// <param name="time">The time value.</param>
/// <param name="maxVal">The max value.</param>
/// <returns>A double with a value between 0 and <paramref name="maxVal"/>.</returns>
public static double PingPong(double time, double maxVal)
{
    double lengthTimesTwo = maxVal * 2d;
    double timeMod = time % lengthTimesTwo;

    if (timeMod >= 0 && timeMod < maxVal)
        return timeMod;
    else
        return lengthTimesTwo - timeMod;
}

/// <summary>
/// Bounces a value between a min and a max value.
/// </summary>
/// <param name="time">The time value.</param>
/// <param name="minVal">The min value.</param>
/// <param name="maxVal">The max value.</param>
/// <returns>A float with a value between <paramref name="minVal"/> and <paramref name="maxVal"/>.</returns>
public static double PingPong(double time, double minVal, double maxVal)
{
    return PingPong(time, maxVal - minVal) + minVal;
}

Simple Circle struct:

/// <summary>
/// A structure representing a Circle.
/// </summary>
public struct Circle
{
    private static readonly Circle emptyCircle = new Circle(Vector2.Zero, 0f);

    /// <summary>
    /// The center point of the Circle.
    /// </summary>
    public Vector2 Center;

    /// <summary>
    /// The radius of the Circle.
    /// </summary>
    public double Radius;

    /// <summary>
    /// Tells if the Circle is empty.
    /// </summary>
    public bool IsEmpty => (Center == Vector2.Zero && Radius == 0f);

    /// <summary>
    /// Returns an empty Circle at the origin with a radius of 0.
    /// </summary>
    public static ref readonly Circle Empty => ref emptyCircle;

    /// <summary>
    /// Creates a new instance of a Circle with a specified center point and radius.
    /// </summary>
    /// <param name="x">The X point of the Circle.</param>
    /// <param name="y">The Y point of the Circle.</param>
    /// <param name="radius">The radius of the Circle.</param>
    public Circle(float x, float y, double radius) : this(new Vector2(x, y), radius)
    {

    }

    /// <summary>
    /// Creates a new instance of a Circle with a specified center point and radius.
    /// </summary>
    /// <param name="center">The center of the Circle.</param>
    /// <param name="radius">The radius of the Circle.</param>
    public Circle(Vector2 center, double radius)
    {
        Center = center;
        Radius = radius;
    }

    /// <summary>
    /// Tells if this Circle intersects another Circle.
    /// </summary>
    /// <param name="other">The Circle to test intersection with.</param>
    /// <returns>true if the sum of the radii squared is less than or equal to the distance between the circles squared.</returns>
    public bool Intersects(Circle other)
    {
        double radiusSquared = Math.Pow(Radius + other.Radius, 2);
        double distance = Vector2.DistanceSquared(Center, other.Center);

        return (distance <= radiusSquared);
    }

    /// <summary>
    /// Tells if the Circle contains a Vector2.
    /// </summary>
    /// <param name="value">The Vector2 to test.</param>
    /// <returns>true if <paramref name="value"/> is contained in the Circle, otherwise false.</returns>
    public bool Contains(Vector2 value)
    {
        return (Intersects(new Circle(value, 0d)));
    }

    /// <summary>
    /// Tells if the Circle contains a Point.
    /// </summary>
    /// <param name="value">The Point to test.</param>
    /// <returns>true if <paramref name="value"/> is contained in the Circle, otherwise false.</returns>
    public bool Contains(Point value)
    {
        return Contains(new Vector2(value.X, value.Y));
    }

    /// <summary>
    /// Gets a point around the Circle at a particular angle.
    /// </summary>
    /// <param name="angle">The angle, in radians.</param>
    /// <returns>A Vector2 with the X and Y components at the location around the circle.</returns>
    public Vector2 GetPointAround(double angle)
    {
        float x = (float)(Math.Cos(angle) * Radius) + Center.X;
        float y = (float)(Math.Sin(angle) * Radius) + Center.Y;

        return new Vector2(x, y);
    }

    #region Comparison and Operator Overloading

    public override bool Equals(object obj)
    {
        return (obj is Circle) && (this == (Circle)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 18;
            hash = (hash * 37) + Center.GetHashCode();
            hash = (hash * 37) + Radius.GetHashCode();
            return hash;
        }
    }

    public static bool operator ==(Circle a, Circle b)
    {
        return (a.Center == b.Center && a.Radius == b.Radius);
    }

    public static bool operator !=(Circle a, Circle b)
    {
        return (a.Center != b.Center || a.Radius != b.Radius);
    }

    public override string ToString()
    {
        return Center.ToString() + " Radius: " + Radius;
    }

    #endregion
}
1 Like

I find it interesting that your segment intersection function returns false if they’re collinear. I have one that returns the earliest intersection, but I guess it depends what the application requires.

I didn’t write that one or the intercept. i got those two off some c comp.lang algorithm site long ago and converted them, i might have made that change i cant remember.

I might as well copy this here. This is like a super easy sky Sphere and then some.
You basically dont have to do anything but drop a picture to it and call draw.

I better put these here before i lose them as this is a good spot for reference.
Besides someone will probably need them.

This one is requisite atm for a render target cube.
Its just the monogame CreateLookAt but switched over to be left handed.

        /// <summary>
        /// Creates a inverted monogame CreateLookAt or a left handed matrix.
        /// This returns a matrix suitable for a render target cube.
        /// </summary>
        public static Matrix CreateLookAtLeftHanded(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
        {
            var vector = Vector3.Normalize(cameraPosition - cameraTarget);
            var vector2 = -Vector3.Normalize(Vector3.Cross(cameraUpVector, vector));
            var vector3 = Vector3.Cross(-vector, vector2);
            Matrix result = Matrix.Identity;
            result.M11 = vector2.X;
            result.M12 = vector3.X;
            result.M13 = vector.X;
            result.M14 = 0f;
            result.M21 = vector2.Y;
            result.M22 = vector3.Y;
            result.M23 = vector.Y;
            result.M24 = 0f;
            result.M31 = vector2.Z;
            result.M32 = vector3.Z;
            result.M33 = vector.Z;
            result.M34 = 0f;
            result.M41 = -Vector3.Dot(vector2, cameraPosition);
            result.M42 = -Vector3.Dot(vector3, cameraPosition);
            result.M43 = -Vector3.Dot(vector, cameraPosition);
            result.M44 = 1f;
            return result;
        }

This one is needed for the camera when reflection on a surface plane is to occur such as with mirroring water.
This one is courtesy of markus mine was bugged.

        public static Vector3 InflectPositionFromPlane(Vector3 theCameraPostion, Vector3 thePlanesSurfaceNormal, Vector3 anyPositionOnThatSurfacePlane)
        {
            float camToPlaneDist = Vector3.Dot(thePlanesSurfaceNormal, theCameraPostion - anyPositionOnThatSurfacePlane);
            return theCameraPostion - thePlanesSurfaceNormal * camToPlaneDist * 2;
        }

This concerns screen projection such as when you want to do object or triangle picking in your world.
They project from screen to world rays. I just want to put this here because i added a nice exception to it that says it all.

        public Ray GetScreenVector2AsRayInto3dWorld(Vector2 screenPosition, Matrix projectionMatrix, Matrix viewMatrix, Matrix cameraWorld, float near, float far, GraphicsDevice device)
        {
            if (far > 1.0f)
                throw new ArgumentException("Far Plane can't be more then 1f or this function will fail to work in many cases");
            Vector3 nearScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, near); // must be more then zero.
            Vector3 nearWorldPoint = Unproject(nearScreenPoint, projectionMatrix, viewMatrix, Matrix.Identity, device);

            Vector3 farScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, far); // the projection matrice's far plane value.
            Vector3 farWorldPoint = Unproject(farScreenPoint, projectionMatrix, viewMatrix, Matrix.Identity, device);

            Vector3 worldRaysNormal = Vector3.Normalize((farWorldPoint + nearWorldPoint) - nearWorldPoint);
            return new Ray(nearWorldPoint, worldRaysNormal);
        }

@willmotil

Why didn’t you just scale after a regular Matrix.CreateLookAt? Duplicating code for that is a bit nuts and more importantly you only have to do anything to the projection matrix, it doesn’t make any semantic sense to do that to the view-matrix.

ie. leftHandedProjectionMatrix = projectionMatrix * Matrix.CreateScale(-1.0f, 1.0f, 1.0f)

Left-handed projection matrix (code I’ve used sine day-1):

public void SetToPerspective(GraphicsDevice graphicsDevice, float fov)
{
    _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
        MathHelper.ToRadians(fov),
        (float)graphicsDevice.Viewport.Width /
        (float)graphicsDevice.Viewport.Height,
        0.1f, 500.0f) * Matrix.CreateScale(-1.0f, 1.0f, 1.0f); // flip x-axis to the one-true coordinate system
}

Duplicating code for that is a bit nuts
its just copy pasted from the current one with two added minus signs.

I didn’t try fliping the projection matrix first, so thats why i fliped the lookat.
I didn’t think it made much difference ? I suppose thats simpler.

I think the real question is. Does it make sense that you need a left handed matrix for a rendertargetcube in monogame without even so much as a comment in the class to let you know ? I wasn’t to thrilled with that headache Which is also the point of posting it.

Yes, it’s moot as far as end-result goes … it is anything but moot semantically.

The projection matrix is your final mapping to clip-space and thus the output, performing the conversion anywhere else is a dangerous semantic to ignore. It is the end of the line, converting part-way down the line is never a good idea.


I think the real question is. Does it make sense that you need a left handed matrix for a rendertargetcube in monogame without even so much as a comment in the class to let you know ?

But it is obvious. The layout of cubes is strictly defined and documented sufficiently by the graphics APIs. It’s not really any API/library’s fault that you didn’t read the specifications first, especially one as raw as the XNA/FNA/Monogame family.

Duplicating code for that is a bit nuts
its just copy pasted from the current one with two added minus signs.

Which is the nuts part.

Well i don’t think it’s obvious for a framework that uses a right handed coordinate system.
but whatever.

Edit:
Acid i tried pluging that in and i cant seem to get it to work, but if you want to disscuss it further or toss up a working example you could make a comment here.

I don’t wish to discuss it further. I’m not interested in wasting time.

Code posted is working and shipped.