MeshPrimitiveReader.cs 19 KB

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