MeshPrimitiveReader.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework.Graphics;
  5. using SharpGLTF.Schema2;
  6. using XY = System.Numerics.Vector2;
  7. using XYZ = System.Numerics.Vector3;
  8. using XYZW = System.Numerics.Vector4;
  9. namespace SharpGLTF.Runtime
  10. {
  11. /// <summary>
  12. /// Reads the content of a glTF <see cref="MeshPrimitive"/> object into a structure that's easier to consume by MonoGame.
  13. /// </summary>
  14. public sealed class MeshPrimitiveReader
  15. : VertexNormalsFactory.IMeshPrimitive
  16. , VertexTangentsFactory.IMeshPrimitive
  17. {
  18. #region lifecycle
  19. internal MeshPrimitiveReader(MeshPrimitive srcPrim, bool doubleSided)
  20. {
  21. _Positions = srcPrim.GetVertexAccessor("POSITION")?.AsVector3Array();
  22. _Normals = srcPrim.GetVertexAccessor("NORMAL")?.AsVector3Array();
  23. _Tangents = srcPrim.GetVertexAccessor("TANGENT")?.AsVector4Array();
  24. _Color0 = srcPrim.GetVertexAccessor("COLOR_0")?.AsColorArray();
  25. _TexCoord0 = srcPrim.GetVertexAccessor("TEXCOORD_0")?.AsVector2Array();
  26. _Joints0 = srcPrim.GetVertexAccessor("JOINTS_0")?.AsVector4Array();
  27. _Joints1 = srcPrim.GetVertexAccessor("JOINTS_1")?.AsVector4Array();
  28. _Weights0 = srcPrim.GetVertexAccessor("WEIGHTS_0")?.AsVector4Array();
  29. _Weights1 = srcPrim.GetVertexAccessor("WEIGHTS_1")?.AsVector4Array();
  30. if (_Joints0 == null || _Weights0 == null) { _Joints0 = _Joints1 = _Weights0 = _Weights1 = null; }
  31. if (_Joints1 == null || _Weights1 == null) { _Joints1 = _Weights1 = null; }
  32. if (_Weights0 != null)
  33. {
  34. _Weights0 = _Weights0.ToArray(); // isolate memory to prevent overwriting source glTF.
  35. for (int i = 0; i < _Weights0.Count; ++i)
  36. {
  37. var r = XYZW.Dot(_Weights0[i], XYZW.One);
  38. _Weights0[i] /= r;
  39. }
  40. }
  41. _TrianglesSource = srcPrim.GetTriangleIndices().ToArray();
  42. if (doubleSided) // Monogame's effect material does not support double sided materials, so we simulate it by adding reverse faces
  43. {
  44. var back = _TrianglesSource.Select(item => (item.A, item.C, item.B));
  45. _Triangles = _TrianglesSource.Concat(back).ToArray();
  46. }
  47. else
  48. {
  49. _Triangles = _TrianglesSource;
  50. }
  51. }
  52. #endregion
  53. #region data
  54. private readonly (int A, int B, int C)[] _TrianglesSource;
  55. private readonly (int A, int B, int C)[] _Triangles;
  56. private readonly IList<XYZ> _Positions;
  57. private IList<XYZ> _Normals;
  58. private IList<XYZW> _Tangents;
  59. private readonly IList<XYZW> _Color0;
  60. private readonly IList<XY> _TexCoord0;
  61. private readonly IList<XYZW> _Joints0;
  62. private readonly IList<XYZW> _Joints1;
  63. private readonly IList<XYZW> _Weights0;
  64. private readonly IList<XYZW> _Weights1;
  65. #endregion
  66. #region properties
  67. public bool IsSkinned => _Joints0 != null;
  68. public int VertexCount => _Positions?.Count ?? 0;
  69. public (int A, int B, int C)[] TriangleIndices => _Triangles;
  70. #endregion
  71. #region API
  72. public XYZ GetPosition(int idx) { return _Positions[idx]; }
  73. public XYZ GetNormal(int idx) { return _Normals[idx]; }
  74. public XYZW GetTangent(int idx) { return _Tangents[idx]; }
  75. public XY GetTextureCoord(int idx, int set)
  76. {
  77. if (set == 0 && _TexCoord0 != null) return _TexCoord0[idx];
  78. return XY.Zero;
  79. }
  80. public XYZW GetColor(int idx, int set)
  81. {
  82. if (set == 0 && _Color0 != null) return _Color0[idx];
  83. return XYZW.One;
  84. }
  85. public XYZW GetIndices(int idx)
  86. {
  87. if (_Joints0 != null) return _Joints0[idx];
  88. return XYZW.Zero;
  89. }
  90. public XYZW GetWeights(int idx)
  91. {
  92. if (_Weights0 != null) return _Weights0[idx];
  93. return XYZW.UnitX;
  94. }
  95. /// <summary>
  96. /// Gets the current Vertex attributes as an array of <see cref="{TVertex}"/> vertices.
  97. /// </summary>
  98. /// <typeparam name="TVertex">A Vertex type implementing <see cref="IVertexType"/>.</typeparam>
  99. /// <returns>A <see cref="{TVertex}"/> array</returns>
  100. public unsafe TVertex[] ToXnaVertices<TVertex>()
  101. where TVertex:unmanaged, IVertexType
  102. {
  103. var declaration = default(TVertex).VertexDeclaration;
  104. if (sizeof(TVertex) != declaration.VertexStride) throw new ArgumentException(nameof(TVertex));
  105. var dst = new TVertex[_Positions.Count];
  106. for (int i = 0; i < dst.Length; ++i)
  107. {
  108. var v = _VertexWriter.CreateFromArray(dst, i);
  109. foreach(var element in declaration.GetVertexElements())
  110. {
  111. switch(element.VertexElementUsage)
  112. {
  113. case VertexElementUsage.Position: v.SetValue(element, GetPosition(i)); break;
  114. case VertexElementUsage.Normal: v.SetValue(element, GetNormal(i)); break;
  115. case VertexElementUsage.Tangent: v.SetValue(element, GetTangent(i), true); break;
  116. case VertexElementUsage.TextureCoordinate: v.SetValue(element, GetTextureCoord(i,element.UsageIndex)); break;
  117. case VertexElementUsage.Color: v.SetValue(element, GetColor(i, element.UsageIndex) , true); break;
  118. case VertexElementUsage.BlendIndices: v.SetValue(element, GetIndices(i), false); break;
  119. case VertexElementUsage.BlendWeight: v.SetValue(element, GetWeights(i), true); break;
  120. }
  121. }
  122. }
  123. return dst;
  124. }
  125. #endregion
  126. #region nested types
  127. readonly ref struct _VertexWriter
  128. {
  129. #region constructor
  130. public static _VertexWriter CreateFromArray<TVertex>(TVertex[] vvv, int idx)
  131. where TVertex : unmanaged, IVertexType
  132. {
  133. var v = vvv.AsSpan().Slice(idx, 1);
  134. var d = System.Runtime.InteropServices.MemoryMarshal.Cast<TVertex, Byte>(v);
  135. return new _VertexWriter(d);
  136. }
  137. public _VertexWriter(Span<Byte> vertex)
  138. {
  139. _Vertex = vertex;
  140. }
  141. #endregion
  142. #region data
  143. private readonly Span<Byte> _Vertex;
  144. #endregion
  145. #region API
  146. public unsafe void SetValue(VertexElement element, XY value)
  147. {
  148. if (element.VertexElementFormat == VertexElementFormat.Vector2)
  149. {
  150. var dst = _Vertex.Slice(element.Offset);
  151. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, XY>(dst)[0] = value;
  152. return;
  153. }
  154. throw new NotImplementedException();
  155. }
  156. public unsafe void SetValue(VertexElement element, XYZ value)
  157. {
  158. if (element.VertexElementFormat == VertexElementFormat.Vector3)
  159. {
  160. var dst = _Vertex.Slice(element.Offset);
  161. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, XYZ>(dst)[0] = value;
  162. return;
  163. }
  164. throw new NotImplementedException();
  165. }
  166. public unsafe void SetValue(VertexElement element, XYZW value, bool valueIsUnitLength)
  167. {
  168. var dst = _Vertex.Slice(element.Offset);
  169. switch (element.VertexElementFormat)
  170. {
  171. case VertexElementFormat.Vector4:
  172. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, XYZW>(dst)[0] = value;
  173. return;
  174. case VertexElementFormat.Byte4:
  175. if (valueIsUnitLength)
  176. {
  177. SetValue(element, new Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedByte4(value.ToXna()));
  178. }
  179. else
  180. {
  181. SetValue(element, new Microsoft.Xna.Framework.Graphics.PackedVector.Byte4(value.ToXna()));
  182. }
  183. return;
  184. case VertexElementFormat.Short4:
  185. SetValue(element, new Microsoft.Xna.Framework.Graphics.PackedVector.Short4(value.ToXna()));
  186. return;
  187. case VertexElementFormat.NormalizedShort4:
  188. SetValue(element, new Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedShort4(value.ToXna()));
  189. return;
  190. }
  191. throw new NotImplementedException();
  192. }
  193. public unsafe void SetValue(VertexElement element, Microsoft.Xna.Framework.Graphics.PackedVector.Byte4 value)
  194. {
  195. if (element.VertexElementFormat != VertexElementFormat.Byte4) throw new ArgumentException(nameof(element));
  196. var dst = _Vertex.Slice(element.Offset);
  197. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, Microsoft.Xna.Framework.Graphics.PackedVector.Byte4>(dst)[0] = value;
  198. }
  199. public unsafe void SetValue(VertexElement element, Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedByte4 value)
  200. {
  201. if (element.VertexElementFormat != VertexElementFormat.Byte4) throw new ArgumentException(nameof(element));
  202. var dst = _Vertex.Slice(element.Offset);
  203. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedByte4>(dst)[0] = value;
  204. }
  205. public unsafe void SetValue(VertexElement element, Microsoft.Xna.Framework.Graphics.PackedVector.Short4 value)
  206. {
  207. if (element.VertexElementFormat != VertexElementFormat.Short4) throw new ArgumentException(nameof(element));
  208. var dst = _Vertex.Slice(element.Offset);
  209. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, Microsoft.Xna.Framework.Graphics.PackedVector.Short4>(dst)[0] = value;
  210. }
  211. public unsafe void SetValue(VertexElement element, Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedShort4 value)
  212. {
  213. if (element.VertexElementFormat != VertexElementFormat.NormalizedShort4) throw new ArgumentException(nameof(element));
  214. var dst = _Vertex.Slice(element.Offset);
  215. System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedShort4>(dst)[0] = value;
  216. }
  217. #endregion
  218. }
  219. #endregion
  220. #region Support methods for VertexNormalsFactory and VertexTangentsFactory
  221. IEnumerable<(int A, int B, int C)> VertexNormalsFactory.IMeshPrimitive.GetTriangleIndices() { return _TrianglesSource; }
  222. IEnumerable<(int A, int B, int C)> VertexTangentsFactory.IMeshPrimitive.GetTriangleIndices() { return _TrianglesSource; }
  223. XYZ VertexNormalsFactory.IMeshPrimitive.GetVertexPosition(int idx) { return GetPosition(idx); }
  224. XYZ VertexTangentsFactory.IMeshPrimitive.GetVertexPosition(int idx) { return GetPosition(idx); }
  225. XYZ VertexTangentsFactory.IMeshPrimitive.GetVertexNormal(int idx) { return GetNormal(idx); }
  226. XY VertexTangentsFactory.IMeshPrimitive.GetVertexTexCoord(int idx) { return GetTextureCoord(idx, 0); }
  227. void VertexNormalsFactory.IMeshPrimitive.SetVertexNormal(int idx, XYZ normal)
  228. {
  229. _Normals ??= new XYZ[VertexCount];
  230. if (!(_Normals is XYZ[])) return; // if it's not a plain array, it's a glTF source, so we prevent writing existing normals.
  231. _Normals[idx] = normal;
  232. }
  233. void VertexTangentsFactory.IMeshPrimitive.SetVertexTangent(int idx, XYZW tangent)
  234. {
  235. _Tangents ??= new XYZW[VertexCount];
  236. if (!(_Tangents is XYZW[])) return; // if it's not a plain array, it's a glTF source, so we prevent writing existing tangents.
  237. _Tangents[idx] = tangent;
  238. }
  239. #endregion
  240. }
  241. sealed class MeshPrimitiveWriter
  242. {
  243. #region data
  244. // shared buffers
  245. private readonly Dictionary<Type, IPrimitivesBuffers> _Buffers = new Dictionary<Type, IPrimitivesBuffers>();
  246. // primitives
  247. private readonly List<_MeshPrimitive> _MeshPrimitives = new List<_MeshPrimitive>();
  248. #endregion
  249. #region API
  250. public void WriteMeshPrimitive<TVertex>(int logicalMeshIndex, Effect effect, MeshPrimitiveReader primitive)
  251. where TVertex : unmanaged, IVertexType
  252. {
  253. if (!_Buffers.TryGetValue(typeof(TVertex), out IPrimitivesBuffers pb))
  254. {
  255. _Buffers[typeof(TVertex)] = pb = new _PrimitivesBuffers<TVertex>();
  256. }
  257. var part = (pb as _PrimitivesBuffers<TVertex>).Append(logicalMeshIndex, effect, primitive);
  258. _MeshPrimitives.Add(part);
  259. }
  260. internal IReadOnlyDictionary<int, RuntimeModelMesh> GetRuntimeMeshes(GraphicsDevice device, GraphicsResourceTracker disposables)
  261. {
  262. // create shared vertex/index buffers
  263. var vbuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateVertexBuffer(device));
  264. var ibuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateIndexBuffer(device));
  265. foreach (var vb in vbuffers.Values) disposables.AddDisposable(vb);
  266. foreach (var ib in ibuffers.Values) disposables.AddDisposable(ib);
  267. // create RuntimeModelMesh
  268. RuntimeModelMesh _convert(IEnumerable<_MeshPrimitive> srcParts)
  269. {
  270. var dstMesh = new RuntimeModelMesh(device);
  271. foreach(var srcPart in srcParts)
  272. {
  273. var vb = vbuffers[srcPart.PrimitiveBuffers];
  274. var ib = ibuffers[srcPart.PrimitiveBuffers];
  275. var dstPart = dstMesh.CreateMeshPart();
  276. dstPart.Effect = srcPart.PrimitiveEffect;
  277. dstPart.SetVertexBuffer(vb, srcPart.VertexOffset, srcPart.VertexCount);
  278. dstPart.SetIndexBuffer(ib, srcPart.TriangleOffset * 3, srcPart.TriangleCount);
  279. }
  280. return dstMesh;
  281. }
  282. return _MeshPrimitives
  283. .GroupBy(item => item.LogicalMeshIndex)
  284. .ToDictionary(k => k.Key, v => _convert(v));
  285. }
  286. #endregion
  287. #region nested types
  288. interface IPrimitivesBuffers
  289. {
  290. VertexBuffer CreateVertexBuffer(GraphicsDevice device);
  291. IndexBuffer CreateIndexBuffer(GraphicsDevice device);
  292. }
  293. /// <summary>
  294. /// Contains the shared vertex/index buffers of all the mesh primitive that share the same vertex type.
  295. /// </summary>
  296. /// <typeparam name="TVertex"></typeparam>
  297. sealed class _PrimitivesBuffers<TVertex> : IPrimitivesBuffers
  298. where TVertex : unmanaged, IVertexType
  299. {
  300. #region data
  301. private readonly List<TVertex> _Vertices = new List<TVertex>();
  302. private readonly List<(int,int,int)> _Triangles = new List<(int, int, int)>();
  303. #endregion
  304. #region API
  305. public _MeshPrimitive Append(int meshKey, Effect effect, MeshPrimitiveReader primitive)
  306. {
  307. var partVertices = primitive.ToXnaVertices<TVertex>();
  308. var partTriangles = primitive.TriangleIndices;
  309. var part = new _MeshPrimitive
  310. {
  311. LogicalMeshIndex = meshKey,
  312. PrimitiveEffect = effect,
  313. PrimitiveBuffers = this,
  314. VertexOffset = _Vertices.Count,
  315. VertexCount = partVertices.Length,
  316. TriangleOffset = _Triangles.Count,
  317. TriangleCount = partTriangles.Length
  318. };
  319. _Vertices.AddRange(partVertices);
  320. _Triangles.AddRange(partTriangles);
  321. return part;
  322. }
  323. public VertexBuffer CreateVertexBuffer(GraphicsDevice device)
  324. {
  325. var data = new VertexBuffer(device, typeof(TVertex), _Vertices.Count, BufferUsage.None);
  326. data.SetData(_Vertices.ToArray());
  327. return data;
  328. }
  329. public IndexBuffer CreateIndexBuffer(GraphicsDevice device)
  330. {
  331. return CreateIndexBuffer(device, _Triangles);
  332. }
  333. private static IndexBuffer CreateIndexBuffer(GraphicsDevice device, IEnumerable<(int A, int B, int C)> triangles)
  334. {
  335. var sequence32 = triangles
  336. .SelectMany(item => new[] { (UInt32)item.C, (UInt32)item.B, (UInt32)item.A })
  337. .ToArray();
  338. var max = sequence32.Max();
  339. if (max > 65535)
  340. {
  341. var indices = new IndexBuffer(device, typeof(UInt32), sequence32.Length, BufferUsage.None);
  342. indices.SetData(sequence32);
  343. return indices;
  344. }
  345. else
  346. {
  347. var sequence16 = sequence32.Select(item => (UInt16)item).ToArray();
  348. var indices = new IndexBuffer(device, typeof(UInt16), sequence16.Length, BufferUsage.None);
  349. indices.SetData(sequence16);
  350. return indices;
  351. }
  352. }
  353. #endregion
  354. }
  355. /// <summary>
  356. /// Represents a mesh primitive
  357. /// </summary>
  358. struct _MeshPrimitive
  359. {
  360. public int LogicalMeshIndex;
  361. public Effect PrimitiveEffect;
  362. public IPrimitivesBuffers PrimitiveBuffers;
  363. public int VertexOffset;
  364. public int VertexCount;
  365. public int TriangleOffset;
  366. public int TriangleCount;
  367. }
  368. #endregion
  369. }
  370. }