SolidMeshUtils.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Text;
  5. using SharpGLTF.Geometry;
  6. using SharpGLTF.Geometry.VertexTypes;
  7. namespace SharpGLTF.Geometry.Parametric
  8. {
  9. using VERTEX = VertexBuilder<VertexPositionNormal, VertexColor1Texture1, VertexEmpty>;
  10. abstract class ParametricShape<TMaterial>
  11. {
  12. public abstract void AddTo(IMeshBuilder<TMaterial> meshBuilder, Matrix4x4 xform);
  13. public MeshBuilder<TMaterial, VertexPositionNormal, VertexColor1Texture1, VertexEmpty> ToMesh(Matrix4x4 xform)
  14. {
  15. var mesh = new MeshBuilder<TMaterial, VertexPositionNormal, VertexColor1Texture1, VertexEmpty>();
  16. AddTo(mesh, xform);
  17. return mesh;
  18. }
  19. }
  20. class Cube<TMaterial> : ParametricShape<TMaterial>
  21. {
  22. // TODO:
  23. // Faces and UV alignment should follow: https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Vendor/EXT_lights_image_based/figures/Cube_map.svg
  24. #region lifecycle
  25. public Cube(TMaterial material)
  26. {
  27. _Front = _Back = _Left = _Right = _Top = _Bottom = material;
  28. }
  29. public Cube(TMaterial material, float width, float height, float length)
  30. {
  31. _Front = _Back = _Left = _Right = _Top = _Bottom = material;
  32. _Size = new Vector3(width, height, length);
  33. }
  34. #endregion
  35. #region data
  36. private Vector3 _Size = Vector3.One;
  37. private TMaterial _Front;
  38. private TMaterial _Back;
  39. private TMaterial _Left;
  40. private TMaterial _Right;
  41. private TMaterial _Top;
  42. private TMaterial _Bottom;
  43. #endregion
  44. #region API
  45. public override void AddTo(IMeshBuilder<TMaterial> meshBuilder, Matrix4x4 xform)
  46. {
  47. var x = Vector3.UnitX * _Size.X * 0.5f;
  48. var y = Vector3.UnitY * _Size.Y * 0.5f;
  49. var z = Vector3.UnitZ * _Size.Z * 0.5f;
  50. _AddCubeFace(meshBuilder.UsePrimitive(_Right), x, y, z, xform);
  51. _AddCubeFace(meshBuilder.UsePrimitive(_Left), -x, z, y, xform);
  52. _AddCubeFace(meshBuilder.UsePrimitive(_Top), y, z, x, xform);
  53. _AddCubeFace(meshBuilder.UsePrimitive(_Bottom), -y, x, z, xform);
  54. _AddCubeFace(meshBuilder.UsePrimitive(_Front), z, x, y, xform);
  55. _AddCubeFace(meshBuilder.UsePrimitive(_Back), -z, y, x, xform);
  56. }
  57. private static void _AddCubeFace(IPrimitiveBuilder primitiveBuilder, Vector3 origin, Vector3 axisX, Vector3 axisY, Matrix4x4 xform)
  58. {
  59. var p1 = Vector3.Transform(origin - axisX - axisY, xform);
  60. var p2 = Vector3.Transform(origin + axisX - axisY, xform);
  61. var p3 = Vector3.Transform(origin + axisX + axisY, xform);
  62. var p4 = Vector3.Transform(origin - axisX + axisY, xform);
  63. var n = Vector3.Normalize(Vector3.TransformNormal(origin, xform));
  64. primitiveBuilder.AddQuadrangle
  65. (
  66. new VERTEX( (p1, n), (Vector4.One, Vector2.Zero) ),
  67. new VERTEX( (p2, n), (Vector4.One, Vector2.UnitX) ),
  68. new VERTEX( (p3, n), (Vector4.One, Vector2.One) ),
  69. new VERTEX( (p4, n), (Vector4.One, Vector2.UnitY) )
  70. );
  71. }
  72. #endregion
  73. }
  74. class IcoSphere<TMaterial> : ParametricShape<TMaterial>
  75. {
  76. #region lifecycle
  77. public IcoSphere(TMaterial material, float radius = 0.5f)
  78. {
  79. _Material = material;
  80. _Radius = radius;
  81. }
  82. #endregion
  83. #region data
  84. private float _Radius = 0.5f;
  85. private TMaterial _Material;
  86. private int _Subdivision = 3;
  87. #endregion
  88. #region API
  89. public override void AddTo(IMeshBuilder<TMaterial> meshBuilder, Matrix4x4 xform)
  90. {
  91. // http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
  92. var t = 1 + (float)(Math.Sqrt(5.0) / 2);
  93. var v0 = new Vector3(-1, t, 0) * _Radius;
  94. var v1 = new Vector3(1, t, 0) * _Radius;
  95. var v2 = new Vector3(-1, -t, 0) * _Radius;
  96. var v3 = new Vector3(1, -t, 0) * _Radius;
  97. var v4 = new Vector3(0, -1, t) * _Radius;
  98. var v5 = new Vector3(0, 1, t) * _Radius;
  99. var v6 = new Vector3(0, -1, -t) * _Radius;
  100. var v7 = new Vector3(0, 1, -t) * _Radius;
  101. var v8 = new Vector3(t, 0, -1) * _Radius;
  102. var v9 = new Vector3(t, 0, 1) * _Radius;
  103. var v10 = new Vector3(-t, 0, -1) * _Radius;
  104. var v11 = new Vector3(-t, 0, 1) * _Radius;
  105. var prim = meshBuilder.UsePrimitive(_Material);
  106. // 5 faces around point 0
  107. _AddSphereFace(prim, xform, v0, v11, v5, _Subdivision);
  108. _AddSphereFace(prim, xform, v0, v5, v1, _Subdivision);
  109. _AddSphereFace(prim, xform, v0, v1, v7, _Subdivision);
  110. _AddSphereFace(prim, xform, v0, v7, v10, _Subdivision);
  111. _AddSphereFace(prim, xform, v0, v10, v11, _Subdivision);
  112. // 5 adjacent faces
  113. _AddSphereFace(prim, xform, v1, v5, v9, _Subdivision);
  114. _AddSphereFace(prim, xform, v5, v11, v4, _Subdivision);
  115. _AddSphereFace(prim, xform, v11, v10, v2, _Subdivision);
  116. _AddSphereFace(prim, xform, v10, v7, v6, _Subdivision);
  117. _AddSphereFace(prim, xform, v7, v1, v8, _Subdivision);
  118. // 5 faces around point 3
  119. _AddSphereFace(prim, xform, v3, v9, v4, _Subdivision);
  120. _AddSphereFace(prim, xform, v3, v4, v2, _Subdivision);
  121. _AddSphereFace(prim, xform, v3, v2, v6, _Subdivision);
  122. _AddSphereFace(prim, xform, v3, v6, v8, _Subdivision);
  123. _AddSphereFace(prim, xform, v3, v8, v9, _Subdivision);
  124. // 5 adjacent faces
  125. _AddSphereFace(prim, xform, v4, v9, v5, _Subdivision);
  126. _AddSphereFace(prim, xform, v2, v4, v11, _Subdivision);
  127. _AddSphereFace(prim, xform, v6, v2, v10, _Subdivision);
  128. _AddSphereFace(prim, xform, v8, v6, v7, _Subdivision);
  129. _AddSphereFace(prim, xform, v9, v8, v1, _Subdivision);
  130. }
  131. private static void _AddSphereFace(IPrimitiveBuilder primitiveBuilder, Matrix4x4 xform, Vector3 a, Vector3 b, Vector3 c, int iterations = 0)
  132. {
  133. if (iterations <= 0)
  134. {
  135. var tt = (a + b + c) / 3.0f;
  136. var aa = _CreateVertex(a, xform);
  137. var bb = _CreateVertex(b, xform);
  138. var cc = _CreateVertex(c, xform);
  139. primitiveBuilder.AddTriangle(aa, bb, cc);
  140. return;
  141. }
  142. --iterations;
  143. var ab = Vector3.Normalize(a + b) * a.Length();
  144. var bc = Vector3.Normalize(b + c) * b.Length();
  145. var ca = Vector3.Normalize(c + a) * c.Length();
  146. // central triangle
  147. _AddSphereFace(primitiveBuilder, xform, ab, bc, ca, iterations);
  148. // vertex triangles
  149. _AddSphereFace(primitiveBuilder, xform, a, ab, ca, iterations);
  150. _AddSphereFace(primitiveBuilder, xform, b, bc, ab, iterations);
  151. _AddSphereFace(primitiveBuilder, xform, c, ca, bc, iterations);
  152. }
  153. private static VERTEX _CreateVertex(Vector3 position, Matrix4x4 xform)
  154. {
  155. var v = new VERTEX();
  156. v.Geometry.Position = Vector3.Transform(position, xform);
  157. v.Geometry.Normal = Vector3.Normalize(Vector3.TransformNormal(position, xform));
  158. v.Material.Color = Vector4.One;
  159. v.Material.TexCoord = Vector2.Zero;
  160. return v;
  161. }
  162. #endregion
  163. }
  164. static class SolidMeshUtils
  165. {
  166. public static void AddCube<TMaterial>(this IMeshBuilder<TMaterial> meshBuilder, TMaterial material, Matrix4x4 xform)
  167. {
  168. var cube = new Cube<TMaterial>(material);
  169. cube.AddTo(meshBuilder, xform);
  170. }
  171. public static void AddSphere<TMaterial>(this IMeshBuilder<TMaterial> meshBuilder, TMaterial material, Single radius, Matrix4x4 xform)
  172. {
  173. var sphere = new IcoSphere<TMaterial>(material, radius);
  174. sphere.AddTo(meshBuilder, xform);
  175. }
  176. public static MeshBuilder<VertexPosition, VertexTexture1> CreateTerrainMesh(int width, int length, Func<int,int,float> heightFunction, string terrainColorImagePath)
  177. {
  178. // we create a new material to use with the terrain mesh
  179. var material = new Materials.MaterialBuilder("TerrainMaterial")
  180. .WithChannelImage(Materials.KnownChannel.BaseColor, terrainColorImagePath);
  181. // we create a MeshBuilder
  182. var terrainMesh = new MeshBuilder<VertexPosition, VertexTexture1>("terrain");
  183. var texScale = new Vector2(width, length);
  184. // fill the MeshBuilder with quads using the heightFunction.
  185. for (int y = 1; y < length; ++y)
  186. {
  187. for (int x = 1; x < width; ++x)
  188. {
  189. // quad vertex positions
  190. var a = new Vector3(x - 1, heightFunction(x - 1, y + 0), y + 0);
  191. var b = new Vector3(x + 0, heightFunction(x + 0, y + 0), y + 0);
  192. var c = new Vector3(x + 0, heightFunction(x + 0, y - 1), y - 1);
  193. var d = new Vector3(x - 1, heightFunction(x - 1, y - 1), y - 1);
  194. // quad UV coordinates
  195. var at = new Vector2(a.X, a.Z) / texScale;
  196. var bt = new Vector2(b.X, b.Z) / texScale;
  197. var ct = new Vector2(c.X, c.Z) / texScale;
  198. var dt = new Vector2(d.X, d.Z) / texScale;
  199. terrainMesh
  200. .UsePrimitive(material)
  201. .AddQuadrangle
  202. (
  203. (a, at),
  204. (b, bt),
  205. (c, ct),
  206. (d, dt)
  207. );
  208. }
  209. }
  210. terrainMesh.Validate();
  211. return terrainMesh;
  212. }
  213. }
  214. }