using System; using ANX.Framework.NonXNA; 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 readonly 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()) { 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) { 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() { var creator = AddInSystemFactory.Instance.GetDefaultCreator(); return creator.GetShaderByteCode(PreDefinedShader.SkinnedEffect); } #endregion #region GetSourceLanguage private static EffectSourceLanguage GetSourceLanguage() { var creator = AddInSystemFactory.Instance.GetDefaultCreator(); return creator.GetStockShaderSourceLanguage; } #endregion #region Clone public override Effect Clone() { return new SkinnedEffect(this); } #endregion #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 #region EnableDefaultLighting public void EnableDefaultLighting() { LightingEnabled = true; ambientLightColor = new Vector3(0.05333332f, 0.09882354f, 0.1819608f); 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; 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; 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 #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 + "!"); var result = new Matrix[MaxBones]; for (int index = 0; index < MaxBones; index++) { result[index] = bones[index]; } return result; } #endregion #region SetBoneTransforms public void SetBoneTransforms(Matrix[] boneTransforms) { if (boneTransforms == null || boneTransforms.Length == 0) throw new ArgumentNullException("boneTransforms"); if (boneTransforms.Length > 72) throw new ArgumentException("The maximum number of allowed bones for the SkinnedEffect is " + MaxBones + "!"); for (int index = 0; index < MaxBones; index++) bones[index] = index < boneTransforms.Length ? boneTransforms[index] : Matrix.Identity; } #endregion protected internal override void OnApply() { Matrix worldView; Matrix.Multiply(ref world, ref view, out worldView); Matrix wvp; Matrix.Multiply(ref worldView, ref projection, out wvp); Parameters["WorldViewProj"].SetValue(wvp); SetLightingMatrices(); SetMaterialColor(); Parameters["Bones"].SetValue(bones); Parameters["FogColor"].SetValue(fogColor); Parameters["SpecularPower"].SetValue(SpecularPower); Parameters["SpecularColor"].SetValue(specularColor); if (Texture != null) Parameters["Texture"].SetValue(Texture); if (isFogEnabled) SetFogVector(ref worldView); else Parameters["FogVector"].SetValue(Vector4.Zero); SelectTechnique(); } #region SelectTechnique private void SelectTechnique() { string name = ""; if (WeightsPerVertex == 1) name = "OneBone"; else if (WeightsPerVertex == 2) name = "TwoBones"; else if (WeightsPerVertex == 4) name = "FourBones"; bool oneLight = DirectionalLight1.Enabled == false && DirectionalLight2.Enabled == false; if (preferPerPixelLighting) name += "PixelLighting"; else if (oneLight) name += "OneLight"; else name += "VertexLighting"; if (isFogEnabled == false) name += "NoFog"; CurrentTechnique = Techniques[name]; } #endregion #region SetLightingMatrices private void SetLightingMatrices() { Matrix worldInverse; Matrix.Invert(ref world, out worldInverse); Matrix worldInverseTranspose; Matrix.Transpose(ref worldInverse, out worldInverseTranspose); Parameters["World"].SetValue(world); Parameters["WorldInverseTranspose"].SetValue(worldInverseTranspose); 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 } }