/ContentSystem/Rendering/ShaderData.cs
C# | 621 lines | 405 code | 46 blank | 170 comment | 26 complexity | e603990c7902bf4a461779dce71600aa MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Collections.Generic;
- using System.IO;
- using Delta.Engine;
- using Delta.Utilities;
- using Delta.Utilities.Graphics;
- using Delta.Utilities.Graphics.ShaderFeatures;
- using Delta.Utilities.Helpers;
-
- namespace Delta.ContentSystem.Rendering
- {
- /// <summary>
- /// Shader data, currently just a list of ShaderFeatures, which are high
- /// level classes providing shader functionality and parameters. These
- /// contain currently fixed code paths and will generate data for the final
- /// fx, glsl and glessl shaders. All this will be more dynamic later when
- /// we can directly edit all shader nodes and put those things into modules,
- /// which are similar to our ShaderFeatures now.
- /// </summary>
- public class ShaderData : Content, ISaveLoadBinary
- {
- #region Constants
- /// <summary>
- /// Version number for this ShaderData. If this goes above 1, we need
- /// to support loading older versions as well. Saving is always going
- /// to be the latest version (this one).
- /// </summary>
- private const int VersionNumber = 1;
-
- /// <summary>
- /// Default extension for Delta Engine shader files. Only used in the
- /// shader content processors and generators.
- /// </summary>
- public const string Extension = "DeltaShader";
- #endregion
-
- #region Get (Static)
- /// <summary>
- /// Get and load content based on the content name. This method makes sure
- /// we do not load the same content twice (the constructor is protected).
- /// Can also be used to create empty shader data object without having to
- /// invoke the ContentSystem (with "", for Delta.ContentPlugins.Shader).
- /// </summary>
- /// <param name="contentName">Content name we want to load, this is
- /// passed onto the Content System, which will do the actual loading with
- /// help of the Load method in this class.</param>
- /// <returns>The loaded or empty Content object, always unique for the same
- /// name, this helps comparing data.</returns>
- public static ShaderData Get(string contentName)
- {
- return Get<ShaderData>(contentName, ContentType.Shader);
- }
-
- /// <summary>
- /// Get or load a shader without the content name, but instead by searching
- /// for the given shaderFlags combination. Used to find shaders by
- /// features, not by the content name, which is not that important or
- /// interesting for shader creation with code. If nothing was found it
- /// still can provide a fallback (Basic shader) or crash if that fails too.
- /// </summary>
- /// <param name="shaderFlags">The shader flags.</param>
- /// <returns>
- /// Loaded shader data (if nothing was found the fallback shader
- /// TexturedShader2D is used)
- /// </returns>
- public static ShaderData Get(ShaderFeatureFlags shaderFlags)
- {
- Dictionary<string, ContentMetaData> allShaderContent =
- ContentManager.GetAllContentMetaData(ContentType.Shader);
- foreach (ContentMetaData shaderContent in allShaderContent.Values)
- {
- // Note: ShaderFlags is used for ShaderFeatureFlags.
- if (shaderContent.ShaderFlags == shaderFlags)
- {
- // Just load the found entry
- return Get(shaderContent.Name);
- }
- } // foreach
-
- // Nothing found? Then load the fallback shader!
- // Only warn if the application is still running and was not aborted.
- if (Application.IsShuttingDown == false)
- {
- Log.Warning(
- "Unable to find shader with flags: " + shaderFlags +
- ". Trying to use one of the 6 best matching fallback shaders " +
- "(basic, line, textured, colored, 2d, 3d)");
- }
- bool is2D = IsShaderFeatureFlagOn(shaderFlags, ShaderFeatureFlags.UI2D);
- if (IsShaderFeatureFlagOn(shaderFlags,
- ShaderFeatureFlags.ColoredVertices))
- {
- if (IsShaderFeatureFlagOn(shaderFlags,
- ShaderFeatureFlags.NoTexturing))
- {
- return Get(
- is2D
- ? "LineShader2D"
- : "LineShader3D");
- }
- return Get(
- is2D
- ? "TexturedColoredShader2D"
- : "TexturedColoredShader3D");
- }
- return Get(
- is2D
- ? "TexturedShader2D"
- : "TexturedShader3D");
- }
- #endregion
-
- #region ShaderCode (Public)
- /// <summary>
- /// Shader code to be compiled for this platform. Provided by the content
- /// system in the format this platform supports (.fx, .glsl, .glessl).
- /// </summary>
- public string ShaderCode = "";
- #endregion
-
- #region Flags (Public)
- /// <summary>
- /// By default all shader only have the basic features. If more features
- /// are added or removed this must be updated as well. Mainly used for
- /// optimizations, fall backs and fixed function code paths to support
- /// similar effects like defined in the shader code.
- /// </summary>
- public ShaderFeatureFlags Flags = ShaderFeatureFlags.Basic;
- #endregion
-
- #region UsesTexturing (Public)
- /// <summary>
- /// Does this shader use texturing? Always true except when the NoTexturing
- /// shader feature flag was specified (only used for line shaders).
- /// </summary>
- public bool UsesTexturing
- {
- get
- {
- return IsFeatureOn(ShaderFeatureFlags.NoTexturing) == false;
- }
- }
- #endregion
-
- #region UsesVertexColoring (Public)
- /// <summary>
- /// Uses vertex coloring, only true if ColoredVertices was specified.
- /// </summary>
- public bool UsesVertexColoring
- {
- get
- {
- return IsFeatureOn(ShaderFeatureFlags.ColoredVertices);
- }
- }
- #endregion
-
- #region UseSkinning (Public)
- /// <summary>
- /// Use skinning
- /// </summary>
- public bool UseSkinning
- {
- get
- {
- return IsFeatureOn(ShaderFeatureFlags.UseSkinning);
- }
- }
- #endregion
-
- #region UseAlphaTest (Public)
- /// <summary>
- /// UseAlphaTest
- /// </summary>
- public bool UseAlphaTest
- {
- get
- {
- return IsFeatureOn(ShaderFeatureFlags.AlphaTest);
- }
- }
- #endregion
-
- #region UseLightMap (Public)
- /// <summary>
- /// Use light map
- /// </summary>
- public bool UseLightMap
- {
- get
- {
- return IsFeatureOn(ShaderFeatureFlags.LightMap);
- }
- }
- #endregion
-
- #region VertexFormat (Public)
- /// <summary>
- /// Vertex format, set in the constructor! Very important to make
- /// decisions and optimizations. This should not be null, else classes like
- /// GeometryData will crash with NullReferenceException (they rely on
- /// valid vertex formats).
- /// </summary>
- public VertexFormat VertexFormat =
- VertexFormat.Position2DTextured;
- #endregion
-
- #region Private
-
- #region features (Private)
- /// <summary>
- /// List of shader features this shader currently has. Should be always
- /// synchronized up with featureFlags! Use Add and Remove to add and remove
- /// shader features.
- /// </summary>
- private readonly List<IShaderFeature> features = new List<IShaderFeature>();
- #endregion
-
- #region cachedParameters (Private)
- /// <summary>
- /// Cached shader parameters and their default or constant values for this
- /// shader. Set like features and featureFlags in Add and Remove. If the
- /// features change over time you need to re-add them or just save and
- /// load the whole ShaderData (this dictionary is supposed to be fast, we
- /// don't want to go through all features and search for all parameters).
- /// </summary>
- private readonly Dictionary<string, object> cachedParameters =
- new Dictionary<string, object>();
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create shader data by loading it from content right away.
- /// Use the static Get method to call this.
- /// </summary>
- /// <param name="contentName">Name of the content.</param>
- protected ShaderData(string contentName)
- : base(contentName, ContentType.Shader)
- {
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Load shader data from binary data stream.
- /// </summary>
- /// <param name="reader">Binary reader of the underlying stream</param>
- public void Load(BinaryReader reader)
- {
- try
- {
- // We currently only support our version, if more versions are added,
- // we need to do different loading code depending on the version here.
- int version = reader.ReadInt32();
- if (version != VersionNumber)
- {
- Log.InvalidVersionWarning("ShaderData: " + Name, version,
- VersionNumber);
- return;
- }
-
- // Load combination of all flags (most important)
- Flags = (ShaderFeatureFlags)reader.ReadInt32();
- if (Flags != data.ShaderFlags)
- {
- Log.Warning("The shader flags from this shader content " + Name +
- ": '" + Flags + "' are different from the meta data " +
- "ShaderFlags: '" + data.ShaderFlags + "'. Should not happen!");
- }
-
- // Load the shader code.
- ShaderCode = reader.ReadString();
-
- // Load the vertex format.
- VertexFormat = new VertexFormat(reader);
-
- // Check if vertex format is correct, do some basic tests!
- if (VertexFormat.Elements.Length == 0 ||
- VertexFormat.Elements[0].Size == 0 ||
- VertexFormat.LengthInBytes == 0)
- {
- Log.Warning("Invalid vertex format loaded for shader " + Name +
- ", it does not contain any elements or has no length in bytes. " +
- "Reverting to default Position2DTextured VertexFormat!");
- VertexFormat =
- VertexFormat.Position2DTextured;
- FailedToLoad = true;
- }
-
- // And finally the flags
- int numOfFeatures = reader.ReadInt32();
- features.Clear();
- for (int i = 0; i < numOfFeatures; i++)
- {
- features.Add(LoadShaderFeature(reader));
- }
-
- // If the shader code is empty, output a warning!
- if (String.IsNullOrEmpty(ShaderCode)) //TODO: &&
- // But only if we are not on the content server
- {
- Log.Warning("ShaderCode was not in the content file, make sure " +
- "you use a generated content file for your platform!");
- FailedToLoad = true;
- }
-
- UpdateCachedParameters();
- } // try
- catch (Exception ex)
- {
- Log.Warning("Failed to load shader " + Name + ", check if the " +
- "content file works: " + ex);
- // Setup some flags and data to not crash later.
- Flags = ShaderFeatureFlags.UI2D; //obs:.Basic;
- VertexFormat =
- VertexFormat.Position2DTextured;
- FailedToLoad = true;
- }
- }
-
- /// <summary>
- /// Save shader data, which consists just of features (enum and their
- /// IShaderFeature classes with all their shader parameters in them).
- /// </summary>
- /// <param name="writer">The writer.</param>
- public void Save(BinaryWriter writer)
- {
- writer.Write(VersionNumber);
-
- // Save combination of all flags (most important)
- writer.Write((int)Flags);
-
- // Save the shader code.
- writer.Write(ShaderCode);
-
- // Save the vertex format
- VertexFormat.Save(writer);
-
- // And finally the features (they might change from time to time)
- writer.Write(features.Count);
- foreach (IShaderFeature feature in features)
- {
- writer.Write((int)feature.ShaderFeature);
- feature.Save(writer);
- }
-
- // Note: We don't need to save the cachedParameters because we can
- // easily regenerate them when loading this ShaderData file again.
- }
- #endregion
-
- #region Add (Public)
- /// <summary>
- /// Add a new ShaderFeature to the ShaderData.
- /// </summary>
- /// <param name="newShaderFeature">The new shader feature.</param>
- public void Add(IShaderFeature newShaderFeature)
- {
- foreach (IShaderFeature feature in features)
- {
- if (feature.ShaderFeature == newShaderFeature.ShaderFeature)
- {
- Log.Warning("The shader feature you like to add already exist.");
- return;
- }
- }
-
- features.Add(newShaderFeature);
- // Add the new shader flag.
- Flags |= newShaderFeature.ShaderFeature;
- }
- #endregion
-
- #region Remove (Public)
- /// <summary>
- /// Remove the ShaderFeature from the ShaderData.
- /// </summary>
- /// <param name="featureToRemove">The feature to remove.</param>
- /// <returns>True if we could remove the shader feature, false (and a log
- /// warning) when the feature did not exist here.</returns>
- public bool Remove(IShaderFeature featureToRemove)
- {
- if (features.Contains(featureToRemove))
- {
- Log.Warning("The shader feature you like to remove does not exist.");
- return false;
- }
-
- // Remove the shader flag.
- Flags ^= featureToRemove.ShaderFeature;
-
- return features.Remove(featureToRemove);
- }
- #endregion
-
- #region IsFeatureOn (Public)
- /// <summary>
- /// Is a certain shader flag used? Very simple combined enum check,
- /// but this is used several times here and in higher levels up.
- /// </summary>
- /// <param name="featureToCheck">The feature to check for</param>
- /// <returns>
- /// <c>true</c> if the shader feature to check is on. Otherwise,<c>false</c>
- /// is returned (feature was not found). Note that features can be combined.
- /// </returns>
- public bool IsFeatureOn(ShaderFeatureFlags featureToCheck)
- {
- return (Flags & featureToCheck) == featureToCheck;
- }
- #endregion
-
- #region GetShaderFeatures (Public)
- /// <summary>
- /// Get shader features, needed in the shader editor to list all features.
- /// </summary>
- /// <returns>Shader features</returns>
- public List<IShaderFeature> GetShaderFeatures()
- {
- return features;
- }
- #endregion
-
- #region Methods (Private)
-
- #region IsShaderFeatureFlagOn
- /// <summary>
- /// Helper to check if a specific flag is set, used for Get.
- /// </summary>
- private static bool IsShaderFeatureFlagOn(ShaderFeatureFlags flags,
- ShaderFeatureFlags featureToCheck)
- {
- return (flags & featureToCheck) == featureToCheck;
- }
- #endregion
-
- #region Load
- /// <summary>
- /// Native load method, will just load the xml data.
- /// </summary>
- /// <param name="alreadyLoadedNativeData">
- /// The first instance that has already loaded the required content data
- /// of this content class or just 'null' if there is none loaded yet (or
- /// anymore).
- /// </param>
- protected override void Load(Content alreadyLoadedNativeData)
- {
- try
- {
- if (String.IsNullOrEmpty(RelativeFilePath) == false)
- {
- // Load via the ISaveLoadBinary interface methods below.
- // Cloning should not really happen for shaders anyway.
- FileHelper.Load(RelativeFilePath, this);
- }
- }
- catch (Exception ex)
- {
- Log.Warning(
- "Failed to load shader data from file '" + RelativeFilePath + "': " +
- ex);
- FailedToLoad = true;
- }
- }
- #endregion
-
- #region LoadShaderFeature
- /// <summary>
- /// Load shader feature
- /// </summary>
- /// <param name="reader">Reader to the binary stream for grabbing the
- /// data for the shader feature.</param>
- /// <returns>The loaded shader feature</returns>
- public static IShaderFeature LoadShaderFeature(BinaryReader reader)
- {
- ShaderFeatureFlags featureFlag = (ShaderFeatureFlags)reader.ReadInt32();
- switch (featureFlag)
- {
- case ShaderFeatureFlags.AlphaTest:
- return new AlphaTestShaderFeature(reader);
- case ShaderFeatureFlags.AmbientDiffuse:
- return new AmbientDiffuseShaderFeature(reader);
- case ShaderFeatureFlags.Basic:
- return new BasicShaderFeature(reader);
- case ShaderFeatureFlags.ColoredVertices:
- return new ColoredVerticesShaderFeature(reader);
- case ShaderFeatureFlags.DetailMapping:
- return new DetailMappingShaderFeature(reader);
- case ShaderFeatureFlags.DynamicDirectionalLighting:
- return new DynamicDirectionalLightingShaderFeature(reader);
- case ShaderFeatureFlags.Fresnel:
- return new FresnelShaderFeature(reader);
- case ShaderFeatureFlags.Fog:
- return new FogShaderFeature(reader);
- case ShaderFeatureFlags.LightMap:
- return new LightMapShaderFeature(reader);
- case ShaderFeatureFlags.NormalMap:
- return new NormalMapShaderFeature(reader);
- case ShaderFeatureFlags.ParallaxMap:
- return new ParallaxMapShaderFeature(reader);
- case ShaderFeatureFlags.NoTexturing:
- return new NoTexturingShaderFeature(reader);
- case ShaderFeatureFlags.PostScreenHDR:
- return new PostScreenHDRShaderFeature(reader);
- // return new SkyCubeMappingShaderFeature(reader);
- case ShaderFeatureFlags.SpecularDiffuse:
- return new SpecularDiffuseShaderFeature(reader);
- case ShaderFeatureFlags.SpecularMap:
- return new SpecularMapShaderFeature(reader);
- case ShaderFeatureFlags.Specular:
- return new SpecularShaderFeature(reader);
- case ShaderFeatureFlags.UI2D:
- return new UI2DShaderFeature(reader);
- case ShaderFeatureFlags.VertexCompression:
- return new VertexCompressionShaderFeature(reader);
- case ShaderFeatureFlags.Water:
- return new WaterShaderFeature(reader);
- case ShaderFeatureFlags.UseSkinning:
- return new UseSkinningShaderFeature(reader);
- case ShaderFeatureFlags.GenerateShadowMap:
- return new GenerateShadowMapShaderFeature(reader);
- case ShaderFeatureFlags.UseShadowMap:
- return new UseShadowMapShaderFeature(reader);
- default:
- // Abort with error that this flag was not found (will be catched
- // by the shader loader).
- throw new NotSupportedException(
- "Unsupported ShaderFeatureFlag found: " + featureFlag +
- ", we got no class for this yet!");
- } // switch
- }
-
- public static IShaderFeature LoadShaderFeature(ShaderFeatureFlags flag)
- {
- switch (flag)
- {
- case ShaderFeatureFlags.AlphaTest:
- return new AlphaTestShaderFeature();
- case ShaderFeatureFlags.AmbientDiffuse:
- return new AmbientDiffuseShaderFeature();
- case ShaderFeatureFlags.Basic:
- return new BasicShaderFeature();
- case ShaderFeatureFlags.ColoredVertices:
- return new ColoredVerticesShaderFeature();
- case ShaderFeatureFlags.DetailMapping:
- return new DetailMappingShaderFeature();
- case ShaderFeatureFlags.DynamicDirectionalLighting:
- return new DynamicDirectionalLightingShaderFeature();
- case ShaderFeatureFlags.Fresnel:
- return new FresnelShaderFeature();
- case ShaderFeatureFlags.Fog:
- return new FogShaderFeature();
- case ShaderFeatureFlags.LightMap:
- return new LightMapShaderFeature();
- case ShaderFeatureFlags.NormalMap:
- return new NormalMapShaderFeature();
- case ShaderFeatureFlags.ParallaxMap:
- return new ParallaxMapShaderFeature();
- case ShaderFeatureFlags.NoTexturing:
- return new NoTexturingShaderFeature();
- case ShaderFeatureFlags.PostScreenHDR:
- return new PostScreenHDRShaderFeature();
- case ShaderFeatureFlags.SpecularDiffuse:
- return new SpecularDiffuseShaderFeature();
- case ShaderFeatureFlags.SpecularMap:
- return new SpecularMapShaderFeature();
- case ShaderFeatureFlags.Specular:
- return new SpecularShaderFeature();
- case ShaderFeatureFlags.UI2D:
- return new UI2DShaderFeature();
- case ShaderFeatureFlags.VertexCompression:
- return new VertexCompressionShaderFeature();
- case ShaderFeatureFlags.Water:
- return new WaterShaderFeature();
- case ShaderFeatureFlags.UseSkinning:
- return new UseSkinningShaderFeature();
- case ShaderFeatureFlags.GenerateShadowMap:
- return new GenerateShadowMapShaderFeature();
- case ShaderFeatureFlags.UseShadowMap:
- return new UseShadowMapShaderFeature();
- default:
- // Abort with error that this flag was not found (will be catched
- // by the shader loader).
- throw new NotSupportedException(
- "Unsupported ShaderFeatureFlag found: " + flag +
- ", we got no class for this yet!");
- } // switch
- }
- #endregion
-
- #region UpdateCachedParameters
- /// <summary>
- /// Update cached parameters
- /// </summary>
- private void UpdateCachedParameters()
- {
- try
- {
- cachedParameters.Clear();
- foreach (IShaderFeature feature in features)
- {
- foreach (KeyValuePair<string, object> parameter in
- feature.Parameters)
- {
- cachedParameters.Add(parameter.Key, parameter.Value);
- }
- }
- } // try
- catch (Exception ex)
- {
- Log.Warning(
- "Broken shader features, probably multiple entries with the same " +
- "feature. " + features.Write() + ", Exception: " + ex);
- }
- }
- #endregion
-
- #endregion
- }
- }