TilemapEffect.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. #region License
  2. // Copyright 2019-2021 Kastellanos Nikolaos
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion
  16. using System;
  17. using System.IO;
  18. using System.Reflection;
  19. using Microsoft.Xna.Framework;
  20. using Microsoft.Xna.Framework.Graphics;
  21. namespace tainicom.Aether.Shaders
  22. {
  23. public class TilemapEffect : Effect, IEffectMatrices
  24. {
  25. #region Effect Parameters
  26. EffectParameter textureParam;
  27. EffectParameter textureAtlasParam;
  28. EffectParameter mapSizeParam;
  29. EffectParameter invAtlasSizeParam;
  30. EffectParameter diffuseColorParam;
  31. EffectParameter fogColorParam;
  32. EffectParameter fogVectorParam;
  33. // IEffectMatrices
  34. EffectParameter worldViewProjParam;
  35. #endregion
  36. #region Fields
  37. bool fogEnabled;
  38. bool vertexColorEnabled;
  39. Matrix world = Matrix.Identity;
  40. Matrix view = Matrix.Identity;
  41. Matrix projection = Matrix.Identity;
  42. Matrix worldView;
  43. Vector3 diffuseColor = Vector3.One;
  44. float alpha = 1;
  45. float fogStart = 0;
  46. float fogEnd = 1;
  47. Vector2 atlasSize;
  48. EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
  49. #if ((MG && WINDOWS) || W8_1 || W10)
  50. static readonly String resourceName = "tainicom.Aether.Shaders.Resources.TilemapEffect.dx11.mgfxo";
  51. #else
  52. static readonly String resourceName = "tainicom.Aether.Shaders.Resources.TilemapEffect.xna.WinReach";
  53. #endif
  54. internal static byte[] LoadEffectResource(string name)
  55. {
  56. using (var stream = LoadEffectResourceStream(name))
  57. using (var ms = new MemoryStream())
  58. {
  59. stream.CopyTo(ms);
  60. return ms.ToArray();
  61. }
  62. }
  63. internal static Stream LoadEffectResourceStream(string name)
  64. {
  65. // Detect MG version
  66. var version = "";
  67. #if !XNA && !PORTABLE
  68. version = ".9";
  69. var mgVersion = GetAssembly(typeof(Effect)).GetName().Version;
  70. if (mgVersion.Major == 3)
  71. {
  72. if (mgVersion.Minor == 4)
  73. version = ".6";
  74. if (mgVersion.Minor == 5)
  75. version = ".7";
  76. if (mgVersion.Minor == 6)
  77. version = ".8";
  78. if (mgVersion.Minor == 7)
  79. version = ".8";
  80. if (mgVersion.Minor == 8)
  81. version = ".9";
  82. }
  83. name = name + version;
  84. #endif
  85. Stream stream = GetAssembly(typeof(TilemapEffect)).GetManifestResourceStream(name);
  86. return stream;
  87. }
  88. private static Assembly GetAssembly(Type type)
  89. {
  90. #if W8_1 || W10
  91. return type.GetTypeInfo().Assembly;
  92. #else
  93. return type.Assembly;
  94. #endif
  95. }
  96. #endregion
  97. #region Public Properties
  98. public Matrix Projection
  99. {
  100. get { return projection; }
  101. set
  102. {
  103. projection = value;
  104. dirtyFlags |= EffectDirtyFlags.WorldViewProj;
  105. }
  106. }
  107. public Matrix View
  108. {
  109. get { return view; }
  110. set
  111. {
  112. view = value;
  113. dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
  114. }
  115. }
  116. public Matrix World
  117. {
  118. get { return world; }
  119. set
  120. {
  121. world = value;
  122. dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
  123. }
  124. }
  125. /// <summary>
  126. /// Gets or sets the Tilemap texture.
  127. /// </summary>
  128. public Texture2D Texture
  129. {
  130. get { return textureParam.GetValueTexture2D(); }
  131. set { textureParam.SetValue(value); }
  132. }
  133. /// <summary>
  134. /// Gets or sets the Atlas texture.
  135. /// </summary>
  136. public Texture2D TextureAtlas
  137. {
  138. get { return textureAtlasParam.GetValueTexture2D(); }
  139. set { textureAtlasParam.SetValue(value); }
  140. }
  141. /// <summary>
  142. /// Gets or sets the Map size.
  143. /// </summary>
  144. public Vector2 MapSize
  145. {
  146. get { return mapSizeParam.GetValueVector2(); }
  147. set { mapSizeParam.SetValue(value); }
  148. }
  149. /// <summary>
  150. /// Gets or sets the Atlas size.
  151. /// </summary>
  152. public Vector2 AtlasSize
  153. {
  154. get { return atlasSize; }
  155. set
  156. {
  157. atlasSize = value;
  158. value.X = 1f/value.X;
  159. value.Y = 1f/value.Y;
  160. invAtlasSizeParam.SetValue(value);
  161. }
  162. }
  163. /// <summary>
  164. /// Gets or sets the material diffuse color (range 0 to 1).
  165. /// </summary>
  166. public Vector3 DiffuseColor
  167. {
  168. get { return diffuseColor; }
  169. set
  170. {
  171. diffuseColor = value;
  172. dirtyFlags |= EffectDirtyFlags.MaterialColor;
  173. }
  174. }
  175. /// <summary>
  176. /// Gets or sets the material alpha.
  177. /// </summary>
  178. public float Alpha
  179. {
  180. get { return alpha; }
  181. set
  182. {
  183. alpha = value;
  184. dirtyFlags |= EffectDirtyFlags.MaterialColor;
  185. }
  186. }
  187. /// <summary>
  188. /// Gets or sets the fog enable flag.
  189. /// </summary>
  190. public bool FogEnabled
  191. {
  192. get { return fogEnabled; }
  193. set
  194. {
  195. if (fogEnabled != value)
  196. {
  197. fogEnabled = value;
  198. dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.FogEnable;
  199. }
  200. }
  201. }
  202. /// <summary>
  203. /// Gets or sets the fog start distance.
  204. /// </summary>
  205. public float FogStart
  206. {
  207. get { return fogStart; }
  208. set
  209. {
  210. fogStart = value;
  211. dirtyFlags |= EffectDirtyFlags.Fog;
  212. }
  213. }
  214. /// <summary>
  215. /// Gets or sets the fog end distance.
  216. /// </summary>
  217. public float FogEnd
  218. {
  219. get { return fogEnd; }
  220. set
  221. {
  222. fogEnd = value;
  223. dirtyFlags |= EffectDirtyFlags.Fog;
  224. }
  225. }
  226. /// <summary>
  227. /// Gets or sets the fog color.
  228. /// </summary>
  229. public Vector3 FogColor
  230. {
  231. get { return fogColorParam.GetValueVector3(); }
  232. set { fogColorParam.SetValue(value); }
  233. }
  234. /// <summary>
  235. /// Gets or sets whether vertex color is enabled.
  236. /// </summary>
  237. public bool VertexColorEnabled
  238. {
  239. get { return vertexColorEnabled; }
  240. set
  241. {
  242. if (vertexColorEnabled != value)
  243. {
  244. vertexColorEnabled = value;
  245. dirtyFlags |= EffectDirtyFlags.ShaderIndex;
  246. }
  247. }
  248. }
  249. #endregion
  250. #region Methods
  251. public TilemapEffect(GraphicsDevice graphicsDevice)
  252. : base(graphicsDevice,
  253. #if NETFX_CORE || WP8
  254. LoadEffectResourceStream(resourceName), true
  255. #else
  256. LoadEffectResource(resourceName)
  257. #endif
  258. )
  259. {
  260. CacheEffectParameters(null);
  261. }
  262. public TilemapEffect(GraphicsDevice graphicsDevice, byte[] Bytecode): base(graphicsDevice, Bytecode)
  263. {
  264. CacheEffectParameters(null);
  265. }
  266. protected TilemapEffect(TilemapEffect cloneSource)
  267. : base(cloneSource)
  268. {
  269. CacheEffectParameters(cloneSource);
  270. fogEnabled = cloneSource.fogEnabled;
  271. vertexColorEnabled = cloneSource.vertexColorEnabled;
  272. world = cloneSource.world;
  273. view = cloneSource.view;
  274. projection = cloneSource.projection;
  275. diffuseColor = cloneSource.diffuseColor;
  276. alpha = cloneSource.alpha;
  277. fogStart = cloneSource.fogStart;
  278. fogEnd = cloneSource.fogEnd;
  279. atlasSize = cloneSource.atlasSize;
  280. }
  281. public override Effect Clone()
  282. {
  283. return new TilemapEffect(this);
  284. }
  285. void CacheEffectParameters(TilemapEffect cloneSource)
  286. {
  287. textureParam = Parameters["Texture"];
  288. textureAtlasParam = Parameters["TextureAtlas"];
  289. mapSizeParam = Parameters["MapSize"];
  290. invAtlasSizeParam = Parameters["InvAtlasSize"];
  291. diffuseColorParam = Parameters["DiffuseColor"];
  292. fogColorParam = Parameters["FogColor"];
  293. fogVectorParam = Parameters["FogVector"];
  294. // IEffectMatrices
  295. worldViewProjParam = Parameters["WorldViewProj"];
  296. }
  297. /// <summary>
  298. /// Lazily computes derived parameter values immediately before applying the effect.
  299. /// </summary>
  300. protected override void OnApply()
  301. {
  302. // Recompute the world+view+projection matrix or fog vector?
  303. dirtyFlags = SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
  304. // Recompute the diffuse/alpha material color parameter?
  305. if ((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0)
  306. {
  307. diffuseColorParam.SetValue(new Vector4(diffuseColor * alpha, alpha));
  308. dirtyFlags &= ~EffectDirtyFlags.MaterialColor;
  309. }
  310. // Recompute the shader index?
  311. if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
  312. {
  313. int shaderIndex = 0;
  314. if (!fogEnabled)
  315. shaderIndex += 1;
  316. if (vertexColorEnabled)
  317. shaderIndex += 2;
  318. dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
  319. CurrentTechnique = Techniques[shaderIndex];
  320. }
  321. }
  322. /// <summary>
  323. /// Lazily recomputes the world+view+projection matrix and
  324. /// fog vector based on the current effect parameter settings.
  325. /// </summary>
  326. static EffectDirtyFlags SetWorldViewProjAndFog(EffectDirtyFlags dirtyFlags,
  327. ref Matrix world, ref Matrix view, ref Matrix projection, ref Matrix worldView,
  328. bool fogEnabled, float fogStart, float fogEnd,
  329. EffectParameter worldViewProjParam, EffectParameter fogVectorParam)
  330. {
  331. // Recompute the world+view+projection matrix?
  332. if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
  333. {
  334. Matrix worldViewProj;
  335. Matrix.Multiply(ref world, ref view, out worldView);
  336. Matrix.Multiply(ref worldView, ref projection, out worldViewProj);
  337. worldViewProjParam.SetValue(worldViewProj);
  338. dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
  339. }
  340. if (fogEnabled)
  341. {
  342. // Recompute the fog vector?
  343. if ((dirtyFlags & (EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable)) != 0)
  344. {
  345. SetFogVector(ref worldView, fogStart, fogEnd, fogVectorParam);
  346. dirtyFlags &= ~(EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable);
  347. }
  348. }
  349. else
  350. {
  351. // When fog is disabled, make sure the fog vector is reset to zero.
  352. if ((dirtyFlags & EffectDirtyFlags.FogEnable) != 0)
  353. {
  354. fogVectorParam.SetValue(Vector4.Zero);
  355. dirtyFlags &= ~EffectDirtyFlags.FogEnable;
  356. }
  357. }
  358. return dirtyFlags;
  359. }
  360. /// <summary>
  361. /// Sets a vector which can be dotted with the object space vertex position to compute fog amount.
  362. /// </summary>
  363. static void SetFogVector(ref Matrix worldView, float fogStart, float fogEnd, EffectParameter fogVectorParam)
  364. {
  365. if (fogStart == fogEnd)
  366. {
  367. // Degenerate case: force everything to 100% fogged if start and end are the same.
  368. fogVectorParam.SetValue(new Vector4(0, 0, 0, 1));
  369. }
  370. else
  371. {
  372. // We want to transform vertex positions into view space, take the resulting
  373. // Z value, then scale and offset according to the fog start/end distances.
  374. // Because we only care about the Z component, the shader can do all this
  375. // with a single dot product, using only the Z row of the world+view matrix.
  376. float scale = 1f / (fogStart - fogEnd);
  377. Vector4 fogVector = new Vector4();
  378. fogVector.X = worldView.M13 * scale;
  379. fogVector.Y = worldView.M23 * scale;
  380. fogVector.Z = worldView.M33 * scale;
  381. fogVector.W = (worldView.M43 + fogStart) * scale;
  382. fogVectorParam.SetValue(fogVector);
  383. }
  384. }
  385. #endregion
  386. enum EffectDirtyFlags
  387. {
  388. WorldViewProj = 1,
  389. //World = 2,
  390. //EyePosition = 4,
  391. MaterialColor = 8,
  392. Fog = 16,
  393. FogEnable = 32,
  394. //AlphaTest = 64,
  395. ShaderIndex = 128,
  396. All = -1
  397. }
  398. }
  399. }