diff --git a/ANX.Framework/Graphics/RenderTarget2D.cs b/ANX.Framework/Graphics/RenderTarget2D.cs index 720d5071..4586f698 100644 --- a/ANX.Framework/Graphics/RenderTarget2D.cs +++ b/ANX.Framework/Graphics/RenderTarget2D.cs @@ -67,6 +67,8 @@ namespace ANX.Framework.Graphics { this.width = width; this.height = height; + OneOverWidth = 1f / width; + OneOverHeight = 1f / height; base.levelCount = 1; base.format = SurfaceFormat.Color; diff --git a/ANX.Framework/Graphics/SkinnedEffect.cs b/ANX.Framework/Graphics/SkinnedEffect.cs index 08186ee3..ab342015 100644 --- a/ANX.Framework/Graphics/SkinnedEffect.cs +++ b/ANX.Framework/Graphics/SkinnedEffect.cs @@ -1,34 +1,189 @@ -#region Using Statements using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using ANX.Framework.NonXNA; -using ANX.Framework.Graphics; - -#endregion // Using Statements +using ANX.Framework.NonXNA.Development; // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. // For details see: http://anxframework.codeplex.com/license - - namespace ANX.Framework.Graphics { + [PercentageComplete(100)] + [TestState(TestStateAttribute.TestState.Untested)] + [Developer("AstrorEnales")] public class SkinnedEffect : Effect, IEffectMatrices, IEffectLights, IEffectFog - { + { + public const int MaxBones = 72; + + #region Private + private Matrix world; + private Matrix view; + private Matrix projection; + private bool preferPerPixelLighting; + private bool isFogEnabled; + private Vector3 diffuseColor; + private Vector3 specularColor; + private Vector3 fogColor; + private Vector3 emissiveColor; + private Vector3 ambientLightColor; + private Matrix[] bones; + private int weightsPerBone; + #endregion + + #region Public + public float FogEnd { get; set; } + public float FogStart { get; set; } + public Texture2D Texture { get; set; } + public float Alpha { get; set; } + public float SpecularPower { get; set; } + public DirectionalLight DirectionalLight0 { get; private set; } + public DirectionalLight DirectionalLight1 { get; private set; } + public DirectionalLight DirectionalLight2 { get; private set; } + + public int WeightsPerVertex + { + get + { + return weightsPerBone; + } + set + { + if (value != 1 && value != 2 && value != 4) + throw new ArgumentOutOfRangeException("Weights per bone only allows 1, 2 or 4 as value!"); + weightsPerBone = value; + } + } + + public bool PreferPerPixelLighting + { + get { return preferPerPixelLighting; } + set + { + preferPerPixelLighting = value; + SelectTechnique(); + } + } + + public Matrix Projection + { + get { return projection; } + set { projection = value; } + } + + public Matrix View + { + get { return view; } + set { view = value; } + } + + public Matrix World + { + get { return world; } + set { world = value; } + } + + public Vector3 AmbientLightColor + { + get { return ambientLightColor; } + set { ambientLightColor = value; } + } + + public bool LightingEnabled + { + get { return true; } + set + { + if (value == false) + throw new NotSupportedException("SkinnedEffect without Lighting isn't supported!"); + } + } + + public Vector3 FogColor + { + get { return fogColor; } + set { fogColor = value; } + } + + public bool FogEnabled + { + get { return isFogEnabled; } + set + { + isFogEnabled = value; + SelectTechnique(); + } + } + + public Vector3 DiffuseColor + { + get { return diffuseColor; } + set { diffuseColor = value; } + } + public Vector3 EmissiveColor + { + get { return emissiveColor; } + set { emissiveColor = value; } + } + + public Vector3 SpecularColor + { + get { return specularColor; } + set { specularColor = value; } + } + #endregion + + #region Constructor public SkinnedEffect(GraphicsDevice graphics) : base(graphics, GetByteCode(), GetSourceLanguage()) - { - throw new NotImplementedException(); + { + world = Matrix.Identity; + view = Matrix.Identity; + projection = Matrix.Identity; + FogStart = 0f; + FogEnd = 1f; + Alpha = 1f; + diffuseColor = Vector3.One; + ambientLightColor = Vector3.One; + emissiveColor = Vector3.One; + specularColor = Vector3.One; + SpecularPower = 16f; + WeightsPerVertex = 4; + CreateLights(null); + DirectionalLight0.Enabled = true; + + bones = new Matrix[MaxBones]; + for (int index = 0; index < MaxBones; index++) + bones[index] = Matrix.Identity; + + SelectTechnique(); } protected SkinnedEffect(SkinnedEffect cloneSource) : base(cloneSource) - { - throw new NotImplementedException(); + { + world = cloneSource.world; + view = cloneSource.view; + projection = cloneSource.projection; + preferPerPixelLighting = cloneSource.preferPerPixelLighting; + isFogEnabled = cloneSource.isFogEnabled; + diffuseColor = cloneSource.diffuseColor; + specularColor = cloneSource.specularColor; + fogColor = cloneSource.fogColor; + emissiveColor = cloneSource.emissiveColor; + ambientLightColor = cloneSource.ambientLightColor; + FogEnd = cloneSource.FogEnd; + FogStart = cloneSource.FogStart; + Texture = cloneSource.Texture; + SpecularPower = cloneSource.SpecularPower; + Alpha = cloneSource.Alpha; + WeightsPerVertex = cloneSource.WeightsPerVertex; + for (int index = 0; index < MaxBones; index++) + bones[index] = cloneSource.bones[index]; + + CreateLights(cloneSource); + SelectTechnique(); } + #endregion #region GetByteCode private static byte[] GetByteCode() @@ -46,245 +201,186 @@ namespace ANX.Framework.Graphics } #endregion - public override Effect Clone() + #region Clone + public override Effect Clone() { return new SkinnedEffect(this); - } + } + #endregion - public const int MaxBones = 72; + #region CreateLights + private void CreateLights(SkinnedEffect cloneSource) + { + DirectionalLight0 = new DirectionalLight(Parameters["DirLight0Direction"], Parameters["DirLight0DiffuseColor"], + null, (cloneSource != null) ? cloneSource.DirectionalLight0 : null); + DirectionalLight1 = new DirectionalLight(Parameters["DirLight1Direction"], Parameters["DirLight1DiffuseColor"], + null, (cloneSource != null) ? cloneSource.DirectionalLight1 : null); + DirectionalLight2 = new DirectionalLight(Parameters["DirLight2Direction"], Parameters["DirLight2DiffuseColor"], + null, (cloneSource != null) ? cloneSource.DirectionalLight2 : null); + } + #endregion - public bool PreferPerPixelLighting - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + #region EnableDefaultLighting + public void EnableDefaultLighting() + { + LightingEnabled = true; + ambientLightColor = new Vector3(0.05333332f, 0.09882354f, 0.1819608f); - public Matrix Projection - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + DirectionalLight0.Direction = new Vector3(-0.5265408f, -0.5735765f, -0.6275069f); + DirectionalLight0.DiffuseColor = new Vector3(1f, 0.9607844f, 0.8078432f); + DirectionalLight0.SpecularColor = DirectionalLight0.DiffuseColor; + DirectionalLight0.Enabled = true; - public Matrix View - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + DirectionalLight1.Direction = new Vector3(0.7198464f, 0.3420201f, 0.6040227f); + DirectionalLight1.DiffuseColor = new Vector3(0.9647059f, 0.7607844f, 0.4078432f); + DirectionalLight1.SpecularColor = Vector3.Zero; + DirectionalLight1.Enabled = true; - public Matrix World - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + DirectionalLight2.Direction = new Vector3(0.4545195f, -0.7660444f, 0.4545195f); + DirectionalLight2.DiffuseColor = new Vector3(0.3231373f, 0.3607844f, 0.3937255f); + DirectionalLight2.SpecularColor = DirectionalLight2.DiffuseColor; + DirectionalLight2.Enabled = true; + } + #endregion - public void EnableDefaultLighting() - { - throw new NotImplementedException(); - } + #region GetBoneTransforms + public Matrix[] GetBoneTransforms(int count) + { + if (count <= 0) + throw new ArgumentOutOfRangeException("count"); + if (count > 72) + throw new ArgumentException("The maximum number of allowed bones for the SkinnedEffect is " + MaxBones + "!"); - public Vector3 AmbientLightColor - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + var result = new Matrix[MaxBones]; + for (int index = 0; index < MaxBones; index++) + { + result[index] = bones[index]; + } + return result; + } + #endregion - public DirectionalLight DirectionalLight0 - { - get { throw new NotImplementedException(); } - } + #region SetBoneTransforms + public void SetBoneTransforms(Matrix[] boneTransforms) + { + if (boneTransforms == null || boneTransforms.Length == 0) + throw new ArgumentNullException("boneTransforms"); - public DirectionalLight DirectionalLight1 - { - get { throw new NotImplementedException(); } - } + if (boneTransforms.Length > 72) + throw new ArgumentException("The maximum number of allowed bones for the SkinnedEffect is " + MaxBones + "!"); - public DirectionalLight DirectionalLight2 - { - get { throw new NotImplementedException(); } - } + for (int index = 0; index < MaxBones; index++) + bones[index] = index < boneTransforms.Length ? boneTransforms[index] : Matrix.Identity; + } + #endregion - public bool LightingEnabled - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + #region PreBindSetParameters + internal override void PreBindSetParameters() + { + Matrix worldView; + Matrix.Multiply(ref world, ref view, out worldView); + Matrix wvp; + Matrix.Multiply(ref worldView, ref projection, out wvp); + Parameters["WorldViewProj"].SetValue(wvp); - public Vector3 FogColor - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + SetLightingMatrices(); + SetMaterialColor(); - public bool FogEnabled - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + Parameters["Bones"].SetValue(bones); - public float FogEnd - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + Parameters["FogColor"].SetValue(fogColor); + Parameters["SpecularPower"].SetValue(SpecularPower); + Parameters["SpecularColor"].SetValue(specularColor); - public float FogStart - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + if (Texture != null) + Parameters["Texture"].SetValue(Texture); - public Texture2D Texture - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + if (isFogEnabled) + SetFogVector(ref worldView); + else + Parameters["FogVector"].SetValue(Vector4.Zero); - public int WeightsPerVertex - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + SelectTechnique(); + } + #endregion - public Vector3 DiffuseColor - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + #region SelectTechnique + private void SelectTechnique() + { + string name = ""; + if (WeightsPerVertex == 1) + name = "OneBone"; + else if (WeightsPerVertex == 2) + name = "TwoBones"; + else if (WeightsPerVertex == 4) + name = "FourBones"; - public Vector3 EmissiveColor - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + bool oneLight = DirectionalLight1.Enabled == false && DirectionalLight2.Enabled == false; + if (preferPerPixelLighting) + name += "PixelLighting"; + else if (oneLight) + name += "OneLight"; + else + name += "VertexLighting"; - public Vector3 SpecularColor - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + if (isFogEnabled == false) + name += "NoFog"; - public float SpecularPower - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + CurrentTechnique = Techniques[name]; + } + #endregion - public float Alpha - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } + #region SetLightingMatrices + private void SetLightingMatrices() + { + Matrix worldInverse; + Matrix.Invert(ref world, out worldInverse); + Matrix worldInverseTranspose; + Matrix.Transpose(ref worldInverse, out worldInverseTranspose); - public Matrix[] GetBoneTransforms(int count) - { - throw new NotImplementedException(); - } + Parameters["World"].SetValue(world); + Parameters["WorldInverseTranspose"].SetValue(worldInverseTranspose); - public void SetBoneTransforms(Matrix[] boneTransforms) - { - throw new NotImplementedException(); - } + Matrix viewInverse; + Matrix.Invert(ref view, out viewInverse); + Parameters["EyePosition"].SetValue(viewInverse.Translation); + } + #endregion + + #region SetMaterialColor + private void SetMaterialColor() + { + Vector4 diffuse; + diffuse.X = diffuseColor.X * Alpha; + diffuse.Y = diffuseColor.Y * Alpha; + diffuse.Z = diffuseColor.Z * Alpha; + diffuse.W = Alpha; + Vector3 emissive; + emissive.X = (emissiveColor.X + ambientLightColor.X * diffuseColor.X) * Alpha; + emissive.Y = (emissiveColor.Y + ambientLightColor.Y * diffuseColor.Y) * Alpha; + emissive.Z = (emissiveColor.Z + ambientLightColor.Z * diffuseColor.Z) * Alpha; + Parameters["DiffuseColor"].SetValue(diffuse); + Parameters["EmissiveColor"].SetValue(emissive); + } + #endregion + + #region SetFogVector + private void SetFogVector(ref Matrix worldView) + { + if (FogStart == FogEnd) + { + Parameters["FogVector"].SetValue(new Vector4(0f, 0f, 0f, 1f)); + return; + } + + float fogFactor = 1f / (FogStart - FogEnd); + Vector4 value; + value.X = worldView.M13 * fogFactor; + value.Y = worldView.M23 * fogFactor; + value.Z = worldView.M33 * fogFactor; + value.W = (worldView.M43 + FogStart) * fogFactor; + Parameters["FogVector"].SetValue(value); + } + #endregion } } diff --git a/ANX.Framework/Graphics/SpriteBatch.cs b/ANX.Framework/Graphics/SpriteBatch.cs index 474b7779..d110d9a8 100644 --- a/ANX.Framework/Graphics/SpriteBatch.cs +++ b/ANX.Framework/Graphics/SpriteBatch.cs @@ -1,10 +1,8 @@ -#region Using Statements using System; using System.Collections.Generic; using System.Text; using ANX.Framework.NonXNA; - -#endregion // Using Statements +using ANX.Framework.NonXNA.Development; // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. @@ -12,11 +10,14 @@ using ANX.Framework.NonXNA; namespace ANX.Framework.Graphics { + [PercentageComplete(100)] + [TestState(TestStateAttribute.TestState.Untested)] + [Developer("Glatzemann, AstrorEnales")] public class SpriteBatch : GraphicsResource - { - #region Private Members - private const int InitialBatchSize = 1024; + { + private const int InitialBatchSize = 1024; + #region Private private Effect spriteBatchEffect; private bool hasBegun; private SpriteSortMode currentSortMode; @@ -43,8 +44,7 @@ namespace ANX.Framework.Graphics private static TextureComparer textureComparer = new TextureComparer(); private static FrontToBackComparer frontToBackComparer = new FrontToBackComparer(); private static BackToFrontComparer backToFrontComparer = new BackToFrontComparer(); - - #endregion // Private Members + #endregion #region Constructor public SpriteBatch(GraphicsDevice graphicsDevice) @@ -55,43 +55,55 @@ namespace ANX.Framework.Graphics base.GraphicsDevice = graphicsDevice; var renderSystemCreator = AddInSystemFactory.Instance.GetDefaultCreator(); - this.spriteBatchEffect = new Effect(graphicsDevice, renderSystemCreator.GetShaderByteCode(NonXNA.PreDefinedShader.SpriteBatch), renderSystemCreator.GetStockShaderSourceLanguage); + this.spriteBatchEffect = new Effect(graphicsDevice, + renderSystemCreator.GetShaderByteCode(NonXNA.PreDefinedShader.SpriteBatch), + renderSystemCreator.GetStockShaderSourceLanguage); this.spriteInfos = new SpriteInfo[InitialBatchSize]; - this.InitializeIndexBuffer(InitialBatchSize); - this.InitializeVertexBuffer(); } #endregion - #region Begin-Method + #region Begin public void Begin() { - Begin(SpriteSortMode.Texture, null, null, null, null, null, Matrix.Identity); + Begin(SpriteSortMode.Texture, null, null, null, null, null); } public void Begin(SpriteSortMode sortMode, BlendState blendState) { - Begin(sortMode, blendState, null, null, null, null, Matrix.Identity); + Begin(sortMode, blendState, null, null, null, null); } - public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState) + public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, + DepthStencilState depthStencilState, RasterizerState rasterizerState) { - Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, null, Matrix.Identity); + Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, null); } - public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) - { - Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, Matrix.Identity); + public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, + DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) + { + if (hasBegun == true) + throw new Exception("End() has to be called before a new SpriteBatch can be started with Begin()"); + + hasBegun = true; + this.currentSortMode = sortMode; + + this.blendState = blendState; + this.samplerState = samplerState; + this.depthStencilState = depthStencilState; + this.rasterizerState = rasterizerState; + this.effect = effect; + this.transformMatrix = Matrix.Identity; } - public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) + public void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, + DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) { - if (this.hasBegun == true) - { + if (hasBegun == true) throw new Exception("End() has to be called before a new SpriteBatch can be started with Begin()"); - } hasBegun = true; @@ -104,150 +116,186 @@ namespace ANX.Framework.Graphics this.effect = effect; this.transformMatrix = transformMatrix; } + #endregion - #endregion // Begin-Method + #region Draw + public void Draw(Texture2D texture, Rectangle destinationRectangle, Color color) + { + Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, + destinationRectangle.Height), null, color, Vector2.Zero, 0f, 0f, Vector2.One, SpriteEffects.None); + } - #region Draw-Method - public void Draw (Texture2D texture, Rectangle destinationRectangle, Color color) + public void Draw(Texture2D texture, Rectangle destinationRectangle, Nullable sourceRectangle, Color color) + { + Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, + destinationRectangle.Height), sourceRectangle, color, Vector2.Zero, 0f, 0f, Vector2.One, SpriteEffects.None); + } + + public void Draw(Texture2D texture, Rectangle destinationRectangle, Nullable sourceRectangle, Color color, + Single rotation, Vector2 origin, SpriteEffects effects, Single layerDepth) { - Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, destinationRectangle.Height), null, color, Vector2.Zero, 0.0f, 0.0f, Vector2.One, SpriteEffects.None); - } - - public void Draw (Texture2D texture, Rectangle destinationRectangle, Nullable sourceRectangle, Color color) - { - Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, destinationRectangle.Height), sourceRectangle, color, Vector2.Zero, 0.0f, 0.0f, Vector2.One, SpriteEffects.None); - } - - public void Draw(Texture2D texture, Rectangle destinationRectangle, Nullable sourceRectangle, Color color, Single rotation, Vector2 origin, SpriteEffects effects, Single layerDepth) - { - Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, destinationRectangle.Height), sourceRectangle, color, origin, layerDepth, rotation, Vector2.One, effects); + Draw(texture, new Vector2(destinationRectangle.X, destinationRectangle.Y), new Vector2(destinationRectangle.Width, + destinationRectangle.Height), sourceRectangle, color, origin, layerDepth, rotation, Vector2.One, effects); } public void Draw(Texture2D texture, Vector2 position, Color color) { - Draw(texture, position, new Vector2(texture.Width, texture.Height), null, color, Vector2.Zero, 0.0f, 0.0f, Vector2.One, SpriteEffects.None); + Draw(texture, position, new Vector2(texture.Width, texture.Height), null, color, Vector2.Zero, 0f, 0f, Vector2.One, + SpriteEffects.None); } public void Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color) { - Vector2 size = sourceRectangle.HasValue ? new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : new Vector2(texture.Width, texture.Height); - Draw(texture, position, size, sourceRectangle, color, Vector2.Zero, 0.0f, 0.0f, Vector2.One, SpriteEffects.None); + Vector2 size = sourceRectangle.HasValue ? + new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : + new Vector2(texture.Width, texture.Height); + Draw(texture, position, size, sourceRectangle, color, Vector2.Zero, 0f, 0f, Vector2.One, SpriteEffects.None); } - public void Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color, Single rotation, Vector2 origin, Single scale, SpriteEffects effects, Single layerDepth) + public void Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color, Single rotation, + Vector2 origin, Single scale, SpriteEffects effects, Single layerDepth) { - Vector2 size = sourceRectangle.HasValue ? new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : new Vector2(texture.Width, texture.Height); + Vector2 size = sourceRectangle.HasValue ? + new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : + new Vector2(texture.Width, texture.Height); Draw(texture, position, size, sourceRectangle, color, origin, layerDepth, rotation, new Vector2(scale), effects); } - public void Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth) - { - Vector2 size = sourceRectangle.HasValue ? new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : new Vector2(texture.Width, texture.Height); - Draw(texture, position, size, sourceRectangle, color, origin, layerDepth, rotation, scale, effects); + public void Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color, Single rotation, + Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth) + { + Vector2 size = sourceRectangle.HasValue ? + new Vector2(sourceRectangle.Value.Width, sourceRectangle.Value.Height) : + new Vector2(texture.Width, texture.Height); + Draw(texture, position, size, sourceRectangle, color, origin, layerDepth, rotation, scale, effects); + } + #endregion + + #region DrawOptimizedText + internal void DrawOptimizedText(Texture2D texture, Vector2 position, ref Rectangle sourceRectangle, ref Color color, + float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) + { + Vector2 size = new Vector2(sourceRectangle.Width, sourceRectangle.Height); + Draw(texture, position, size, sourceRectangle, color, Vector2.Zero, layerDepth, rotation, scale, effects); + } + + internal void DrawOptimizedText(Texture2D texture, Vector2 position, ref Rectangle sourceRectangle, ref Color color) + { + if (hasBegun == false) + throw new InvalidOperationException("Begin() must be called before Draw()"); + + if (texture == null) + throw new ArgumentNullException("texture"); + + ResizeIfNeeded(); + + Vector2 bottomRight; + bottomRight.X = position.X + sourceRectangle.Width; + bottomRight.Y = position.Y + sourceRectangle.Height; + + SpriteInfo currentSprite = spriteInfos[currentBatchPosition]; + currentSprite.Corners = new Vector2[] + { + position, + new Vector2(bottomRight.X, position.Y), + bottomRight, + new Vector2(position.X, bottomRight.Y) + }; + + currentSprite.Tint = color; + currentSprite.texture = texture; + + currentSprite.topLeftUV.X = sourceRectangle.X * texture.OneOverWidth; + currentSprite.topLeftUV.Y = sourceRectangle.Y * texture.OneOverHeight; + currentSprite.bottomRightUV.X = (sourceRectangle.X + sourceRectangle.Width) * texture.OneOverWidth; + currentSprite.bottomRightUV.Y = (sourceRectangle.Y + sourceRectangle.Height) * texture.OneOverHeight; + + currentSprite.origin = Vector2.Zero; + currentSprite.rotation = 0f; + currentSprite.layerDepth = 1f; + + spriteInfos[currentBatchPosition] = currentSprite; + currentBatchPosition++; + + if (this.currentSortMode == SpriteSortMode.Immediate) + { + BatchRender(0, 1); + Flush(); + } + } + #endregion + + #region DrawString + public void DrawString(SpriteFont font, String text, Vector2 position, Color color) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); + + font.DrawString(text, this, position, color); } - #endregion // Draw-Method + public void DrawString(SpriteFont font, StringBuilder text, Vector2 position, Color color) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); - #region DrawString-Method - public void DrawString(SpriteFont font, String text, Vector2 position, Color color) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } + font.DrawString(text.ToString(), this, position, color); + } - if (text == null) - { - throw new ArgumentNullException("text"); - } + public void DrawString(SpriteFont font, String text, Vector2 position, Color color, Single rotation, Vector2 origin, + Single scale, SpriteEffects effects, Single layerDepth) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); - font.DrawString(ref text, this, position, color, Vector2.One, Vector2.Zero, 0.0f, 1.0f, SpriteEffects.None); + font.DrawString(text, this, position, color, new Vector2(scale), origin, rotation, layerDepth, effects); } - public void DrawString(SpriteFont font, String text, Vector2 position, Color color, Single rotation, Vector2 origin, Single scale, SpriteEffects effects, Single layerDepth) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } + public void DrawString(SpriteFont font, String text, Vector2 position, Color color, Single rotation, Vector2 origin, + Vector2 scale, SpriteEffects effects, Single layerDepth) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); - if (text == null) - { - throw new ArgumentNullException("text"); - } - - font.DrawString(ref text, this, position, color, new Vector2(scale), origin, rotation, layerDepth, effects); + font.DrawString(text, this, position, color, scale, origin, rotation, layerDepth, effects); } - public void DrawString(SpriteFont font, String text, Vector2 position, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } + public void DrawString(SpriteFont font, StringBuilder text, Vector2 position, Color color, Single rotation, + Vector2 origin, Single scale, SpriteEffects effects, Single layerDepth) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); - if (text == null) - { - throw new ArgumentNullException("text"); - } + font.DrawString(text.ToString(), this, position, color, new Vector2(scale), origin, rotation, layerDepth, effects); + } - font.DrawString(ref text, this, position, color, scale, origin, rotation, layerDepth, effects); - } + public void DrawString(SpriteFont font, StringBuilder text, Vector2 position, Color color, Single rotation, + Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth) + { + if (font == null) + throw new ArgumentNullException("font"); + if (text == null) + throw new ArgumentNullException("text"); - public void DrawString (SpriteFont font, StringBuilder text, Vector2 position, Color color) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } - - if (text == null) - { - throw new ArgumentNullException("text"); - } - - DrawString(font, text.ToString(), position, color); - } - - public void DrawString (SpriteFont font, StringBuilder text, Vector2 position, Color color, Single rotation, Vector2 origin, Single scale, SpriteEffects effects, Single layerDepth) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } - - if (text == null) - { - throw new ArgumentNullException("text"); - } - - DrawString(font, text.ToString(), position, color, rotation, origin, scale, effects, layerDepth); - } - - public void DrawString (SpriteFont font, StringBuilder text, Vector2 position, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth) - { - if (font == null) - { - throw new ArgumentNullException("font"); - } - - if (text == null) - { - throw new ArgumentNullException("text"); - } - - DrawString(font, text.ToString(), position, color, rotation, origin, scale, effects, layerDepth); - } - - #endregion // DrawString-Method + font.DrawString(text.ToString(), this, position, color, scale, origin, rotation, layerDepth, effects); + } + #endregion #region End public void End() { if (hasBegun == false) - { throw new Exception("Begin() has to be called before End()"); - } hasBegun = false; @@ -256,17 +304,11 @@ namespace ANX.Framework.Graphics if (currentBatchPosition > 0) { if (this.currentSortMode == SpriteSortMode.Texture) - { Array.Sort(spriteInfos, 0, currentBatchPosition, textureComparer); - } else if (this.currentSortMode == SpriteSortMode.BackToFront) - { Array.Sort(spriteInfos, 0, currentBatchPosition, backToFrontComparer); - } else if (this.currentSortMode == SpriteSortMode.FrontToBack) - { Array.Sort(spriteInfos, 0, currentBatchPosition, frontToBackComparer); - } int startOffset = 0; Texture2D lastTexture = spriteInfos[0].texture; @@ -285,57 +327,72 @@ namespace ANX.Framework.Graphics Flush(); } - #endregion + #endregion - private void Draw(Texture2D texture, Vector2 topLeft, Vector2 destinationSize, Rectangle? sourceRectangle, Color tint, Vector2 origin, float layerDepth, float rotation, Vector2 scale, SpriteEffects effects) + #region ResizeIfNeeded + private void ResizeIfNeeded() + { + if (currentBatchPosition >= spriteInfos.Length) + { + int newSize = spriteInfos.Length * 2; + Array.Resize(ref spriteInfos, newSize); + InitializeIndexBuffer(newSize); + } + } + #endregion + + #region Draw + private void Draw(Texture2D texture, Vector2 topLeft, Vector2 destinationSize, Rectangle? sourceRectangle, + Color tint, Vector2 origin, float layerDepth, float rotation, Vector2 scale, SpriteEffects effects) { if (hasBegun == false) - { throw new InvalidOperationException("Begin() must be called before Draw()"); - } if (texture == null) - { throw new ArgumentNullException("texture"); - } - if (currentBatchPosition >= spriteInfos.Length) - { - int newSize = spriteInfos.Length * 2; - Array.Resize(ref spriteInfos, newSize); - InitializeIndexBuffer(newSize); - } + ResizeIfNeeded(); - Vector2 bottomRight = topLeft + (destinationSize * scale); + Vector2 bottomRight = new Vector2(topLeft.X + (destinationSize.X * scale.X), + topLeft.Y + (destinationSize.Y * scale.Y)); - spriteInfos[currentBatchPosition].Corners = new Vector2[] { topLeft, new Vector2(bottomRight.X, topLeft.Y), bottomRight, new Vector2(topLeft.X, bottomRight.Y) }; + SpriteInfo currentSprite = spriteInfos[currentBatchPosition]; + currentSprite.Corners = new Vector2[] + { + topLeft, + new Vector2(bottomRight.X, topLeft.Y), + bottomRight, + new Vector2(topLeft.X, bottomRight.Y) + }; - spriteInfos[currentBatchPosition].Tint = tint; - spriteInfos[currentBatchPosition].texture = texture; + currentSprite.Tint = tint; + currentSprite.texture = texture; if (sourceRectangle.HasValue) { - spriteInfos[currentBatchPosition].topLeftUV = new Vector2(sourceRectangle.Value.X / (float)texture.Width, sourceRectangle.Value.Y / (float)texture.Height); - spriteInfos[currentBatchPosition].bottomRightUV = new Vector2((sourceRectangle.Value.X + sourceRectangle.Value.Width) / (float)texture.Width, (sourceRectangle.Value.Y + sourceRectangle.Value.Height) / (float)texture.Height); + currentSprite.topLeftUV.X = sourceRectangle.Value.X * texture.OneOverWidth; + currentSprite.topLeftUV.Y = sourceRectangle.Value.Y * texture.OneOverHeight; + currentSprite.bottomRightUV.X = (sourceRectangle.Value.X + sourceRectangle.Value.Width) * texture.OneOverWidth; + currentSprite.bottomRightUV.Y = (sourceRectangle.Value.Y + sourceRectangle.Value.Height) * texture.OneOverHeight; } else { - spriteInfos[currentBatchPosition].topLeftUV = Vector2.Zero; - spriteInfos[currentBatchPosition].bottomRightUV = Vector2.One; + currentSprite.topLeftUV = Vector2.Zero; + currentSprite.bottomRightUV = Vector2.One; } if ((effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { - float tempY = spriteInfos[currentBatchPosition].bottomRightUV.Y; - spriteInfos[currentBatchPosition].bottomRightUV.Y = spriteInfos[currentBatchPosition].topLeftUV.Y; - spriteInfos[currentBatchPosition].topLeftUV.Y = tempY; + float tempY = currentSprite.bottomRightUV.Y; + currentSprite.bottomRightUV.Y = currentSprite.topLeftUV.Y; + currentSprite.topLeftUV.Y = tempY; } if ((effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { - float tempX = spriteInfos[currentBatchPosition].bottomRightUV.X; - spriteInfos[currentBatchPosition].bottomRightUV.X = spriteInfos[currentBatchPosition].topLeftUV.X; - spriteInfos[currentBatchPosition].topLeftUV.X = tempX; + float tempX = currentSprite.bottomRightUV.X; + currentSprite.bottomRightUV.X = currentSprite.topLeftUV.X; + currentSprite.topLeftUV.X = tempX; } if (rotation != 0f) @@ -348,17 +405,22 @@ namespace ANX.Framework.Graphics Vector2 transformVector; Vector2 result; + float offsetX = topLeft.X + origin.X; + float offsetY = topLeft.Y + origin.Y; for (int i = 0; i < 4; i++) { - transformVector = spriteInfos[currentBatchPosition].Corners[i] - topLeft - origin; + transformVector.X = currentSprite.Corners[i].X - offsetX; + transformVector.Y = currentSprite.Corners[i].Y - offsetY; Vector2.Transform(ref transformVector, ref this.cachedRotationMatrix, out result); - spriteInfos[currentBatchPosition].Corners[i] = result + topLeft + origin; + currentSprite.Corners[i].X = result.X + offsetX; + currentSprite.Corners[i].Y = result.Y + offsetY; } } - spriteInfos[currentBatchPosition].origin = origin; - spriteInfos[currentBatchPosition].rotation = rotation; - spriteInfos[currentBatchPosition].layerDepth = layerDepth; + currentSprite.origin = origin; + currentSprite.rotation = rotation; + currentSprite.layerDepth = layerDepth; + spriteInfos[currentBatchPosition] = currentSprite; currentBatchPosition++; @@ -367,20 +429,18 @@ namespace ANX.Framework.Graphics BatchRender(0, 1); Flush(); } - } + } + #endregion - private void BatchRender(int offset, int count) + #region BatchRender + private void BatchRender(int offset, int count) { int vertexCount = count * 4; if (this.vertices == null) - { this.vertices = new VertexPositionColorTexture[vertexCount]; - } else if (this.vertices.Length < vertexCount) - { Array.Resize(ref this.vertices, vertexCount); - } int vertexPos = 0; for (int i = offset; i < offset + count; i++) @@ -415,27 +475,34 @@ namespace ANX.Framework.Graphics spriteBatchEffect.Parameters["Texture"].SetValue(this.spriteInfos[offset].texture); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexCount, 0, count * 2); - } + } + #endregion - private void Flush() + #region Flush + private void Flush() { currentBatchPosition = 0; } + #endregion - private void InitializeIndexBuffer(int size) + #region InitializeIndexBuffer + private void InitializeIndexBuffer(int size) { int indexCount = size * 6; if (this.indexBuffer == null) { - this.indexBuffer = new DynamicIndexBuffer(this.GraphicsDevice, IndexElementSize.SixteenBits, indexCount, BufferUsage.WriteOnly); + this.indexBuffer = new DynamicIndexBuffer(this.GraphicsDevice, IndexElementSize.SixteenBits, indexCount, + BufferUsage.WriteOnly); this.indexBuffer.ContentLost += new EventHandler(indexBuffer_ContentLost); } SetIndexData(indexCount); - } + } + #endregion - private void indexBuffer_ContentLost(object sender, EventArgs e) + #region indexBuffer_ContentLost + private void indexBuffer_ContentLost(object sender, EventArgs e) { if (this.indexBuffer != null) { @@ -445,11 +512,13 @@ namespace ANX.Framework.Graphics } InitializeIndexBuffer(InitialBatchSize); - } + } + #endregion - private void SetIndexData(int indexCount) + #region SetIndexData + private void SetIndexData(int indexCount) { - short[] indices = new short[indexCount]; + var indices = new ushort[indexCount]; int baseIndex; int baseArrayIndex; @@ -458,27 +527,32 @@ namespace ANX.Framework.Graphics baseIndex = i * 4; baseArrayIndex = baseIndex + i + i; - indices[baseArrayIndex] = (short)baseIndex; - indices[baseArrayIndex + 1] = (short)(baseIndex + 1); - indices[baseArrayIndex + 2] = (short)(baseIndex + 2); - indices[baseArrayIndex + 3] = (short)baseIndex; - indices[baseArrayIndex + 4] = (short)(baseIndex + 2); - indices[baseArrayIndex + 5] = (short)(baseIndex + 3); + indices[baseArrayIndex] = (ushort)baseIndex; + indices[baseArrayIndex + 1] = (ushort)(baseIndex + 1); + indices[baseArrayIndex + 2] = (ushort)(baseIndex + 2); + indices[baseArrayIndex + 3] = (ushort)baseIndex; + indices[baseArrayIndex + 4] = (ushort)(baseIndex + 2); + indices[baseArrayIndex + 5] = (ushort)(baseIndex + 3); } - this.indexBuffer.SetData(indices); - } + this.indexBuffer.SetData(indices); + } + #endregion - private void InitializeVertexBuffer() + #region InitializeVertexBuffer + private void InitializeVertexBuffer() { if (this.vertexBuffer == null) { - this.vertexBuffer = new DynamicVertexBuffer(this.GraphicsDevice, typeof(VertexPositionColorTexture), InitialBatchSize * 4, BufferUsage.WriteOnly); - this.vertexBuffer.ContentLost += new EventHandler(vertexBuffer_ContentLost); + this.vertexBuffer = new DynamicVertexBuffer(this.GraphicsDevice, typeof(VertexPositionColorTexture), + InitialBatchSize * 4, BufferUsage.WriteOnly); + this.vertexBuffer.ContentLost += vertexBuffer_ContentLost; } } + #endregion - private void vertexBuffer_ContentLost(object sender, EventArgs e) + #region vertexBuffer_ContentLost + private void vertexBuffer_ContentLost(object sender, EventArgs e) { this.currentBatchPosition = 0; @@ -490,16 +564,19 @@ namespace ANX.Framework.Graphics } InitializeVertexBuffer(); - } + } + #endregion - private void SetRenderStates() + #region SetRenderStates + private void SetRenderStates() { - GraphicsDevice.BlendState = this.blendState != null ? this.blendState : BlendState.AlphaBlend; - GraphicsDevice.DepthStencilState = this.depthStencilState != null ? this.depthStencilState : DepthStencilState.None; - GraphicsDevice.RasterizerState = this.rasterizerState != null ? this.rasterizerState : RasterizerState.CullCounterClockwise; - GraphicsDevice.SamplerStates[0] = this.samplerState != null ? this.samplerState : SamplerState.LinearClamp; + GraphicsDevice.BlendState = blendState != null ? blendState : BlendState.AlphaBlend; + GraphicsDevice.DepthStencilState = depthStencilState != null ? depthStencilState : DepthStencilState.None; + GraphicsDevice.RasterizerState = rasterizerState != null ? rasterizerState : RasterizerState.CullCounterClockwise; + GraphicsDevice.SamplerStates[0] = samplerState != null ? samplerState : SamplerState.LinearClamp; - if (cachedTransformMatrix == null || GraphicsDevice.Viewport.Width != viewportWidth || GraphicsDevice.Viewport.Height != viewportHeight) + if (cachedTransformMatrix == null || GraphicsDevice.Viewport.Width != viewportWidth || + GraphicsDevice.Viewport.Height != viewportHeight) { this.viewportWidth = GraphicsDevice.Viewport.Width; this.viewportHeight = GraphicsDevice.Viewport.Height; @@ -518,14 +595,18 @@ namespace ANX.Framework.Graphics cachedTransformMatrix.M42 -= cachedTransformMatrix.M22; } - this.spriteBatchEffect.Parameters["MatrixTransform"].SetValue(this.transformMatrix * cachedTransformMatrix); + Matrix result; + Matrix.Multiply(ref transformMatrix, ref cachedTransformMatrix, out result); + this.spriteBatchEffect.Parameters["MatrixTransform"].SetValue(result); spriteBatchEffect.NativeEffect.Apply(GraphicsDevice); GraphicsDevice.Indices = this.indexBuffer; GraphicsDevice.SetVertexBuffer(this.vertexBuffer); - } + } + #endregion - public override void Dispose() + #region Dispose + public override void Dispose() { if (this.spriteBatchEffect != null) { @@ -550,19 +631,18 @@ namespace ANX.Framework.Graphics { throw new NotImplementedException(); } + #endregion - private class TextureComparer : IComparer + private class TextureComparer : IComparer { public int Compare(SpriteInfo x, SpriteInfo y) { - if (x.texture.GetHashCode() > y.texture.GetHashCode()) - { + int hash1 = x.texture.GetHashCode(); + int hash2 = y.texture.GetHashCode(); + if (hash1 > hash2) return -1; - } - else if (x.texture.GetHashCode() < y.texture.GetHashCode()) - { + else if (hash1 < hash2) return 1; - } return 0; } @@ -573,13 +653,9 @@ namespace ANX.Framework.Graphics public int Compare(SpriteInfo x, SpriteInfo y) { if (x.layerDepth > y.layerDepth) - { return 1; - } else if (x.layerDepth < y.layerDepth) - { return -1; - } return 0; } @@ -590,17 +666,12 @@ namespace ANX.Framework.Graphics public int Compare(SpriteInfo x, SpriteInfo y) { if (x.layerDepth > y.layerDepth) - { return -1; - } else if (x.layerDepth < y.layerDepth) - { return 1; - } return 0; } } - } } diff --git a/ANX.Framework/Graphics/SpriteFont.cs b/ANX.Framework/Graphics/SpriteFont.cs index 5ed39660..669121ee 100644 --- a/ANX.Framework/Graphics/SpriteFont.cs +++ b/ANX.Framework/Graphics/SpriteFont.cs @@ -1,9 +1,8 @@ -#region Using Statements using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; -#endregion // Using Statements +using ANX.Framework.NonXNA.Development; // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. @@ -11,252 +10,262 @@ using System.Text; namespace ANX.Framework.Graphics { + [PercentageComplete(100)] + [TestState(TestStateAttribute.TestState.Untested)] + [Developer("Glatzemann, AstrorEnales")] public sealed class SpriteFont { - #region Private Members + #region Private private Texture2D texture; - private List glyphs; - private List cropping; + private Rectangle[] glyphs; + private Rectangle[] cropping; private List characterMap; - private int lineSpacing; - private float horizontalSpacing; - private List kerning; - private char? defaultCharacter; - private ReadOnlyCollection characters; + private Vector3[] kernings; + private char? defaultCharacter; + Vector2 topLeft; + Vector2 position; + #endregion - #endregion // Private Members - - public ReadOnlyCollection Characters - { - get { return characters; } - } + #region Public + public ReadOnlyCollection Characters { get; private set; } + public int LineSpacing { get; set; } + public float Spacing { get; set; } public char? DefaultCharacter { get { return defaultCharacter; } set { - if (value.HasValue && !this.characterMap.Contains(value.Value)) - { + if (value.HasValue && this.characterMap.Contains(value.Value) == false) throw new NotSupportedException("Character is not in used font"); - } + this.defaultCharacter = value; } } + #endregion - public int LineSpacing - { - get { return lineSpacing; } - set { lineSpacing = value; } - } - - public float Spacing - { - get { return horizontalSpacing; } - set { horizontalSpacing = value; } - } - - - internal SpriteFont( - Texture2D texture, List glyphs, List cropping, List charMap, - int lineSpacing, float horizontalSpacing, List kerning, char? defaultCharacter) + #region Constructor + internal SpriteFont(Texture2D texture, List glyphs, List cropping, List charMap, + int lineSpacing, float horizontalSpacing, List kerning, char? defaultCharacter) { this.texture = texture; - this.glyphs = glyphs; - this.cropping = cropping; + this.glyphs = glyphs.ToArray(); + this.cropping = cropping.ToArray(); this.characterMap = charMap; - this.lineSpacing = lineSpacing; - this.horizontalSpacing = horizontalSpacing; - this.kerning = kerning; + this.LineSpacing = lineSpacing; + this.Spacing = horizontalSpacing; + this.kernings = kerning.ToArray(); this.defaultCharacter = defaultCharacter; - this.characters = new ReadOnlyCollection(characterMap); + Characters = new ReadOnlyCollection(characterMap); } + #endregion - public Vector2 MeasureString(string text) + #region MeasureString + public Vector2 MeasureString(string text) { if (text == null) - { throw new ArgumentNullException("text"); - } - return InternalMeasure(ref text); + return InternalMeasure(text); } public Vector2 MeasureString(StringBuilder text) { if (text == null) - { throw new ArgumentNullException("text"); - } - String cachedText = text.ToString(); - return InternalMeasure(ref cachedText); + return InternalMeasure(text.ToString()); } + #endregion - internal void DrawString(ref String text, SpriteBatch spriteBatch, Vector2 textPos, Color color, Vector2 scale, Vector2 origin, float rotation, float layerDepth, SpriteEffects effects) + #region DrawString + internal void DrawString(string text, SpriteBatch spriteBatch, Vector2 textPos, Color color, Vector2 scale, + Vector2 origin, float rotation, float layerDepth, SpriteEffects effects) { - Matrix transformation = Matrix.CreateRotationZ(rotation) * Matrix.CreateTranslation(-origin.X * scale.X, -origin.Y * scale.Y, 0f); + Matrix rotationMatrix; + Matrix.CreateRotationZ(rotation, out rotationMatrix); + Matrix translationMatrix; + Matrix.CreateTranslation(-origin.X * scale.X, -origin.Y * scale.Y, 0f, out translationMatrix); + Matrix transformation; + Matrix.Multiply(ref rotationMatrix, ref translationMatrix, out transformation); + + topLeft.X = topLeft.Y = 0f; int horizontalFlipModifier = 1; float width = 0f; - Vector2 topLeft = new Vector2(); - bool firstCharacterInLine = true; + bool flipVertically = (effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; + bool flipHorizontally = (effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; - if ((effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) + if (flipHorizontally) { - topLeft.X = width = this.InternalMeasure(ref text).X * scale.X; + topLeft.X = width = InternalMeasure(text).X * scale.X; horizontalFlipModifier = -1; } - if ((effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) - { - topLeft.Y = (this.InternalMeasure(ref text).Y - this.lineSpacing) * scale.Y; - } + if (flipVertically) + topLeft.Y = (InternalMeasure(text).Y - LineSpacing) * scale.Y; - for (int i = 0; i < text.Length; i++) - { - char currentCharacter = text[i]; - switch (currentCharacter) - { - case '\r': - break; + bool firstCharacterInLine = true; + for (int i = 0; i < text.Length; i++) + { + char currentCharacter = text[i]; + if (currentCharacter == '\r') + continue; - case '\n': - firstCharacterInLine = true; - topLeft.X = width; - if ((effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) - { - topLeft.Y -= this.lineSpacing * scale.Y; - } - else - { - topLeft.Y += this.lineSpacing * scale.Y; - } - break; + if (currentCharacter == '\n') + { + firstCharacterInLine = true; + topLeft.X = width; + float factor = LineSpacing * scale.Y; + topLeft.Y += flipVertically ? -factor : factor; + continue; + } - default: - { - int characterIndex = GetIndexForCharacter(currentCharacter); - Vector3 kerning = this.kerning[characterIndex]; - Rectangle glyphRect = this.glyphs[characterIndex]; - Rectangle croppingRect = this.cropping[characterIndex]; + int characterIndex = GetIndexForCharacter(currentCharacter); + Vector3 kerning = kernings[characterIndex]; + Rectangle croppingRect = cropping[characterIndex]; - if (firstCharacterInLine) - { - kerning.X = Math.Max(kerning.X, 0f); - } - else - { - topLeft.X += (this.Spacing * scale.X) * horizontalFlipModifier; - } - topLeft.X += (kerning.X * scale.X) * horizontalFlipModifier; + if (firstCharacterInLine) + kerning.X = Math.Max(kerning.X, 0f); + else + topLeft.X += (Spacing * scale.X) * horizontalFlipModifier; - if ((effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) - { - croppingRect.Y = (this.lineSpacing - glyphRect.Height) - croppingRect.Y; - } - if ((effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) - { - croppingRect.X -= croppingRect.Width; - } - Vector2 position = Vector2.Transform(topLeft + (new Vector2(croppingRect.X, croppingRect.Y) * scale), transformation); - spriteBatch.Draw(this.texture, position + textPos, glyphRect, color, rotation, Vector2.Zero, scale, effects, layerDepth); - firstCharacterInLine = false; - topLeft.X += ((kerning.Y + kerning.Z) * scale.X) * horizontalFlipModifier; - break; - } - } - } + topLeft.X += (kerning.X * scale.X) * horizontalFlipModifier; + + if (flipVertically) + croppingRect.Y = (LineSpacing - glyphs[characterIndex].Height) - croppingRect.Y; + + if (flipHorizontally) + croppingRect.X -= croppingRect.Width; + + position.X = topLeft.X + (croppingRect.X * scale.X); + position.Y = topLeft.Y + (croppingRect.Y * scale.Y); + Vector2 result; + Vector2.Transform(ref position, ref transformation, out result); + result.X += textPos.X; + result.Y += textPos.Y; + spriteBatch.DrawOptimizedText(texture, result, ref glyphs[characterIndex], ref color, rotation, scale, + effects, layerDepth); + firstCharacterInLine = false; + topLeft.X += ((kerning.Y + kerning.Z) * scale.X) * horizontalFlipModifier; + } } + #endregion - private Vector2 InternalMeasure(ref String text) + #region DrawString + internal void DrawString(string text, SpriteBatch spriteBatch, Vector2 textPos, Color color) + { + topLeft.X = topLeft.Y = 0f; + bool firstCharacterInLine = true; + for (int i = 0; i < text.Length; i++) + { + char currentCharacter = text[i]; + if (currentCharacter == '\r') + continue; + + if (currentCharacter == '\n') + { + firstCharacterInLine = true; + topLeft.X = 0f; + topLeft.Y += LineSpacing; + continue; + } + + int characterIndex = GetIndexForCharacter(currentCharacter); + Vector3 kerning = kernings[characterIndex]; + Rectangle croppingRect = cropping[characterIndex]; + + if (firstCharacterInLine) + kerning.X = Math.Max(kerning.X, 0f); + else + topLeft.X += Spacing; + + topLeft.X += kerning.X; + + position.X = topLeft.X + croppingRect.X + textPos.X; + position.Y = topLeft.Y + croppingRect.Y + textPos.Y; + spriteBatch.DrawOptimizedText(texture, position, ref glyphs[characterIndex], ref color); + firstCharacterInLine = false; + topLeft.X += kerning.Y + kerning.Z; + } + } + #endregion + + #region InternalMeasure + private Vector2 InternalMeasure(string text) { if (text.Length < 1) - { return Vector2.Zero; - } Vector2 size = Vector2.Zero; - size.Y = this.lineSpacing; + size.Y = this.LineSpacing; float maxWidth = 0f; int currentCharacter = 0; float z = 0f; bool firstCharacterInLine = true; - for (int i = 0; i < text.Length; i++) - { - char currentChar = text[i]; + for (int i = 0; i < text.Length; i++) + { + char currentChar = text[i]; + if (currentChar == '\r') + continue; - if (currentChar == '\r') - { - continue; - } + if (currentChar == '\n') + { + size.X += Math.Max(z, 0f); + z = 0f; + maxWidth = Math.Max(size.X, maxWidth); + size = Vector2.Zero; + size.Y = LineSpacing; + firstCharacterInLine = true; + currentCharacter++; + continue; + } - if (currentChar == '\n') - { - size.X += Math.Max(z, 0f); - z = 0f; - maxWidth = Math.Max(size.X, maxWidth); - size = Vector2.Zero; - size.Y = this.lineSpacing; - firstCharacterInLine = true; - currentCharacter++; - } - else - { - int currentCharIndex = this.GetIndexForCharacter(currentChar); - Vector3 kerning = this.kerning[currentCharIndex]; - if (firstCharacterInLine) - { - kerning.X = Math.Max(kerning.X, 0f); - } - else - { - size.X += this.Spacing + z; - } - size.X += kerning.X + kerning.Y; - z = kerning.Z; - size.Y = Math.Max(size.Y, (float)this.cropping[currentCharIndex].Height); - firstCharacterInLine = false; - } - } - size.Y += currentCharacter * this.lineSpacing; + int currentCharIndex = GetIndexForCharacter(currentChar); + Vector3 kerning = kernings[currentCharIndex]; + if (firstCharacterInLine) + kerning.X = Math.Max(kerning.X, 0f); + else + size.X += this.Spacing + z; + + size.X += kerning.X + kerning.Y; + z = kerning.Z; + size.Y = Math.Max(size.Y, (float)cropping[currentCharIndex].Height); + firstCharacterInLine = false; + } + + size.Y += currentCharacter * LineSpacing; size.X = Math.Max(Math.Max(z, 0) + size.X, maxWidth); return size; - } + } + #endregion - private int GetIndexForCharacter(char character) - { - int currentIndex = 0; - int upperBound = this.characterMap.Count - 1; - char testChar; - int searchPos; + #region GetIndexForCharacter + private int GetIndexForCharacter(char character) + { + int currentIndex = 0; + int upperBound = this.characterMap.Count - 1; + char testChar; + int searchPos; - while (currentIndex <= upperBound) - { - searchPos = currentIndex + ((upperBound - currentIndex) >> 1); - testChar = this.characterMap[searchPos]; - if (testChar == character) - { - return searchPos; - } - else if (testChar > character) - { - upperBound = searchPos - 1; - } - else - { - currentIndex = searchPos + 1; - } - } + while (currentIndex <= upperBound) + { + searchPos = currentIndex + ((upperBound - currentIndex) >> 1); + testChar = characterMap[searchPos]; + if (testChar == character) + return searchPos; + else if (testChar > character) + upperBound = searchPos - 1; + else + currentIndex = searchPos + 1; + } - if (this.defaultCharacter.HasValue) - { - return this.GetIndexForCharacter(this.defaultCharacter.Value); - } - - throw new ArgumentException("character not found"); - } + if (defaultCharacter.HasValue) + return GetIndexForCharacter(defaultCharacter.Value); + throw new ArgumentException("character not found"); + } + #endregion } } diff --git a/ANX.Framework/Graphics/SpriteInfo.cs b/ANX.Framework/Graphics/SpriteInfo.cs index ea123788..e84972c8 100644 --- a/ANX.Framework/Graphics/SpriteInfo.cs +++ b/ANX.Framework/Graphics/SpriteInfo.cs @@ -8,16 +8,13 @@ namespace ANX.Framework.Graphics { internal struct SpriteInfo { - //public Vector2 TopLeft; - //public Vector2 BottomRight; - public Vector2[] Corners; public Color Tint; public Texture2D texture; - public Single rotation; + public float rotation; public Vector2 origin; - public Single layerDepth; + public float layerDepth; public Vector2 topLeftUV; public Vector2 bottomRightUV; diff --git a/ANX.Framework/Graphics/Texture2D.cs b/ANX.Framework/Graphics/Texture2D.cs index 57509efc..5ad124c0 100644 --- a/ANX.Framework/Graphics/Texture2D.cs +++ b/ANX.Framework/Graphics/Texture2D.cs @@ -16,6 +16,9 @@ namespace ANX.Framework.Graphics protected internal int width; protected internal int height; + internal float OneOverWidth; + internal float OneOverHeight; + private INativeTexture2D nativeTexture2D; private INativeTexture2D NativeTexture2D { @@ -66,6 +69,8 @@ namespace ANX.Framework.Graphics { this.width = width; this.height = height; + OneOverWidth = 1f / width; + OneOverHeight = 1f / height; base.levelCount = 1; base.format = SurfaceFormat.Color; @@ -79,6 +84,9 @@ namespace ANX.Framework.Graphics { this.width = width; this.height = height; + OneOverWidth = 1f / width; + OneOverHeight = 1f / height; + // TODO: pass the mipmap parameter to the creation of the texture to let the graphics card generate mipmaps! base.levelCount = 1; base.format = format; @@ -91,6 +99,8 @@ namespace ANX.Framework.Graphics { this.width = width; this.height = height; + OneOverWidth = 1f / width; + OneOverHeight = 1f / height; base.levelCount = mipCount; base.format = format; diff --git a/ANX.Framework/Graphics/VertexBuffer.cs b/ANX.Framework/Graphics/VertexBuffer.cs index cd7ceb3c..91552b57 100644 --- a/ANX.Framework/Graphics/VertexBuffer.cs +++ b/ANX.Framework/Graphics/VertexBuffer.cs @@ -16,9 +16,6 @@ namespace ANX.Framework.Graphics public class VertexBuffer : GraphicsResource, IGraphicsResource { #region Private - private VertexDeclaration vertexDeclaration; - private int vertexCount; - private BufferUsage bufferUsage; private INativeVertexBuffer nativeVertexBuffer; #endregion @@ -29,55 +26,31 @@ namespace ANX.Framework.Graphics { get { - if (this.nativeVertexBuffer == null) - { + if (nativeVertexBuffer == null) CreateNativeBuffer(); - } - return this.nativeVertexBuffer; + return nativeVertexBuffer; } } - public BufferUsage BufferUsage - { - get - { - return this.bufferUsage; - } - } - - public int VertexCount - { - get - { - return this.vertexCount; - } - } - - public VertexDeclaration VertexDeclaration - { - get - { - return this.vertexDeclaration; - } - } + public BufferUsage BufferUsage { get; private set; } + public int VertexCount { get; private set; } + public VertexDeclaration VertexDeclaration { get; private set; } #endregion #region Constructor - public VertexBuffer(GraphicsDevice graphicsDevice, Type vertexType, - int vertexCount, BufferUsage usage) - : this(graphicsDevice, VertexBuffer.TypeToVertexDeclaration(vertexType), - vertexCount, usage) + public VertexBuffer(GraphicsDevice graphicsDevice, Type vertexType, int vertexCount, BufferUsage usage) + : this(graphicsDevice, VertexTypeHelper.GetDeclaration(vertexType), vertexCount, usage) { } - public VertexBuffer(GraphicsDevice graphicsDevice, - VertexDeclaration vertexDeclaration, int vertexCount, BufferUsage usage) + public VertexBuffer(GraphicsDevice graphicsDevice, VertexDeclaration vertexDeclaration, int vertexCount, + BufferUsage usage) : base(graphicsDevice) { - this.vertexCount = vertexCount; - this.vertexDeclaration = vertexDeclaration; - this.bufferUsage = usage; + VertexCount = vertexCount; + VertexDeclaration = vertexDeclaration; + BufferUsage = usage; base.GraphicsDevice.ResourceCreated += GraphicsDevice_ResourceCreated; base.GraphicsDevice.ResourceDestroyed += GraphicsDevice_ResourceDestroyed; @@ -120,17 +93,16 @@ namespace ANX.Framework.Graphics #region CreateNativeBuffer private void CreateNativeBuffer() { - this.nativeVertexBuffer = - AddInSystemFactory.Instance.GetDefaultCreator().CreateVertexBuffer(GraphicsDevice, this, vertexDeclaration, vertexCount, bufferUsage); + var creator = AddInSystemFactory.Instance.GetDefaultCreator(); + this.nativeVertexBuffer = creator.CreateVertexBuffer(GraphicsDevice, this, VertexDeclaration, VertexCount, + BufferUsage); } #endregion #region GetData - public void GetData(int offsetInBytes, T[] data, int startIndex, - int elementCount, int vertexStride) where T : struct + public void GetData(int offsetInBytes, T[] data, int startIndex, int elementCount, int vertexStride) where T : struct { - NativeVertexBuffer.GetData(offsetInBytes, data, startIndex, - elementCount, vertexStride); + NativeVertexBuffer.GetData(offsetInBytes, data, startIndex, elementCount, vertexStride); } public void GetData(T[] data) where T : struct @@ -138,19 +110,16 @@ namespace ANX.Framework.Graphics NativeVertexBuffer.GetData(data); } - public void GetData(T[] data, int startIndex, int elementCount) - where T : struct + public void GetData(T[] data, int startIndex, int elementCount) where T : struct { NativeVertexBuffer.GetData(data, startIndex, elementCount); } #endregion #region SetData - public void SetData(int offsetInBytes, T[] data, int startIndex, - int elementCount, int vertexStride) where T : struct + public void SetData(int offsetInBytes, T[] data, int startIndex, int elementCount, int vertexStride) where T : struct { - NativeVertexBuffer.SetData(GraphicsDevice, offsetInBytes, data, - startIndex, elementCount, vertexStride); + NativeVertexBuffer.SetData(GraphicsDevice, offsetInBytes, data, startIndex, elementCount, vertexStride); } public void SetData(T[] data) where T : struct @@ -164,19 +133,6 @@ namespace ANX.Framework.Graphics } #endregion - #region TypeToVertexDeclaration - private static VertexDeclaration TypeToVertexDeclaration(Type t) - { - IVertexType vt = Activator.CreateInstance(t) as IVertexType; - if (vt != null) - { - return vt.VertexDeclaration; - } - - return null; - } - #endregion - #region Dispose public override void Dispose() { @@ -186,16 +142,17 @@ namespace ANX.Framework.Graphics nativeVertexBuffer = null; } - if (vertexDeclaration != null) - { - // do not dispose the VertexDeclaration here, because it's only a reference - vertexDeclaration = null; - } + // do not dispose the VertexDeclaration here, because it's only a reference + if (VertexDeclaration != null) + VertexDeclaration = null; } protected override void Dispose([MarshalAs(UnmanagedType.U1)] bool disposeManaged) { - throw new NotImplementedException(); + if (disposeManaged) + { + Dispose(); + } } #endregion } diff --git a/ANX.Framework/NonXNA/RenderSystem/VertexTypeHelper.cs b/ANX.Framework/NonXNA/RenderSystem/VertexTypeHelper.cs index 283176db..6efc8329 100644 --- a/ANX.Framework/NonXNA/RenderSystem/VertexTypeHelper.cs +++ b/ANX.Framework/NonXNA/RenderSystem/VertexTypeHelper.cs @@ -2,6 +2,10 @@ using System.Collections.Generic; using ANX.Framework.Graphics; +// This file is part of the ANX.Framework created by the +// "ANX.Framework developer group" and released under the Ms-PL license. +// For details see: http://anxframework.codeplex.com/license + namespace ANX.Framework.NonXNA.RenderSystem { internal static class VertexTypeHelper @@ -14,7 +18,17 @@ namespace ANX.Framework.NonXNA.RenderSystem if (rememberedInstances.ContainsKey(type) == false) rememberedInstances.Add(type, Activator.CreateInstance()); - return rememberedInstances[type].VertexDeclaration; + var result = rememberedInstances[type]; + return result != null ? result.VertexDeclaration : null; + } + + public static VertexDeclaration GetDeclaration(Type type) + { + if (rememberedInstances.ContainsKey(type) == false) + rememberedInstances.Add(type, Activator.CreateInstance(type) as IVertexType); + + var result = rememberedInstances[type]; + return result != null ? result.VertexDeclaration : null; } } } diff --git a/Samples/TextRendering/Game1.cs b/Samples/TextRendering/Game1.cs index e122ccb2..b6a9b2bb 100644 --- a/Samples/TextRendering/Game1.cs +++ b/Samples/TextRendering/Game1.cs @@ -21,11 +21,18 @@ namespace TextRendering SpriteFont debugFont; + int fps = 60; + int fpsCount = 0; + float fpsTimer = 0f; + public Game1() { graphics = new GraphicsDeviceManager(this); graphics.PreparingDeviceSettings += new EventHandler(graphics_PreparingDeviceSettings); Content.RootDirectory = "SampleContent"; + + IsFixedTimeStep = false; + graphics.SynchronizeWithVerticalRetrace = false; } void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e) @@ -55,6 +62,15 @@ namespace TextRendering if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); + fpsCount++; + fpsTimer += (float)gameTime.ElapsedGameTime.TotalSeconds; + if (fpsTimer >= 1f) + { + fpsTimer -= 1f; + fps = fpsCount; + fpsCount = 0; + } + base.Update(gameTime); } @@ -63,7 +79,7 @@ namespace TextRendering GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(SpriteSortMode.FrontToBack, null); - spriteBatch.DrawString(this.debugFont, "Hello World!", new Vector2(100, 100), Color.White); + spriteBatch.DrawString(this.debugFont, "Hello World! FPS: " + fps, new Vector2(100, 100), Color.White); spriteBatch.DrawString(this.debugFont, "This screen is powered by the ANX.Framework!\r\nsecond line", new Vector2(100, 100 + this.debugFont.LineSpacing), Color.Black, 0.0f, new Vector2(1, -1), Vector2.One, SpriteEffects.None, 0.0f); spriteBatch.DrawString(this.debugFont, "This screen is powered by the ANX.Framework!\r\nsecond line", new Vector2(100, 100 + this.debugFont.LineSpacing), Color.Red, 0.0f, Vector2.Zero, Vector2.One, SpriteEffects.None, 1.0f); diff --git a/Samples/TextRendering/Program.cs b/Samples/TextRendering/Program.cs index 6ae378d7..e00fdf16 100644 --- a/Samples/TextRendering/Program.cs +++ b/Samples/TextRendering/Program.cs @@ -11,6 +11,7 @@ namespace TextRendering static void Main(string[] args) { //AddInSystemFactory.Instance.SetPreferredSystem(AddInType.RenderSystem, "OpenGL3"); + AddInSystemFactory.Instance.SetPreferredSystem(AddInType.RenderSystem, "DirectX10"); //AddInSystemFactory.Instance.SetPreferredSystem(AddInType.RenderSystem, "DirectX11"); using (Game1 game = new Game1())