NUGET-Changes:
+ Materials now have a method to draw. That should allow more freedom on how materials can have a effect on the resulting shader.

~ PostProcessEffect.Draw now needs a source ColorAttachment.
~ Added some missing summaries

GIT-/SOLUTION-Changes:
Remade the folder structure, to something more senseable.
This commit is contained in:
Michel Fedde 2021-05-14 21:38:50 +02:00
parent db7f01dca1
commit 89de4258e1
181 changed files with 584 additions and 698 deletions

View file

@ -0,0 +1,118 @@
#region usings
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Scene;
using SM.Base.Window;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Contains general basis systems for drawing objects.
/// </summary>
public abstract class DrawingBasis : IShowItem, IModelItem
{
/// <summary>
/// The camera, that was used last time the object was rendered.
/// </summary>
public GenericCamera LastDrawingCamera { get; private set; }
/// <summary>
/// The material it should use.
/// </summary>
public Material Material { get; set; } = new Material();
/// <summary>
/// Transformation for the textures.
/// </summary>
public TextureTransformation TextureTransform { get; set; } = new TextureTransformation();
/// <summary>
/// This allows custom shaders to add own arguments.
/// </summary>
public ShaderArguments ShaderArguments => Material.ShaderArguments;
/// <summary>
/// This can force a shader to render the object with the specified mesh type.
/// </summary>
public PrimitiveType? ForcedMeshType { get; set; }
/// <summary>
/// The mesh it should use.
/// </summary>
public GenericMesh Mesh { get; set; } = SMRenderer.DefaultMesh;
/// <inheritdoc />
public object Parent { get; set; }
/// <inheritdoc />
public string Name { get; set; } = "Unnamed draw object";
/// <inheritdoc />
public ICollection<string> Flags { get; set; }
/// <summary>
/// This value determents if the object should draw something.
/// </summary>
public bool Active { get; set; } = true;
/// <inheritdoc />
public bool RenderActive { get; set; } = true;
/// <inheritdoc />
public void Draw(DrawContext context)
{
context.Material = Material;
context.Mesh = Mesh;
DrawContext(ref context);
}
/// <inheritdoc />
public virtual void OnAdded(object sender)
{
}
/// <inheritdoc />
public virtual void OnRemoved(object sender)
{
}
/// <summary>
/// Draws the context, that was given to them.
/// </summary>
/// <param name="context"></param>
protected virtual void DrawContext(ref DrawContext context)
{
context.ForcedType = ForcedMeshType;
context.TextureMatrix *= TextureTransform.GetMatrix();
context.LastObject = this;
LastDrawingCamera = context.UseCamera;
}
}
/// <summary>
/// Contains general basis systems for drawing objects.
/// </summary>
/// <typeparam name="TTransformation">The transformation type</typeparam>
public abstract class DrawingBasis<TTransformation> : DrawingBasis, IShowTransformItem<TTransformation>
where TTransformation : GenericTransformation, new()
{
/// <summary>
/// The current transformation.
/// </summary>
public TTransformation Transform { get; set; } = new TTransformation();
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
base.DrawContext(ref context);
Transform.LastMaster = context.ModelMatrix;
context.ModelMatrix = Transform.InWorldSpace;
}
}
}

View file

@ -0,0 +1,72 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Contains methods for using transformations right.
/// </summary>
public abstract class GenericTransformation
{
/// <summary>
/// If true, ignores the transformation and sends <see cref="Matrix4.Identity" />, when requested.
/// </summary>
public bool Ignore = false;
/// <summary>
/// The last matrix that was used to calculate the real world matrix.
/// </summary>
public Matrix4 LastMaster { get; set; }
/// <summary>
/// The transformation in world space.
/// </summary>
public Matrix4 InWorldSpace => MergeMatrix(LastMaster);
/// <summary>
/// Contains the current model matrix.
/// </summary>
protected Matrix4 _modelMatrix { get; private set; }
/// <summary>
/// Contains the last frame the matrix was calculated.
/// </summary>
protected ulong _lastFrame { get; private set; }
/// <summary>
/// Returns the current model matrix.
/// </summary>
/// <returns></returns>
public Matrix4 GetMatrix()
{
if (Ignore) return Matrix4.Identity;
if (_lastFrame != SMRenderer.CurrentFrame)
{
_lastFrame = SMRenderer.CurrentFrame;
_modelMatrix = RequestMatrix();
}
return _modelMatrix;
}
/// <summary>
/// This combines the current matrix with the provided one.
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
public Matrix4 MergeMatrix(Matrix4 matrix)
{
return GetMatrix() * matrix;
}
/// <summary>
/// Calculates the current matrix.
/// </summary>
/// <returns>The current matrix.</returns>
protected abstract Matrix4 RequestMatrix();
}
}

View file

@ -0,0 +1,24 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// This represens a drawing instance.
/// </summary>
public class Instance
{
/// <summary>
/// The model matrix.
/// </summary>
public Matrix4 ModelMatrix = Matrix4.Identity;
/// <summary>
/// The Texture matrix
/// </summary>
public Matrix3 TextureMatrix = Matrix3.Identity;
}
}

View file

@ -0,0 +1,47 @@
#region usings
using OpenTK.Graphics;
using SM.Base.Shaders;
using SM.Base.Window;
using SM.OGL.Texture;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Represents a material.
/// </summary>
public class Material
{
/// <summary>
/// A setting to enable Blending.
/// </summary>
public virtual bool Blending { get; set; } = false;
/// <summary>
/// A custom shader, that is used to draw this material.
/// </summary>
public virtual MaterialShader CustomShader { get; set; }
/// <summary>
/// The base texture. (aka. Diffuse Texture)
/// </summary>
public virtual TextureBase Texture { get; set; }
/// <summary>
/// The tint or color.
/// </summary>
public virtual Color4 Tint { get; set; } = Color4.White;
/// <summary>
/// This allows custom shaders to use own shader arguments.
/// </summary>
public ShaderArguments ShaderArguments { get; internal set; } = new ShaderArguments();
public virtual void Draw(DrawContext context)
{
context.Shader.Draw(context);
}
}
}

View file

@ -0,0 +1,212 @@
#region usings
using System;
using System.Collections.Generic;
using OpenTK;
using SM.Base.Scene;
using SM.Base.Time;
using SM.Base.Window;
using Stopwatch = System.Diagnostics.Stopwatch;
#endregion
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// The (drawing) basis for particles
/// </summary>
public abstract class ParticleDrawingBasis<TTransform, TDirection> : DrawingBasis<TTransform>, IScriptable
where TTransform : GenericTransformation, new()
where TDirection : struct
{
private float? _continuesIntervalSeconds = null;
private Interval _continuesInterval;
/// <summary>
/// The stopwatch of the particles.
/// </summary>
protected Timer timer;
/// <summary>
/// This contains the different instances for the particles.
/// </summary>
protected List<ParticleInstance<TDirection>> instances;
/// <summary>
/// The amount of particles
/// </summary>
public int Amount = 32;
/// <summary>
/// The base lifetime for particles in seconds.
/// </summary>
public float Lifetime;
/// <summary>
/// Randomizes the lifetime for particles.
/// </summary>
public float LifetimeRandomize = 0;
/// <summary>
/// If set to any value (except null), it will create the particles continuously.
/// </summary>
public float? ContinuousInterval
{
get => _continuesIntervalSeconds;
set
{
if (value.HasValue)
{
_continuesInterval.Target = value.Value;
}
_continuesIntervalSeconds = value;
}
}
/// <summary>
/// If true, the particles will spawn in Worldspace and can't be moved by the transformation.
/// </summary>
public bool DetachedParticles;
/// <summary>
/// The maximum speed of the particles
/// <para>Default: 25</para>
/// </summary>
public float MaxSpeed = 25;
/// <summary>
/// Sets up the timer.
/// </summary>
/// <param name="duration">Duration how long the particles should live</param>
protected ParticleDrawingBasis(TimeSpan duration)
{
timer = new Timer(duration);
_continuesInterval = new Interval(0);
_continuesInterval.End += CreateContinuesParticles;
Lifetime = (float) duration.TotalSeconds;
}
/// <summary>
/// Get/Sets the state of pausing.
/// </summary>
public bool Paused
{
get => timer.Paused;
set => timer.Paused = value;
}
/// <summary>
/// Controls the movement of each particles.
/// </summary>
public abstract Func<ParticleInstance<TDirection>, TDirection> MovementCalculation { get; set; }
/// <inheritdoc />
public bool UpdateActive {
get => timer.Active || _continuesInterval.Active;
set { return; }
}
/// <inheritdoc />
public void Update(UpdateContext context)
{
Stopwatch stp = new Stopwatch();
stp.Start();
for (int i = 0; i < instances.Count; i++)
{
instances[i].Lifetime -= context.Deltatime;
if (instances[i].Lifetime <= 0)
{
instances.Remove(instances[i]);
break;
}
instances[i].ModelMatrix = CreateMatrix(instances[i], MovementCalculation(instances[i]));
}
Console.WriteLine();
}
/// <summary>
/// Triggers the particles.
/// </summary>
public void Trigger()
{
instances = new List<ParticleInstance<TDirection>>();
if (_continuesIntervalSeconds.HasValue)
{
_continuesInterval.Target = _continuesIntervalSeconds.Value;
_continuesInterval.Start();
return;
}
timer.Start();
CreateParticles();
}
/// <summary>
/// Stops the particles.
/// </summary>
public void Stop()
{
if (_continuesInterval.Active)
{
_continuesInterval.Stop();
}
timer.Stop();
}
/// <inheritdoc />
public override void OnRemoved(object sender)
{
base.OnRemoved(sender);
Stop();
}
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
if (!timer.Active && _continuesInterval != null && !_continuesInterval.Active) return;
base.DrawContext(ref context);
if (DetachedParticles) context.ModelMatrix = Matrix4.Identity;
context.Instances = instances.ConvertAll(a => (Instance)a);
Material.Draw(context);
}
/// <summary>
/// Creates the particles.
/// </summary>
protected virtual void CreateParticles()
{
for (int i = 0; i < Amount; i++)
{
instances.Add(CreateObject(i));
}
}
private void CreateContinuesParticles(Timer arg1, UpdateContext arg2)
{
instances.Add(CreateObject(0));
}
/// <summary>
/// Creates a particle.
/// </summary>
protected abstract ParticleInstance<TDirection> CreateObject(int index);
/// <summary>
/// Generates the desired matrix for drawing.
/// </summary>
protected abstract Matrix4 CreateMatrix(ParticleInstance<TDirection> Struct, TDirection relativePosition);
}
}

View file

@ -0,0 +1,40 @@
using OpenTK;
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// This describes a instance of a particle
/// </summary>
public class ParticleInstance : Instance
{
/// <summary>
/// The lifetime the particle started with.
/// </summary>
public float StartLifetime = 0;
/// <summary>
/// The lifetime this particular particle still has.
/// </summary>
public float Lifetime = 0;
/// <summary>
/// A additional matrix to store rotation and scale.
/// </summary>
public Matrix4 Matrix;
/// <summary>
/// Speeeeeeeeeed
/// </summary>
public float Speed;
}
/// <inheritdoc />
public class ParticleInstance<TValue> : ParticleInstance
where TValue : struct
{
/// <summary>
/// A direction, that the particle should travel.
/// </summary>
public TValue Direction;
}
}

View file

@ -0,0 +1,30 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// Contains methods for particle movements.
/// </summary>
public class ParticleMovement
{
/// <summary>
/// Default movement for 2D.
/// </summary>
public static Vector2 Default2D(ParticleInstance<Vector2> particle)
{
return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed);
}
/// <summary>
/// Default movement for 3D.
/// </summary>
public static Vector3 Default3D(ParticleInstance<Vector3> particle)
{
return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed);
}
}
}

View file

@ -0,0 +1,26 @@
#region usings
using System.Collections.Generic;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// A custom dictionary, with a few useful methods.
/// </summary>
public class ShaderArguments : Dictionary<string, object>
{
/// <summary>
/// Returns the stored value or the default value if the name doesn't exist.
/// </summary>
/// <param name="name"></param>
/// <param name="defaultValue"></param>
/// <typeparam name="TType"></typeparam>
/// <returns></returns>
public TType Get<TType>(string name, TType defaultValue = default)
{
return ContainsKey(name) ? (TType) this[name] : defaultValue;
}
}
}

View file

@ -0,0 +1,37 @@
#region usings
using System;
using OpenTK;
#endregion
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Contains information for a font character.
/// </summary>
[Serializable]
public struct CharParameter
{
/// <summary>
/// The advance on the X-axis.
/// </summary>
public int Advance;
/// <summary>
/// The bearing for this char.
/// </summary>
public float BearingX;
/// <summary>
/// The width of the character.
/// </summary>
public float Width;
/// <summary>
/// Matrix for the texture.
/// </summary>
public Matrix3 TextureMatrix;
}
}

View file

@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using OpenTK;
using SharpFont;
using SM.Base.Textures;
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Represents a font to be used in DrawText-classes.
/// </summary>
public class Font : Texture
{
private static Library _lib;
private Face _fontFace;
/// <summary>
/// The amount the cursor should move forward when a space was found.
/// </summary>
public float SpaceWidth { get; private set; } = 0;
/// <summary>
/// The char set the font should contain.
/// <para>See <see cref="FontCharStorage"/> for some default values.</para>
/// <para>Default: <see cref="FontCharStorage.SimpleUTF8"/></para>
/// </summary>
public ICollection<char> CharSet { get; set; } = FontCharStorage.SimpleUTF8;
/// <summary>
/// The font-size defines how large the result texture should be.
/// <para>Lower equals less quality, but also less memory usage.</para>
/// <para>Higher equals high quality, but also high memory usage.</para>
/// <para>Default: 12</para>
/// </summary>
public float FontSize { get; set; } = 12;
/// <summary>
/// The character positions.
/// </summary>
public Dictionary<char, CharParameter> Positions = new Dictionary<char, CharParameter>();
/// <summary>
/// Creates a font, by using a path
/// </summary>
/// <param name="path">Path to the font-file.</param>
public Font(string path)
{
_lib ??= new Library();
_fontFace = new Face(_lib, path);
UnpackAlignment = 1;
}
/// <summary>
/// (Re-)Generates the texture.
/// </summary>
public void RegenerateTexture()
{
Width = Height = 0;
Positions = new Dictionary<char, CharParameter>();
_fontFace.SetCharSize(0, FontSize, 0, 96);
var pos = new Dictionary<char, float[]>();
foreach (char c in CharSet)
{
_fontFace.LoadChar(c, LoadFlags.Render, LoadTarget.Normal);
pos.Add(c, new []{(float)_fontFace.Glyph.Bitmap.Width, Width});
Width += (int)_fontFace.Glyph.Advance.X + 2;
Height = Math.Max(_fontFace.Glyph.Bitmap.Rows, Height);
}
_fontFace.LoadChar('_', LoadFlags.Render, LoadTarget.Normal);
SpaceWidth = _fontFace.Glyph.Advance.X.ToSingle();
float bBoxHeight = (Math.Abs(_fontFace.BBox.Bottom) + _fontFace.BBox.Top);
float bBoxTopScale = _fontFace.BBox.Top / bBoxHeight;
float baseline = Height * bBoxTopScale + 1;
Map = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(Map))
{
g.Clear(Color.Transparent);
foreach (var keyvalue in pos)
{
_fontFace.LoadChar(keyvalue.Key, LoadFlags.Render, LoadTarget.Normal);
int y = ((int)baseline - (int)_fontFace.Glyph.Metrics.HorizontalBearingY);
g.DrawImageUnscaled(_fontFace.Glyph.Bitmap.ToGdipBitmap(Color.White), (int)keyvalue.Value[1], y);
Vector2 offset = new Vector2(keyvalue.Value[1] / Width, 0);
Vector2 scale = new Vector2(keyvalue.Value[0] / Width, 1);
Positions.Add(keyvalue.Key, new CharParameter()
{
Advance = (int)_fontFace.Glyph.LinearHorizontalAdvance,
BearingX = _fontFace.Glyph.BitmapLeft,
Width = keyvalue.Value[0],
TextureMatrix = TextureTransformation.CalculateMatrix(offset,
scale, 0),
});
}
}
}
/// <inheritdoc />
public override void Compile()
{
RegenerateTexture();
base.Compile();
}
}
}

View file

@ -0,0 +1,29 @@
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Contains default char sets.
/// </summary>
public class FontCharStorage
{
/// <summary>
/// Contains the english alphabet and the common special character.
/// </summary>
public static readonly char[] SimpleUTF8 =
{
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5',
'6',
'7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~'
};
/// <summary>
/// Contains only numbers.
/// </summary>
public static readonly char[] Numbers =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
}
}

View file

@ -0,0 +1,202 @@
#region usings
using System;
using OpenTK;
using OpenTK.Graphics;
using SM.Base.Objects.Static;
using SM.Base.Window;
#endregion
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Represents the options for <see cref="TextDrawingBasis{TTransform}.Origin"/>
/// </summary>
public enum TextOrigin
{
/// <summary>
/// The position equals (0,0) in the left side of the text.
/// </summary>
Left,
/// <summary>
/// The position equals (0,0) in the center of the text.
/// </summary>
Center,
/// <summary>
/// The position equals (0,0) in the right side of the text.
/// </summary>
Right
}
/// <summary>
/// Defines a basis for text drawing.
/// </summary>
/// <typeparam name="TTransform">Transformation type</typeparam>
public abstract class TextDrawingBasis<TTransform> : DrawingBasis<TTransform>
where TTransform : GenericTransformation, new()
{
/// <summary>
/// The different instances for drawing.
/// </summary>
protected Instance[] _instances;
/// <summary>
/// The text, that is drawn.
/// </summary>
protected string _text;
/// <summary>
/// The width of the text object.
/// </summary>
public float Width;
/// <summary>
/// The height of the text object.
/// </summary>
public float Height;
/// <summary>
/// Allow to change the origin of the text.
/// <para>Default: <see cref="TextOrigin.Left"/></para>
/// </summary>
public TextOrigin Origin = TextOrigin.Left;
/// <summary>
/// The spacing between numbers.
/// <para>Default: 1</para>
/// </summary>
public float Spacing = 1f;
/// <summary>
/// The font.
/// </summary>
public Font Font
{
get => (Font)Material.Texture;
set
{
Material.Texture = value;
GenerateMatrixes();
}
}
/// <summary>
/// The text, that is drawn.
/// </summary>
public string Text
{
get => _text;
set
{
_text = value;
GenerateMatrixes();
}
}
/// <summary>
/// The font color.
/// </summary>
public Color4 Color
{
get => Material.Tint;
set => Material.Tint = value;
}
/// <summary>
/// Creates a text object with a font.
/// </summary>
/// <param name="font">The font.</param>
protected TextDrawingBasis(Font font)
{
Material.Texture = font;
Material.Blending = true;
Mesh = Plate.Object;
}
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
base.DrawContext(ref context);
if (_instances == null) GenerateMatrixes();
}
/// <summary>
/// This generates the instances.
/// </summary>
/// <exception cref="Exception">The font doesn't contain a character that is needed for the text.</exception>
public void GenerateMatrixes()
{
if (!Font.WasCompiled) Font.RegenerateTexture();
_text = _text.Replace("\r\n", "\n").Replace("\t", " ");
_instances = new Instance[_text.Length];
float x = 0;
float y = 0;
for (var i = 0; i < _text.Length; i++)
{
if (_text[i] == ' ')
{
x += Font.SpaceWidth * Spacing;
continue;
}
if (_text[i] == '\n')
{
y += Font.Height;
Width = Math.Max(Width, x);
x = 0;
continue;
}
CharParameter parameter;
try
{
parameter = Font.Positions[_text[i]];
}
catch
{
throw new Exception("Font doesn't contain '" + _text[i] + "'");
}
if (i == 0)
{
x += parameter.Width / 2;
}
var matrix = Matrix4.CreateScale(parameter.Width, Font.Height, 1) *
Matrix4.CreateTranslation(x + parameter.Width / 2, -y, 0);
_instances[i] = new Instance
{
ModelMatrix = matrix,
TextureMatrix = parameter.TextureMatrix
};
x += parameter.Advance;
}
Height = y + Font.Height;
Width = x;
if (Origin != TextOrigin.Left)
{
foreach (Instance i in _instances)
{
if (i == null) continue;
switch (Origin)
{
case TextOrigin.Center:
i.ModelMatrix *= Matrix4.CreateTranslation(-Width / 2, 0, 0);
break;
case TextOrigin.Right:
i.ModelMatrix *= Matrix4.CreateTranslation(-Width, 0, 0);
break;
}
}
}
}
}
}

View file

@ -0,0 +1,89 @@
#region usings
using OpenTK;
using OpenTK.Graphics.ES10;
using SM.Base.Textures;
using SM.Base.Types;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Stores transformations for the textures.
/// </summary>
public class TextureTransformation
{
/// <summary>
/// The offset from the origin.
/// </summary>
public CVector2 Offset = new CVector2(0);
/// <summary>
/// The rotation of the texture.
/// </summary>
public CVector1 Rotation = new CVector1(0);
/// <summary>
/// The scale of the texture.
/// </summary>
public CVector2 Scale = new CVector2(1);
/// <summary>
/// Get the texture matrix.
/// </summary>
/// <returns></returns>
public Matrix3 GetMatrix()
{
return CalculateMatrix(Offset, Scale, Rotation);
}
/// <summary>
/// Sets the offset relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture it should use.</param>
/// <param name="pixelLocation">The offset in pixel.</param>
public void SetOffsetRelative(Texture texture, Vector2 pixelLocation)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Offset.Set( Vector2.Divide(pixelLocation, textureSize) );
}
/// <summary>
/// Sets the scale relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture.</param>
/// <param name="rectangleSize">The scale in pixel.</param>
public void SetScaleRelative(Texture texture, Vector2 rectangleSize)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Scale.Set( Vector2.Divide(rectangleSize, textureSize) );
}
/// <summary>
/// Sets the offset and scale relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture.</param>
/// <param name="location">Offset in pixel</param>
/// <param name="rectangleSize">Scale in pixel.</param>
public void SetRectangleRelative(Texture texture, Vector2 location, Vector2 rectangleSize)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Offset.Set(Vector2.Divide(location, textureSize));
Scale.Set(Vector2.Divide(rectangleSize, textureSize));
}
/// <summary>
/// Calculates a texture matrix.
/// </summary>
/// <param name="offset"></param>
/// <param name="scale"></param>
/// <param name="rotation"></param>
/// <returns></returns>
public static Matrix3 CalculateMatrix(Vector2 offset, Vector2 scale, float rotation)
{
float radians = MathHelper.DegreesToRadians(rotation);
Matrix3 result = Matrix3.CreateScale(scale.X, scale.Y, 1) * Matrix3.CreateRotationZ(radians);
result.Row2 = new Vector3(offset.X, offset.Y, 1);
return result;
}
}
}