MeshesFactory.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework.Graphics;
  5. using SharpGLTF.Runtime.Template;
  6. using SharpGLTF.Schema2;
  7. using MODELMESH = SharpGLTF.Runtime.Template.RuntimeModelMesh;
  8. using SRCMATERIAL = SharpGLTF.Schema2.Material;
  9. using SRCMESH = SharpGLTF.Schema2.Mesh;
  10. using SRCPRIM = SharpGLTF.Schema2.MeshPrimitive;
  11. namespace SharpGLTF.Runtime.Pipeline
  12. {
  13. /// <summary>
  14. /// Helper class used to import a glTF meshes and materials into MonoGame
  15. /// </summary>
  16. /// <remarks>
  17. /// derived types: <see cref="DefaultMeshesFactory"/>
  18. /// </remarks>
  19. public abstract class MeshesFactory
  20. {
  21. #region lifecycle
  22. /// <summary>
  23. /// Register here your own <see cref="MeshesFactory"/> derived class to override mesh creation
  24. /// </summary>
  25. public static Func<GraphicsDevice, MeshesFactory> InstanceBuilder { get; set; }
  26. public static MeshesFactory Create(GraphicsDevice device)
  27. {
  28. ArgumentNullException.ThrowIfNull(device);
  29. var mf = InstanceBuilder?.Invoke(device);
  30. mf ??= new DefaultMeshesFactory(device);
  31. return mf;
  32. }
  33. protected MeshesFactory(GraphicsDevice device)
  34. {
  35. _Device = device;
  36. }
  37. #endregion
  38. #region data
  39. private GraphicsDevice _Device;
  40. private GraphicsResourceTracker _Disposables;
  41. private EffectsFactory _EffectsFactory;
  42. // gathers all meshes using shared vertex and index buffers whenever possible.
  43. private MeshPrimitiveWriter _MeshWriter;
  44. private int _CurrentMeshIndex;
  45. // used as a container to a default material;
  46. private ModelRoot _DummyModel;
  47. #endregion
  48. #region properties
  49. /// <summary>
  50. /// Gets the collection of disposables accumulated by processing meshes, effects and textures.
  51. /// </summary>
  52. internal IReadOnlyList<GraphicsResource> Disposables => _Disposables.Disposables;
  53. #endregion
  54. #region API
  55. internal void Reset()
  56. {
  57. _Disposables = new GraphicsResourceTracker();
  58. _EffectsFactory = EffectsFactory.Create(_Device, _Disposables);
  59. }
  60. internal IReadOnlyDictionary<int, MODELMESH> CreateRuntimeMeshes(IEnumerable<SRCMESH> srcMeshes)
  61. {
  62. _MeshWriter = new MeshPrimitiveWriter();
  63. foreach (var srcMesh in srcMeshes)
  64. {
  65. _WriteMesh(srcMesh);
  66. }
  67. return _MeshWriter.GetRuntimeMeshes(_Device, _Disposables);
  68. }
  69. #endregion
  70. #region Mesh API
  71. private void _WriteMesh(SRCMESH srcMesh)
  72. {
  73. if (_Device == null) throw new InvalidOperationException();
  74. var srcPrims = _GetValidPrimitives(srcMesh)
  75. .ToDictionary(item => item, item => new MeshPrimitiveReader(item, item.Material?.DoubleSided ?? false));
  76. VertexNormalsFactory.CalculateSmoothNormals(srcPrims.Values.ToList());
  77. VertexTangentsFactory.CalculateTangents(srcPrims.Values.ToList());
  78. foreach (var srcPrim in srcPrims)
  79. {
  80. _CurrentMeshIndex = srcMesh.LogicalIndex;
  81. _WriteMeshPrimitive(srcPrim.Value, srcPrim.Key.Material);
  82. }
  83. }
  84. private static IEnumerable<SRCPRIM> _GetValidPrimitives(SRCMESH srcMesh)
  85. {
  86. foreach (var srcPrim in srcMesh.Primitives)
  87. {
  88. var ppp = srcPrim.GetVertexAccessor("POSITION");
  89. if (ppp.Count < 3) continue;
  90. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.POINTS) continue;
  91. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINES) continue;
  92. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_LOOP) continue;
  93. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_STRIP) continue;
  94. yield return srcPrim;
  95. }
  96. }
  97. private void _WriteMeshPrimitive(MeshPrimitiveReader srcPrim, SRCMATERIAL srcMaterial)
  98. {
  99. srcMaterial ??= GetDefaultMaterial();
  100. var effect = _EffectsFactory.UseEffect(srcMaterial, srcPrim.IsSkinned);
  101. WriteMeshPrimitive(srcPrim, effect);
  102. }
  103. protected void WriteMeshPrimitive<TVertex>(Effect effect, MeshPrimitiveReader primitive)
  104. where TVertex : unmanaged, IVertexType
  105. {
  106. _MeshWriter.WriteMeshPrimitive<TVertex>(_CurrentMeshIndex, effect, primitive);
  107. }
  108. private SRCMATERIAL GetDefaultMaterial()
  109. {
  110. if (_DummyModel != null)
  111. {
  112. _DummyModel = ModelRoot.CreateModel();
  113. _DummyModel.CreateMaterial("Default");
  114. }
  115. return _DummyModel.LogicalMaterials[0];
  116. }
  117. #endregion
  118. #region overridable API
  119. /// <summary>
  120. /// Converts a <see cref="MeshPrimitiveReader"/> to a device primitive.
  121. /// </summary>
  122. /// <param name="srcPrimitive">The mesh primitive to read and convert</param>
  123. /// <param name="effect">The <see cref="Effect"/> used by the primitive.</param>
  124. protected abstract void WriteMeshPrimitive(MeshPrimitiveReader srcPrimitive, Effect effect);
  125. #endregion
  126. }
  127. class DefaultMeshesFactory : MeshesFactory
  128. {
  129. #region lifecycle
  130. public DefaultMeshesFactory(GraphicsDevice device) : base(device) { }
  131. #endregion
  132. #region meshes creation
  133. protected override void WriteMeshPrimitive(MeshPrimitiveReader srcPrimitive, Effect effect)
  134. {
  135. if (srcPrimitive.IsSkinned) WriteMeshPrimitive<VertexSkinned>(effect, srcPrimitive);
  136. else WriteMeshPrimitive<VertexPositionNormalTexture>(effect, srcPrimitive);
  137. }
  138. #endregion
  139. }
  140. }