ResourceManager.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Graphics;
  6. using SharpGLTF.Schema2;
  7. namespace SharpGLTF.Runtime
  8. {
  9. // tracks all the disposable objects of a model;
  10. // vertex buffers, index buffers, effects and textures.
  11. class GraphicsResourceTracker
  12. {
  13. #region data
  14. private readonly List<GraphicsResource> _Disposables = new List<GraphicsResource>();
  15. #endregion
  16. #region properties
  17. public IReadOnlyList<GraphicsResource> Disposables => _Disposables;
  18. #endregion
  19. #region API
  20. public void AddDisposable(GraphicsResource resource)
  21. {
  22. if (resource == null) throw new ArgumentNullException();
  23. if (_Disposables.Contains(resource)) throw new ArgumentException();
  24. _Disposables.Add(resource);
  25. }
  26. #endregion
  27. }
  28. class TextureFactory
  29. {
  30. #region lifecycle
  31. public TextureFactory(GraphicsDevice device, GraphicsResourceTracker disposables)
  32. {
  33. _Device = device;
  34. _Disposables = disposables;
  35. }
  36. #endregion
  37. #region data
  38. private readonly GraphicsDevice _Device;
  39. private readonly GraphicsResourceTracker _Disposables;
  40. private readonly Dictionary<Memory.MemoryImage, Texture2D> _Textures = new Dictionary<Memory.MemoryImage, Texture2D>();
  41. #endregion
  42. #region API
  43. public Texture2D UseTexture(Memory.MemoryImage image, string name = null)
  44. {
  45. if (_Device == null) throw new InvalidOperationException();
  46. if (!image.IsValid) return null;
  47. if (_Textures.TryGetValue(image, out Texture2D tex)) return tex;
  48. using (var m = image.Open())
  49. {
  50. tex = Texture2D.FromStream(_Device, m);
  51. _Disposables.AddDisposable(tex);
  52. tex.Name = name;
  53. _Textures[image] = tex;
  54. return tex;
  55. }
  56. }
  57. public Texture2D UseWhiteImage()
  58. {
  59. const string solidWhitePNg = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFHpUWHRUaXRsZQAACJkrz8gsSQUABoACIippo0oAAAAoelRYdEF1dGhvcgAACJkLy0xOzStJVQhIzUtMSS1WcCzKTc1Lzy8BAG89CQyAoFAQAAAAGklEQVQoz2P8//8/AymAiYFEMKphVMPQ0QAAVW0DHZ8uFaIAAAAASUVORK5CYII=";
  60. var toBytes = Convert.FromBase64String(solidWhitePNg);
  61. return UseTexture(new ArraySegment<byte>(toBytes), "_InternalSolidWhite");
  62. }
  63. #endregion
  64. }
  65. class EffectsFactory
  66. {
  67. #region lifecycle
  68. public EffectsFactory(GraphicsDevice device, GraphicsResourceTracker disposables)
  69. {
  70. _Device = device;
  71. _TexFactory = new TextureFactory(device, disposables);
  72. _Disposables = disposables;
  73. }
  74. #endregion
  75. #region data
  76. private readonly GraphicsDevice _Device;
  77. private readonly TextureFactory _TexFactory;
  78. private readonly GraphicsResourceTracker _Disposables;
  79. private readonly Dictionary<Object, Effect> _RigidEffects = new Dictionary<Object, Effect>();
  80. private readonly Dictionary<Object, SkinnedEffect> _SkinnedEffects = new Dictionary<Object, SkinnedEffect>();
  81. #endregion
  82. #region API - Schema
  83. public void Register(Object key, bool isSkinned, Effect effect)
  84. {
  85. if (key == null) throw new ArgumentNullException(nameof(key));
  86. if (effect == null) throw new ArgumentNullException(nameof(effect));
  87. if (isSkinned && effect is SkinnedEffect skEffect) { _SkinnedEffects[key] = skEffect; }
  88. else { _RigidEffects[key] = effect; }
  89. }
  90. public Effect GetMaterial(Schema2.Material srcMaterial, bool isSkinned)
  91. {
  92. if (isSkinned)
  93. {
  94. if (_SkinnedEffects.TryGetValue(srcMaterial, out SkinnedEffect dstMaterial)) return dstMaterial;
  95. }
  96. else
  97. {
  98. if (_RigidEffects.TryGetValue(srcMaterial, out Effect dstMaterial)) return dstMaterial;
  99. }
  100. return null;
  101. }
  102. internal Texture2D UseTexture(MaterialChannel? channel, string name)
  103. {
  104. if (!channel.HasValue) return _TexFactory.UseWhiteImage();
  105. if (channel.HasValue && name == null)
  106. {
  107. name = channel.Value.LogicalParent.Name;
  108. if (name == null) name = "null";
  109. name += $"-{channel.Value.Key}";
  110. }
  111. return _TexFactory.UseTexture(channel.Value.Texture?.PrimaryImage?.Content ?? default, name);
  112. }
  113. #endregion
  114. }
  115. }