PageRenderTime 157ms CodeModel.GetById 60ms app.highlight 53ms RepoModel.GetById 38ms app.codeStats 0ms

/ContentSystem/Rendering/ShaderData.cs

#
C# | 621 lines | 405 code | 46 blank | 170 comment | 26 complexity | e603990c7902bf4a461779dce71600aa MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.IO;
  4using Delta.Engine;
  5using Delta.Utilities;
  6using Delta.Utilities.Graphics;
  7using Delta.Utilities.Graphics.ShaderFeatures;
  8using Delta.Utilities.Helpers;
  9
 10namespace Delta.ContentSystem.Rendering
 11{
 12	/// <summary>
 13	/// Shader data, currently just a list of ShaderFeatures, which are high
 14	/// level classes providing shader functionality and parameters. These
 15	/// contain currently fixed code paths and will generate data for the final
 16	/// fx, glsl and glessl shaders. All this will be more dynamic later when
 17	/// we can directly edit all shader nodes and put those things into modules,
 18	/// which are similar to our ShaderFeatures now.
 19	/// </summary>
 20	public class ShaderData : Content, ISaveLoadBinary
 21	{
 22		#region Constants
 23		/// <summary>
 24		/// Version number for this ShaderData. If this goes above 1, we need
 25		/// to support loading older versions as well. Saving is always going
 26		/// to be the latest version (this one).
 27		/// </summary>
 28		private const int VersionNumber = 1;
 29
 30		/// <summary>
 31		/// Default extension for Delta Engine shader files. Only used in the
 32		/// shader content processors and generators.
 33		/// </summary>
 34		public const string Extension = "DeltaShader";
 35		#endregion
 36
 37		#region Get (Static)
 38		/// <summary>
 39		/// Get and load content based on the content name. This method makes sure
 40		/// we do not load the same content twice (the constructor is protected).
 41		/// Can also be used to create empty shader data object without having to
 42		/// invoke the ContentSystem (with "", for Delta.ContentPlugins.Shader).
 43		/// </summary>
 44		/// <param name="contentName">Content name we want to load, this is
 45		/// passed onto the Content System, which will do the actual loading with
 46		/// help of the Load method in this class.</param>
 47		/// <returns>The loaded or empty Content object, always unique for the same
 48		/// name, this helps comparing data.</returns>
 49		public static ShaderData Get(string contentName)
 50		{
 51			return Get<ShaderData>(contentName, ContentType.Shader);
 52		}
 53
 54		/// <summary>
 55		/// Get or load a shader without the content name, but instead by searching
 56		/// for the given shaderFlags combination. Used to find shaders by
 57		/// features, not by the content name, which is not that important or
 58		/// interesting for shader creation with code. If nothing was found it
 59		/// still can provide a fallback (Basic shader) or crash if that fails too.
 60		/// </summary>
 61		/// <param name="shaderFlags">The shader flags.</param>
 62		/// <returns>
 63		/// Loaded shader data (if nothing was found the fallback shader
 64		/// TexturedShader2D is used)
 65		/// </returns>
 66		public static ShaderData Get(ShaderFeatureFlags shaderFlags)
 67		{
 68			Dictionary<string, ContentMetaData> allShaderContent =
 69				ContentManager.GetAllContentMetaData(ContentType.Shader);
 70			foreach (ContentMetaData shaderContent in allShaderContent.Values)
 71			{
 72				// Note: ShaderFlags is used for ShaderFeatureFlags.
 73				if (shaderContent.ShaderFlags == shaderFlags)
 74				{
 75					// Just load the found entry
 76					return Get(shaderContent.Name);
 77				}
 78			} // foreach
 79
 80			// Nothing found? Then load the fallback shader!
 81			// Only warn if the application is still running and was not aborted.
 82			if (Application.IsShuttingDown == false)
 83			{
 84				Log.Warning(
 85					"Unable to find shader with flags: " + shaderFlags +
 86					". Trying to use one of the 6 best matching fallback shaders " +
 87					"(basic, line, textured, colored, 2d, 3d)");
 88			}
 89			bool is2D = IsShaderFeatureFlagOn(shaderFlags, ShaderFeatureFlags.UI2D);
 90			if (IsShaderFeatureFlagOn(shaderFlags,
 91				ShaderFeatureFlags.ColoredVertices))
 92			{
 93				if (IsShaderFeatureFlagOn(shaderFlags,
 94					ShaderFeatureFlags.NoTexturing))
 95				{
 96					return Get(
 97						is2D
 98							? "LineShader2D"
 99							: "LineShader3D");
100				}
101				return Get(
102					is2D
103						? "TexturedColoredShader2D"
104						: "TexturedColoredShader3D");
105			}
106			return Get(
107				is2D
108					? "TexturedShader2D"
109					: "TexturedShader3D");
110		}
111		#endregion
112
113		#region ShaderCode (Public)
114		/// <summary>
115		/// Shader code to be compiled for this platform. Provided by the content
116		/// system in the format this platform supports (.fx, .glsl, .glessl).
117		/// </summary>
118		public string ShaderCode = "";
119		#endregion
120
121		#region Flags (Public)
122		/// <summary>
123		/// By default all shader only have the basic features. If more features
124		/// are added or removed this must be updated as well. Mainly used for
125		/// optimizations, fall backs and fixed function code paths to support
126		/// similar effects like defined in the shader code.
127		/// </summary>
128		public ShaderFeatureFlags Flags = ShaderFeatureFlags.Basic;
129		#endregion
130
131		#region UsesTexturing (Public)
132		/// <summary>
133		/// Does this shader use texturing? Always true except when the NoTexturing
134		/// shader feature flag was specified (only used for line shaders).
135		/// </summary>
136		public bool UsesTexturing
137		{
138			get
139			{
140				return IsFeatureOn(ShaderFeatureFlags.NoTexturing) == false;
141			}
142		}
143		#endregion
144
145		#region UsesVertexColoring (Public)
146		/// <summary>
147		/// Uses vertex coloring, only true if ColoredVertices was specified.
148		/// </summary>
149		public bool UsesVertexColoring
150		{
151			get
152			{
153				return IsFeatureOn(ShaderFeatureFlags.ColoredVertices);
154			}
155		}
156		#endregion
157
158		#region UseSkinning (Public)
159		/// <summary>
160		/// Use skinning
161		/// </summary>
162		public bool UseSkinning
163		{
164			get
165			{
166				return IsFeatureOn(ShaderFeatureFlags.UseSkinning);
167			}
168		}
169		#endregion
170
171		#region UseAlphaTest (Public)
172		/// <summary>
173		/// UseAlphaTest
174		/// </summary>
175		public bool UseAlphaTest
176		{
177			get
178			{
179				return IsFeatureOn(ShaderFeatureFlags.AlphaTest);
180			}
181		}
182		#endregion
183
184		#region UseLightMap (Public)
185		/// <summary>
186		/// Use light map
187		/// </summary>
188		public bool UseLightMap
189		{
190			get
191			{
192				return IsFeatureOn(ShaderFeatureFlags.LightMap);
193			}
194		}
195		#endregion
196
197		#region VertexFormat (Public)
198		/// <summary>
199		/// Vertex format, set in the constructor! Very important to make
200		/// decisions and optimizations. This should not be null, else classes like
201		/// GeometryData will crash with NullReferenceException (they rely on
202		/// valid vertex formats).
203		/// </summary>
204		public VertexFormat VertexFormat =
205			VertexFormat.Position2DTextured;
206		#endregion
207
208		#region Private
209
210		#region features (Private)
211		/// <summary>
212		/// List of shader features this shader currently has. Should be always
213		/// synchronized up with featureFlags! Use Add and Remove to add and remove
214		/// shader features.
215		/// </summary>
216		private readonly List<IShaderFeature> features = new List<IShaderFeature>();
217		#endregion
218
219		#region cachedParameters (Private)
220		/// <summary>
221		/// Cached shader parameters and their default or constant values for this
222		/// shader. Set like features and featureFlags in Add and Remove. If the
223		/// features change over time you need to re-add them or just save and
224		/// load the whole ShaderData (this dictionary is supposed to be fast, we
225		/// don't want to go through all features and search for all parameters).
226		/// </summary>
227		private readonly Dictionary<string, object> cachedParameters =
228			new Dictionary<string, object>();
229		#endregion
230
231		#endregion
232
233		#region Constructors
234		/// <summary>
235		/// Create shader data by loading it from content right away.
236		/// Use the static Get method to call this.
237		/// </summary>
238		/// <param name="contentName">Name of the content.</param>
239		protected ShaderData(string contentName)
240			: base(contentName, ContentType.Shader)
241		{
242		}
243		#endregion
244
245		#region ISaveLoadBinary Members
246		/// <summary>
247		/// Load shader data from binary data stream.
248		/// </summary>
249		/// <param name="reader">Binary reader of the underlying stream</param>
250		public void Load(BinaryReader reader)
251		{
252			try
253			{
254				// We currently only support our version, if more versions are added,
255				// we need to do different loading code depending on the version here.
256				int version = reader.ReadInt32();
257				if (version != VersionNumber)
258				{
259					Log.InvalidVersionWarning("ShaderData: " + Name, version,
260						VersionNumber);
261					return;
262				}
263
264				// Load combination of all flags (most important)
265				Flags = (ShaderFeatureFlags)reader.ReadInt32();
266				if (Flags != data.ShaderFlags)
267				{
268					Log.Warning("The shader flags from this shader content " + Name +
269					            ": '" + Flags + "' are different from the meta data " +
270					            "ShaderFlags: '" + data.ShaderFlags + "'. Should not happen!");
271				}
272
273				// Load the shader code.
274				ShaderCode = reader.ReadString();
275
276				// Load the vertex format.
277				VertexFormat = new VertexFormat(reader);
278
279				// Check if vertex format is correct, do some basic tests!
280				if (VertexFormat.Elements.Length == 0 ||
281				    VertexFormat.Elements[0].Size == 0 ||
282				    VertexFormat.LengthInBytes == 0)
283				{
284					Log.Warning("Invalid vertex format loaded for shader " + Name +
285					            ", it does not contain any elements or has no length in bytes. " +
286					            "Reverting to default Position2DTextured VertexFormat!");
287					VertexFormat =
288						VertexFormat.Position2DTextured;
289					FailedToLoad = true;
290				}
291
292				// And finally the flags
293				int numOfFeatures = reader.ReadInt32();
294				features.Clear();
295				for (int i = 0; i < numOfFeatures; i++)
296				{
297					features.Add(LoadShaderFeature(reader));
298				}
299
300				// If the shader code is empty, output a warning!
301				if (String.IsNullOrEmpty(ShaderCode)) //TODO: &&
302					// But only if we are not on the content server
303				{
304					Log.Warning("ShaderCode was not in the content file, make sure " +
305					            "you use a generated content file for your platform!");
306					FailedToLoad = true;
307				}
308
309				UpdateCachedParameters();
310			} // try
311			catch (Exception ex)
312			{
313				Log.Warning("Failed to load shader " + Name + ", check if the " +
314				            "content file works: " + ex);
315				// Setup some flags and data to not crash later.
316				Flags = ShaderFeatureFlags.UI2D; //obs:.Basic;
317				VertexFormat =
318					VertexFormat.Position2DTextured;
319				FailedToLoad = true;
320			}
321		}
322
323		/// <summary>
324		/// Save shader data, which consists just of features (enum and their
325		/// IShaderFeature classes with all their shader parameters in them).
326		/// </summary>
327		/// <param name="writer">The writer.</param>
328		public void Save(BinaryWriter writer)
329		{
330			writer.Write(VersionNumber);
331
332			// Save combination of all flags (most important)
333			writer.Write((int)Flags);
334
335			// Save the shader code.
336			writer.Write(ShaderCode);
337
338			// Save the vertex format
339			VertexFormat.Save(writer);
340
341			// And finally the features (they might change from time to time)
342			writer.Write(features.Count);
343			foreach (IShaderFeature feature in features)
344			{
345				writer.Write((int)feature.ShaderFeature);
346				feature.Save(writer);
347			}
348
349			// Note: We don't need to save the cachedParameters because we can
350			// easily regenerate them when loading this ShaderData file again.
351		}
352		#endregion
353
354		#region Add (Public)
355		/// <summary>
356		/// Add a new ShaderFeature to the ShaderData.
357		/// </summary>
358		/// <param name="newShaderFeature">The new shader feature.</param>
359		public void Add(IShaderFeature newShaderFeature)
360		{
361			foreach (IShaderFeature feature in features)
362			{
363				if (feature.ShaderFeature == newShaderFeature.ShaderFeature)
364				{
365					Log.Warning("The shader feature you like to add already exist.");
366					return;
367				}
368			}
369
370			features.Add(newShaderFeature);
371			// Add the new shader flag.
372			Flags |= newShaderFeature.ShaderFeature;
373		}
374		#endregion
375
376		#region Remove (Public)
377		/// <summary>
378		/// Remove the ShaderFeature from the ShaderData.
379		/// </summary>
380		/// <param name="featureToRemove">The feature to remove.</param>
381		/// <returns>True if we could remove the shader feature, false (and a log
382		/// warning) when the feature did not exist here.</returns>
383		public bool Remove(IShaderFeature featureToRemove)
384		{
385			if (features.Contains(featureToRemove))
386			{
387				Log.Warning("The shader feature you like to remove does not exist.");
388				return false;
389			}
390
391			// Remove the shader flag.
392			Flags ^= featureToRemove.ShaderFeature;
393
394			return features.Remove(featureToRemove);
395		}
396		#endregion
397
398		#region IsFeatureOn (Public)
399		/// <summary>
400		/// Is a certain shader flag used? Very simple combined enum check,
401		/// but this is used several times here and in higher levels up.
402		/// </summary>
403		/// <param name="featureToCheck">The feature to check for</param>
404		/// <returns>
405		/// <c>true</c> if the shader feature to check is on. Otherwise,<c>false</c>
406		/// is returned (feature was not found). Note that features can be combined.
407		/// </returns>
408		public bool IsFeatureOn(ShaderFeatureFlags featureToCheck)
409		{
410			return (Flags & featureToCheck) == featureToCheck;
411		}
412		#endregion
413
414		#region GetShaderFeatures (Public)
415		/// <summary>
416		/// Get shader features, needed in the shader editor to list all features.
417		/// </summary>
418		/// <returns>Shader features</returns>
419		public List<IShaderFeature> GetShaderFeatures()
420		{
421			return features;
422		}
423		#endregion
424
425		#region Methods (Private)
426
427		#region IsShaderFeatureFlagOn
428		/// <summary>
429		/// Helper to check if a specific flag is set, used for Get.
430		/// </summary>
431		private static bool IsShaderFeatureFlagOn(ShaderFeatureFlags flags,
432			ShaderFeatureFlags featureToCheck)
433		{
434			return (flags & featureToCheck) == featureToCheck;
435		}
436		#endregion
437
438		#region Load
439		/// <summary>
440		/// Native load method, will just load the xml data.
441		/// </summary>
442		/// <param name="alreadyLoadedNativeData">
443		/// The first instance that has already loaded the required content data
444		/// of this content class or just 'null' if there is none loaded yet (or
445		/// anymore).
446		/// </param>
447		protected override void Load(Content alreadyLoadedNativeData)
448		{
449			try
450			{
451				if (String.IsNullOrEmpty(RelativeFilePath) == false)
452				{
453					// Load via the ISaveLoadBinary interface methods below.
454					// Cloning should not really happen for shaders anyway.
455					FileHelper.Load(RelativeFilePath, this);
456				}
457			}
458			catch (Exception ex)
459			{
460				Log.Warning(
461					"Failed to load shader data from file '" + RelativeFilePath + "': " +
462					ex);
463				FailedToLoad = true;
464			}
465		}
466		#endregion
467
468		#region LoadShaderFeature
469		/// <summary>
470		/// Load shader feature
471		/// </summary>
472		/// <param name="reader">Reader to the binary stream for grabbing the
473		/// data for the shader feature.</param>
474		/// <returns>The loaded shader feature</returns>
475		public static IShaderFeature LoadShaderFeature(BinaryReader reader)
476		{
477			ShaderFeatureFlags featureFlag = (ShaderFeatureFlags)reader.ReadInt32();
478			switch (featureFlag)
479			{
480				case ShaderFeatureFlags.AlphaTest:
481					return new AlphaTestShaderFeature(reader);
482				case ShaderFeatureFlags.AmbientDiffuse:
483					return new AmbientDiffuseShaderFeature(reader);
484				case ShaderFeatureFlags.Basic:
485					return new BasicShaderFeature(reader);
486				case ShaderFeatureFlags.ColoredVertices:
487					return new ColoredVerticesShaderFeature(reader);
488				case ShaderFeatureFlags.DetailMapping:
489					return new DetailMappingShaderFeature(reader);
490				case ShaderFeatureFlags.DynamicDirectionalLighting:
491					return new DynamicDirectionalLightingShaderFeature(reader);
492				case ShaderFeatureFlags.Fresnel:
493					return new FresnelShaderFeature(reader);
494				case ShaderFeatureFlags.Fog:
495					return new FogShaderFeature(reader);
496				case ShaderFeatureFlags.LightMap:
497					return new LightMapShaderFeature(reader);
498				case ShaderFeatureFlags.NormalMap:
499					return new NormalMapShaderFeature(reader);
500				case ShaderFeatureFlags.ParallaxMap:
501					return new ParallaxMapShaderFeature(reader);
502				case ShaderFeatureFlags.NoTexturing:
503					return new NoTexturingShaderFeature(reader);
504				case ShaderFeatureFlags.PostScreenHDR:
505					return new PostScreenHDRShaderFeature(reader);
506					//	return new SkyCubeMappingShaderFeature(reader);
507				case ShaderFeatureFlags.SpecularDiffuse:
508					return new SpecularDiffuseShaderFeature(reader);
509				case ShaderFeatureFlags.SpecularMap:
510					return new SpecularMapShaderFeature(reader);
511				case ShaderFeatureFlags.Specular:
512					return new SpecularShaderFeature(reader);
513				case ShaderFeatureFlags.UI2D:
514					return new UI2DShaderFeature(reader);
515				case ShaderFeatureFlags.VertexCompression:
516					return new VertexCompressionShaderFeature(reader);
517				case ShaderFeatureFlags.Water:
518					return new WaterShaderFeature(reader);
519				case ShaderFeatureFlags.UseSkinning:
520					return new UseSkinningShaderFeature(reader);
521				case ShaderFeatureFlags.GenerateShadowMap:
522					return new GenerateShadowMapShaderFeature(reader);
523				case ShaderFeatureFlags.UseShadowMap:
524					return new UseShadowMapShaderFeature(reader);
525				default:
526					// Abort with error that this flag was not found (will be catched
527					// by the shader loader).
528					throw new NotSupportedException(
529						"Unsupported ShaderFeatureFlag found: " + featureFlag +
530						", we got no class for this yet!");
531			} // switch
532		}
533
534		public static IShaderFeature LoadShaderFeature(ShaderFeatureFlags flag)
535		{
536			switch (flag)
537			{
538				case ShaderFeatureFlags.AlphaTest:
539					return new AlphaTestShaderFeature();
540				case ShaderFeatureFlags.AmbientDiffuse:
541					return new AmbientDiffuseShaderFeature();
542				case ShaderFeatureFlags.Basic:
543					return new BasicShaderFeature();
544				case ShaderFeatureFlags.ColoredVertices:
545					return new ColoredVerticesShaderFeature();
546				case ShaderFeatureFlags.DetailMapping:
547					return new DetailMappingShaderFeature();
548				case ShaderFeatureFlags.DynamicDirectionalLighting:
549					return new DynamicDirectionalLightingShaderFeature();
550				case ShaderFeatureFlags.Fresnel:
551					return new FresnelShaderFeature();
552				case ShaderFeatureFlags.Fog:
553					return new FogShaderFeature();
554				case ShaderFeatureFlags.LightMap:
555					return new LightMapShaderFeature();
556				case ShaderFeatureFlags.NormalMap:
557					return new NormalMapShaderFeature();
558				case ShaderFeatureFlags.ParallaxMap:
559					return new ParallaxMapShaderFeature();
560				case ShaderFeatureFlags.NoTexturing:
561					return new NoTexturingShaderFeature();
562				case ShaderFeatureFlags.PostScreenHDR:
563					return new PostScreenHDRShaderFeature();
564				case ShaderFeatureFlags.SpecularDiffuse:
565					return new SpecularDiffuseShaderFeature();
566				case ShaderFeatureFlags.SpecularMap:
567					return new SpecularMapShaderFeature();
568				case ShaderFeatureFlags.Specular:
569					return new SpecularShaderFeature();
570				case ShaderFeatureFlags.UI2D:
571					return new UI2DShaderFeature();
572				case ShaderFeatureFlags.VertexCompression:
573					return new VertexCompressionShaderFeature();
574				case ShaderFeatureFlags.Water:
575					return new WaterShaderFeature();
576				case ShaderFeatureFlags.UseSkinning:
577					return new UseSkinningShaderFeature();
578				case ShaderFeatureFlags.GenerateShadowMap:
579					return new GenerateShadowMapShaderFeature();
580				case ShaderFeatureFlags.UseShadowMap:
581					return new UseShadowMapShaderFeature();
582				default:
583					// Abort with error that this flag was not found (will be catched
584					// by the shader loader).
585					throw new NotSupportedException(
586						"Unsupported ShaderFeatureFlag found: " + flag +
587						", we got no class for this yet!");
588			} // switch
589		}
590		#endregion
591
592		#region UpdateCachedParameters
593		/// <summary>
594		/// Update cached parameters
595		/// </summary>
596		private void UpdateCachedParameters()
597		{
598			try
599			{
600				cachedParameters.Clear();
601				foreach (IShaderFeature feature in features)
602				{
603					foreach (KeyValuePair<string, object> parameter in
604						feature.Parameters)
605					{
606						cachedParameters.Add(parameter.Key, parameter.Value);
607					}
608				}
609			} // try
610			catch (Exception ex)
611			{
612				Log.Warning(
613					"Broken shader features, probably multiple entries with the same " +
614					"feature. " + features.Write() + ", Exception: " + ex);
615			}
616		}
617		#endregion
618
619		#endregion
620	}
621}