LoaderContext.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework.Graphics;
  5. using SharpGLTF.Schema2;
  6. using SRCMESH = SharpGLTF.Schema2.Mesh;
  7. using SRCPRIM = SharpGLTF.Schema2.MeshPrimitive;
  8. using SRCMATERIAL = SharpGLTF.Schema2.Material;
  9. using MODELMESH = SharpGLTF.Runtime.RuntimeModelMesh;
  10. namespace SharpGLTF.Runtime
  11. {
  12. /// <summary>
  13. /// Helper class used to import a glTF meshes and materials into MonoGame
  14. /// </summary>
  15. public abstract class LoaderContext
  16. {
  17. #region lifecycle
  18. public LoaderContext(GraphicsDevice device)
  19. {
  20. _Device = device;
  21. }
  22. #endregion
  23. #region data
  24. private GraphicsDevice _Device;
  25. private GraphicsResourceTracker _Disposables;
  26. private EffectsFactory _MatFactory;
  27. // gathers all meshes using shared vertex and index buffers whenever possible.
  28. private MeshPrimitiveWriter _MeshWriter;
  29. private int _CurrentMeshIndex;
  30. // used as a container to a default material;
  31. private SharpGLTF.Schema2.ModelRoot _DummyModel;
  32. #endregion
  33. #region properties
  34. protected GraphicsDevice Device => _Device;
  35. internal IReadOnlyList<GraphicsResource> Disposables => _Disposables.Disposables;
  36. #endregion
  37. #region API
  38. internal void Reset()
  39. {
  40. _Disposables = new GraphicsResourceTracker();
  41. _MatFactory = new EffectsFactory(_Device, _Disposables);
  42. _MeshWriter = new MeshPrimitiveWriter();
  43. }
  44. #endregion
  45. #region Mesh API
  46. internal void _WriteMesh(SRCMESH srcMesh)
  47. {
  48. if (_Device == null) throw new InvalidOperationException();
  49. var srcPrims = _GetValidPrimitives(srcMesh)
  50. .ToDictionary(item => item, item => new MeshPrimitiveReader(item, item.Material?.DoubleSided ?? false));
  51. VertexNormalsFactory.CalculateSmoothNormals(srcPrims.Values.ToList());
  52. VertexTangentsFactory.CalculateTangents(srcPrims.Values.ToList());
  53. foreach (var srcPrim in srcPrims)
  54. {
  55. _CurrentMeshIndex = srcMesh.LogicalIndex;
  56. _WriteMeshPrimitive(srcPrim.Value, srcPrim.Key.Material);
  57. }
  58. }
  59. private static IEnumerable<SRCPRIM> _GetValidPrimitives(SRCMESH srcMesh)
  60. {
  61. foreach (var srcPrim in srcMesh.Primitives)
  62. {
  63. var ppp = srcPrim.GetVertexAccessor("POSITION");
  64. if (ppp.Count < 3) continue;
  65. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.POINTS) continue;
  66. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINES) continue;
  67. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_LOOP) continue;
  68. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_STRIP) continue;
  69. yield return srcPrim;
  70. }
  71. }
  72. private void _WriteMeshPrimitive(MeshPrimitiveReader srcPrim, SRCMATERIAL srcMaterial)
  73. {
  74. srcMaterial ??= GetDefaultMaterial();
  75. var effect = _MatFactory.GetMaterial(srcMaterial, srcPrim.IsSkinned);
  76. if (effect == null)
  77. {
  78. effect = CreateEffect(srcMaterial, srcPrim.IsSkinned);
  79. _MatFactory.Register(srcMaterial, srcPrim.IsSkinned, effect);
  80. }
  81. WriteMeshPrimitive(srcPrim, effect);
  82. }
  83. protected abstract void WriteMeshPrimitive(MeshPrimitiveReader srcPrimitive, Effect effect);
  84. protected void WriteMeshPrimitive<TVertex>(Effect effect, MeshPrimitiveReader primitive)
  85. where TVertex : unmanaged, IVertexType
  86. {
  87. _MeshWriter.WriteMeshPrimitive<TVertex>(_CurrentMeshIndex, effect, primitive);
  88. }
  89. #endregion
  90. #region EFfects API
  91. /// <summary>
  92. /// Called when finding a new material that needs to be converted to an Effect.
  93. /// </summary>
  94. /// <param name="srcMaterial">The material to convert.</param>
  95. /// <param name="isSkinned">Indicates that the material is used in a skinned mesh.</param>
  96. /// <returns>An effect to be used in place of <paramref name="srcMaterial"/>. </returns>
  97. protected abstract Effect CreateEffect(Material srcMaterial, bool isSkinned);
  98. protected virtual Texture2D UseTexture(MaterialChannel? channel, string name)
  99. {
  100. return _MatFactory.UseTexture(channel, name);
  101. }
  102. #endregion
  103. #region resources API
  104. internal IReadOnlyDictionary<int, MODELMESH> CreateRuntimeModels()
  105. {
  106. return _MeshWriter.GetRuntimeMeshes(_Device, _Disposables);
  107. }
  108. private Material 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. }
  119. }