LoaderContext.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework.Graphics;
  5. using SharpGLTF.Schema2;
  6. namespace SharpGLTF.Runtime
  7. {
  8. /// <summary>
  9. /// Helper class used to import a glTF model into MonoGame
  10. /// </summary>
  11. class LoaderContext
  12. {
  13. #region lifecycle
  14. public LoaderContext(GraphicsDevice device)
  15. {
  16. _Device = device;
  17. _MatFactory = new MaterialFactory(device, _Disposables);
  18. }
  19. #endregion
  20. #region data
  21. private GraphicsDevice _Device;
  22. private readonly GraphicsResourceTracker _Disposables = new GraphicsResourceTracker();
  23. private readonly MaterialFactory _MatFactory;
  24. private readonly Dictionary<Mesh, ModelMesh> _StaticMeshes = new Dictionary<Mesh, ModelMesh>();
  25. private readonly Dictionary<Mesh, ModelMesh> _SkinnedMeshes = new Dictionary<Mesh, ModelMesh>();
  26. #endregion
  27. #region properties
  28. public IReadOnlyList<GraphicsResource> Disposables => _Disposables.Disposables;
  29. #endregion
  30. #region Mesh API
  31. private static IEnumerable<Schema2.MeshPrimitive> GetValidPrimitives(Schema2.Mesh srcMesh)
  32. {
  33. foreach (var srcPrim in srcMesh.Primitives)
  34. {
  35. var ppp = srcPrim.GetVertexAccessor("POSITION");
  36. if (ppp.Count < 3) continue;
  37. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.POINTS) continue;
  38. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINES) continue;
  39. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_LOOP) continue;
  40. if (srcPrim.DrawPrimitiveType == Schema2.PrimitiveType.LINE_STRIP) continue;
  41. yield return srcPrim;
  42. }
  43. }
  44. public ModelMesh CreateMesh(Schema2.Mesh srcMesh, int maxBones = 72)
  45. {
  46. if (_Device == null) throw new InvalidOperationException();
  47. var srcPrims = GetValidPrimitives(srcMesh).ToList();
  48. var dstMesh = new ModelMesh(_Device, Enumerable.Range(0, srcPrims.Count).Select(item => new ModelMeshPart()).ToList());
  49. dstMesh.Name = srcMesh.Name;
  50. dstMesh.BoundingSphere = srcMesh.CreateBoundingSphere();
  51. var srcNormals = new MeshNormalsFallback(srcMesh);
  52. var idx = 0;
  53. foreach (var srcPrim in srcPrims)
  54. {
  55. CreateMeshPart(dstMesh.MeshParts[idx++], srcPrim, srcNormals, maxBones);
  56. }
  57. return dstMesh;
  58. }
  59. private void CreateMeshPart(ModelMeshPart dstPart, MeshPrimitive srcPart, MeshNormalsFallback normalsFunc, int maxBones)
  60. {
  61. var doubleSided = srcPart.Material?.DoubleSided ?? false;
  62. var srcGeometry = new MeshPrimitiveReader(srcPart, doubleSided, normalsFunc);
  63. var eff = srcGeometry.IsSkinned ? _MatFactory.UseSkinnedEffect(srcPart.Material) : _MatFactory.UseStaticEffect(srcPart.Material);
  64. dstPart.Effect = eff;
  65. var vb = srcGeometry.IsSkinned ? CreateVertexBuffer(srcGeometry.ToXnaSkinned()) : CreateVertexBuffer(srcGeometry.ToXnaStatic());
  66. dstPart.VertexBuffer = vb;
  67. dstPart.NumVertices = srcGeometry.VertexCount;
  68. dstPart.VertexOffset = 0;
  69. dstPart.IndexBuffer = CreateIndexBuffer(srcGeometry.TriangleIndices);
  70. dstPart.PrimitiveCount = srcGeometry.TriangleIndices.Length;
  71. dstPart.StartIndex = 0;
  72. }
  73. #endregion
  74. #region resources API
  75. private VertexBuffer CreateVertexBuffer<T>(T[] dstVertices) where T:struct, IVertexType
  76. {
  77. var vb = new VertexBuffer(_Device, typeof(T), dstVertices.Length, BufferUsage.None);
  78. _Disposables.AddDisposable(vb);
  79. vb.SetData(dstVertices);
  80. return vb;
  81. }
  82. private IndexBuffer CreateIndexBuffer(IEnumerable<(int, int, int)> triangles)
  83. {
  84. var sequence32 = triangles
  85. .SelectMany(item => new[] { (UInt32)item.Item3, (UInt32)item.Item2, (UInt32)item.Item1 })
  86. .ToArray();
  87. var max = sequence32.Max();
  88. if (max > 65535)
  89. {
  90. var indices = new IndexBuffer(_Device, typeof(UInt32), sequence32.Length, BufferUsage.None);
  91. _Disposables.AddDisposable(indices);
  92. indices.SetData(sequence32);
  93. return indices;
  94. }
  95. else
  96. {
  97. var sequence16 = sequence32.Select(item => (UInt16)item).ToArray();
  98. var indices = new IndexBuffer(_Device, typeof(UInt16), sequence16.Length, BufferUsage.None);
  99. _Disposables.AddDisposable(indices);
  100. indices.SetData(sequence16);
  101. return indices;
  102. }
  103. }
  104. #endregion
  105. }
  106. }