ModelMeshReplacement.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Microsoft.Xna.Framework.Graphics;
  7. namespace SharpGLTF.Runtime.Template
  8. {
  9. /// <summary>
  10. /// Replaces Monogame's <see cref="ModelMeshPart"/>.
  11. /// </summary>
  12. /// <remarks>
  13. /// We need to duplicate <see cref="ModelMeshPart"/> because it has
  14. /// a constructor to be declared as internal, because this class is
  15. /// intended to be instantiated by loading an XNB file
  16. /// </remarks>
  17. sealed class RuntimeModelMeshPart
  18. {
  19. #region lifecycle
  20. internal RuntimeModelMeshPart(RuntimeModelMesh parent)
  21. {
  22. _Parent = parent;
  23. }
  24. #endregion
  25. #region data
  26. private readonly RuntimeModelMesh _Parent;
  27. private Effect _Effect;
  28. private IndexBuffer _IndexBuffer;
  29. private int _IndexOffset;
  30. private int _PrimitiveCount;
  31. private VertexBuffer _VertexBuffer;
  32. private int _VertexOffset;
  33. private int _VertexCount;
  34. public object Tag { get; set; }
  35. #endregion
  36. #region properties
  37. public Effect Effect
  38. {
  39. get => _Effect;
  40. set
  41. {
  42. if (_Effect == value) return;
  43. _Effect = value;
  44. _Parent.InvalidateEffectCollection(); // if we change this property, we need to invalidate the parent's effect collection.
  45. }
  46. }
  47. public GraphicsDevice Device => _Parent._GraphicsDevice;
  48. #endregion
  49. #region API
  50. public void SetVertexBuffer(VertexBuffer vb, int offset, int count)
  51. {
  52. this._VertexBuffer = vb;
  53. this._VertexOffset = offset;
  54. this._VertexCount = count;
  55. }
  56. public void SetIndexBuffer(IndexBuffer ib, int offset, int count)
  57. {
  58. this._IndexBuffer = ib;
  59. this._IndexOffset = offset;
  60. this._PrimitiveCount = count;
  61. }
  62. public void Draw(GraphicsDevice device)
  63. {
  64. if (_PrimitiveCount > 0)
  65. {
  66. device.SetVertexBuffer(_VertexBuffer);
  67. device.Indices = _IndexBuffer;
  68. for (int j = 0; j < _Effect.CurrentTechnique.Passes.Count; j++)
  69. {
  70. _Effect.CurrentTechnique.Passes[j].Apply();
  71. device.DrawIndexedPrimitives(PrimitiveType.TriangleList, _VertexOffset, _IndexOffset, _PrimitiveCount);
  72. }
  73. }
  74. }
  75. #endregion
  76. }
  77. /// <summary>
  78. /// Replaces Monogame's <see cref="ModelMesh"/>
  79. /// </summary>
  80. /// <remarks>
  81. /// We need to duplicate <see cref="ModelMesh"/> because it depends
  82. /// on <see cref="ModelMeshPart"/> which is intended to be used only
  83. /// by the content loader.
  84. /// </remarks>
  85. sealed class RuntimeModelMesh
  86. {
  87. #region lifecycle
  88. public RuntimeModelMesh(GraphicsDevice graphicsDevice)
  89. {
  90. this._GraphicsDevice = graphicsDevice;
  91. }
  92. #endregion
  93. #region data
  94. internal GraphicsDevice _GraphicsDevice;
  95. private readonly List<RuntimeModelMeshPart> _Primitives = new List<RuntimeModelMeshPart>();
  96. private IReadOnlyList<Effect> _Effects;
  97. private Microsoft.Xna.Framework.BoundingSphere? _Sphere;
  98. #endregion
  99. #region properties
  100. public IReadOnlyCollection<Effect> Effects
  101. {
  102. get
  103. {
  104. if (_Effects != null) return _Effects;
  105. // Create the shared effects collection on demand.
  106. _Effects = _Primitives
  107. .Select(item => item.Effect)
  108. .Distinct()
  109. .ToArray();
  110. return _Effects;
  111. }
  112. }
  113. public Microsoft.Xna.Framework.BoundingSphere BoundingSphere
  114. {
  115. set => _Sphere = value;
  116. get
  117. {
  118. if (_Sphere.HasValue) return _Sphere.Value;
  119. return default;
  120. }
  121. }
  122. public IReadOnlyList<RuntimeModelMeshPart> MeshParts => _Primitives;
  123. public string Name { get; set; }
  124. public ModelBone ParentBone { get; set; }
  125. public object Tag { get; set; }
  126. #endregion
  127. #region API
  128. internal void InvalidateEffectCollection() { _Effects = null; }
  129. public RuntimeModelMeshPart CreateMeshPart()
  130. {
  131. var primitive = new RuntimeModelMeshPart(this);
  132. _Primitives.Add(primitive);
  133. InvalidateEffectCollection();
  134. _Sphere = null;
  135. return primitive;
  136. }
  137. public void Draw()
  138. {
  139. for (int i = 0; i < _Primitives.Count; i++)
  140. {
  141. _Primitives[i].Draw(_GraphicsDevice);
  142. }
  143. }
  144. #endregion
  145. }
  146. }