PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

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