MeshBuilderAdvancedTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Linq;
  5. using NUnit.Framework;
  6. using SharpGLTF.Materials;
  7. using SharpGLTF.Geometry.Parametric;
  8. using SharpGLTF.Schema2;
  9. using SharpGLTF.Scenes;
  10. namespace SharpGLTF.Geometry
  11. {
  12. using VEMPTY = Geometry.VertexTypes.VertexEmpty;
  13. using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
  14. using VPOS = Geometry.VertexTypes.VertexPosition;
  15. using VSKIN4 = Geometry.VertexTypes.VertexJoints4;
  16. [TestFixture]
  17. [Category("Model Authoring")]
  18. public class MeshBuilderAdvancedTests
  19. {
  20. [Test(Description = "Creates a scene using a mesh builder helper class")]
  21. public void CreateSceneWithMeshBuilder()
  22. {
  23. TestContext.CurrentContext.AttachGltfValidatorLinks();
  24. // create a material
  25. var material1 = new MaterialBuilder("material1")
  26. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, Vector4.One);
  27. // create model
  28. var meshBuilder = new MeshBuilder<VPOSNRM>("mesh1");
  29. meshBuilder.VertexPreprocessor.SetValidationPreprocessors();
  30. // define 4 vertices
  31. var v1 = new VPOSNRM(-10, 10, 0, 0, 0, 1);
  32. var v2 = new VPOSNRM(10, 10, 0, 0, 0, 1);
  33. var v3 = new VPOSNRM(10, -10, 0, 0, 0, 1);
  34. var v4 = new VPOSNRM(-10, -10, 0, 0, 0, 1);
  35. // add a polygon to the primitive that uses material1 as key.
  36. meshBuilder.UsePrimitive(material1).AddQuadrangle(v1, v2, v3, v4);
  37. // create a scene
  38. var scene = new SceneBuilder();
  39. scene.AddRigidMesh(meshBuilder, Matrix4x4.Identity);
  40. scene.AttachToCurrentTest("result.glb");
  41. scene.AttachToCurrentTest("result.gltf");
  42. scene.AttachToCurrentTest("result.plotly");
  43. }
  44. [Test(Description = "Creates a scene with 4 meshes, where the meshes have been initialized so they can share the same vertex and index buffers")]
  45. public void CreateSceneWithSharedBuffers()
  46. {
  47. TestContext.CurrentContext.AttachGltfValidatorLinks();
  48. // create materials
  49. var material1 = new MaterialBuilder("material1")
  50. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 1, 0, 1));
  51. var material2 = new MaterialBuilder("material1")
  52. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1));
  53. // create several meshes
  54. var meshBuilder1 = new MeshBuilder<VPOSNRM>("mesh1");
  55. meshBuilder1.VertexPreprocessor.SetValidationPreprocessors();
  56. var meshBuilder2 = new MeshBuilder<VPOSNRM>("mesh2");
  57. meshBuilder2.VertexPreprocessor.SetValidationPreprocessors();
  58. var meshBuilder3 = new MeshBuilder<VPOSNRM>("mesh3");
  59. meshBuilder3.VertexPreprocessor.SetValidationPreprocessors();
  60. var meshBuilder4 = new MeshBuilder<VPOSNRM>("mesh4");
  61. meshBuilder4.VertexPreprocessor.SetValidationPreprocessors();
  62. meshBuilder1.AddCube(material1, Matrix4x4.Identity);
  63. meshBuilder2.AddCube(material2, Matrix4x4.Identity);
  64. meshBuilder3.AddSphere(material1, 0.5f, Matrix4x4.Identity);
  65. meshBuilder4.AddSphere(material2, 0.5f, Matrix4x4.Identity);
  66. meshBuilder1.Validate();
  67. meshBuilder2.Validate();
  68. meshBuilder3.Validate();
  69. meshBuilder4.Validate();
  70. // create scene nodes
  71. var pivot1 = new NodeBuilder("Cube1").WithLocalTranslation(new Vector3(-5, 0, 0));
  72. var pivot2 = new NodeBuilder("Cube2").WithLocalTranslation(new Vector3(0, 5, 0));
  73. var pivot3 = new NodeBuilder("SPhere1").WithLocalTranslation(new Vector3(+5, 0, 0));
  74. var pivot4 = new NodeBuilder("SPhere2").WithLocalTranslation(new Vector3(0, -5, 0));
  75. // create the scene:
  76. var scene = new SceneBuilder();
  77. scene.AddRigidMesh(meshBuilder1, pivot1);
  78. scene.AddRigidMesh(meshBuilder2, pivot2);
  79. scene.AddRigidMesh(meshBuilder3, pivot3);
  80. scene.AddRigidMesh(meshBuilder4, pivot4);
  81. // convert to gltf2
  82. var model = scene.ToGltf2();
  83. model.MergeBuffers();
  84. // checks
  85. Assert.That(model.LogicalBuffers, Has.Count.EqualTo(1));
  86. Assert.That(model.LogicalBufferViews, Has.Count.EqualTo(2));
  87. Assert.That(model.LogicalBufferViews[0].IsVertexBuffer);
  88. Assert.That(model.LogicalBufferViews[1].IsIndexBuffer);
  89. Assert.That(model.LogicalMaterials, Has.Count.EqualTo(2));
  90. model.AttachToCurrentTest("result.glb");
  91. model.AttachToCurrentTest("result.gltf");
  92. scene.AttachToCurrentTest("result.plotly");
  93. }
  94. [Test(Description = "Creates a node animated scene.")]
  95. public void CreateSceneWithAnimatedMeshBuilder()
  96. {
  97. TestContext.CurrentContext.AttachGltfValidatorLinks();
  98. // create a material
  99. var material1 = new MaterialBuilder("material1").WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, Vector4.One);
  100. // create a mesh
  101. var meshBuilder = new MeshBuilder<VPOSNRM>("mesh1");
  102. meshBuilder.VertexPreprocessor.SetValidationPreprocessors();
  103. meshBuilder.AddCube(material1, Matrix4x4.Identity);
  104. meshBuilder.Validate();
  105. // create an animated node
  106. var keyframes = new Dictionary<Single, Vector3>()
  107. {
  108. [1] = new Vector3(0, 0, 0),
  109. [2] = new Vector3(50, 0, 0),
  110. [3] = new Vector3(0, 50, 0),
  111. [4] = new Vector3(0, 0, 0),
  112. };
  113. var pivot = new NodeBuilder("RootNode").WithLocalTranslation("track1", keyframes);
  114. Assert.That(pivot.UseTranslation("track1").Keys, Has.Count.EqualTo(4));
  115. // create scene
  116. var scene = new SceneBuilder();
  117. scene.AddRigidMesh(meshBuilder, pivot);
  118. scene.AttachToCurrentTest("result.glb");
  119. scene.AttachToCurrentTest("result.gltf");
  120. }
  121. [Test(Description = "Creates a skinned animated scene.")]
  122. public void CreateSceneWithSkinnedAnimatedMeshBuilder()
  123. {
  124. TestContext.CurrentContext.AttachGltfValidatorLinks();
  125. // create animation sequence with 4 frames
  126. var keyframes = new Dictionary<Single, Quaternion>
  127. {
  128. [1] = Quaternion.Identity,
  129. [2] = Quaternion.CreateFromYawPitchRoll(0, 1, 0),
  130. [3] = Quaternion.CreateFromYawPitchRoll(0, 0, 1),
  131. [4] = Quaternion.Identity,
  132. };
  133. // create two materials
  134. var pink = new MaterialBuilder("material1")
  135. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1))
  136. .WithDoubleSide(true);
  137. var yellow = new MaterialBuilder("material2")
  138. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 1, 0, 1))
  139. .WithDoubleSide(true);
  140. // create the mesh
  141. var meshBuilder = new MeshBuilder<VPOS, VEMPTY, VSKIN4>("mesh1");
  142. #if DEBUG
  143. meshBuilder.VertexPreprocessor.SetValidationPreprocessors();
  144. #else
  145. meshBuilder.VertexPreprocessor.SetSanitizerPreprocessors();
  146. #endif
  147. const int jointIdx0 = 0;
  148. const int jointIdx1 = 1;
  149. const int jointIdx2 = 2;
  150. var v1 = (new VPOS(-10, 0, +10), new VSKIN4(jointIdx0));
  151. var v2 = (new VPOS(+10, 0, +10), new VSKIN4(jointIdx0));
  152. var v3 = (new VPOS(+10, 0, -10), new VSKIN4(jointIdx0));
  153. var v4 = (new VPOS(-10, 0, -10), new VSKIN4(jointIdx0));
  154. var v5 = (new VPOS(-10, 40, +10), new VSKIN4((jointIdx0, 0.5f), (jointIdx1, 0.5f)));
  155. var v6 = (new VPOS(+10, 40, +10), new VSKIN4((jointIdx0, 0.5f), (jointIdx1, 0.5f)));
  156. var v7 = (new VPOS(+10, 40, -10), new VSKIN4((jointIdx0, 0.5f), (jointIdx1, 0.5f)));
  157. var v8 = (new VPOS(-10, 40, -10), new VSKIN4((jointIdx0, 0.5f), (jointIdx1, 0.5f)));
  158. var v9 = (new VPOS(-5, 80, +5), new VSKIN4(jointIdx2));
  159. var v10 = (new VPOS(+5, 80, +5), new VSKIN4(jointIdx2));
  160. var v11 = (new VPOS(+5, 80, -5), new VSKIN4(jointIdx2));
  161. var v12 = (new VPOS(-5, 80, -5), new VSKIN4(jointIdx2));
  162. meshBuilder.UsePrimitive(pink).AddQuadrangle(v1, v2, v6, v5);
  163. meshBuilder.UsePrimitive(pink).AddQuadrangle(v2, v3, v7, v6);
  164. meshBuilder.UsePrimitive(pink).AddQuadrangle(v3, v4, v8, v7);
  165. meshBuilder.UsePrimitive(pink).AddQuadrangle(v4, v1, v5, v8);
  166. meshBuilder.UsePrimitive(yellow).AddQuadrangle(v5, v6, v10, v9);
  167. meshBuilder.UsePrimitive(yellow).AddQuadrangle(v6, v7, v11, v10);
  168. meshBuilder.UsePrimitive(yellow).AddQuadrangle(v7, v8, v12, v11);
  169. meshBuilder.UsePrimitive(yellow).AddQuadrangle(v8, v5, v9, v12);
  170. meshBuilder.Validate();
  171. // create base model
  172. var model = ModelRoot.CreateModel();
  173. var scene = model.UseScene("Default");
  174. // create the three joints that will affect the mesh
  175. var skelet = scene.CreateNode("Skeleton");
  176. var joint0 = skelet.CreateNode("Joint 0").WithLocalTranslation(new Vector3(0, 0, 0));
  177. var joint1 = joint0.CreateNode("Joint 1").WithLocalTranslation(new Vector3(0, 40, 0)).WithRotationAnimation("Base Track", keyframes);
  178. var joint2 = joint1.CreateNode("Joint 2").WithLocalTranslation(new Vector3(0, 40, 0));
  179. // setup skin
  180. var snode = scene.CreateNode("Skeleton Node");
  181. snode.Skin = model.CreateSkin();
  182. snode.Skin.BindJoints(joint0, joint1, joint2);
  183. snode.WithMesh( model.CreateMesh(meshBuilder) );
  184. model.AttachToCurrentTest("result.glb");
  185. model.AttachToCurrentTest("result.gltf");
  186. }
  187. [Test(Description = "Creates a textured terrain mesh.")]
  188. public void CreateSceneWithTerrain()
  189. {
  190. TestContext.CurrentContext.AttachGltfValidatorLinks();
  191. // texture path
  192. var imagePath = System.IO.Path.Combine(TestContext.CurrentContext.WorkDirectory, "Assets", "Texture1.jpg");
  193. // fancy height function; can be easily replaced with a bitmap sampler.
  194. float heightFunction(int xx, int yy)
  195. {
  196. float x = xx;
  197. float y = yy;
  198. double h = 0;
  199. h += Math.Sin(x / 45);
  200. h += Math.Sin(3 + x / 13) * 0.5f;
  201. h += Math.Sin(2 + y / 31);
  202. h += Math.Sin(y / 13) * 0.5f;
  203. h += Math.Sin((x + y * 2) / 19);
  204. h *= 5;
  205. return (float)h;
  206. }
  207. var terrain = SolidMeshUtils.CreateTerrainMesh(128,128, heightFunction, imagePath);
  208. // create a new gltf model
  209. var model = ModelRoot.CreateModel();
  210. // add all meshes (just one in this case) to the model
  211. model.CreateMeshes(terrain);
  212. // create a scene, a node, and assign the first mesh (the terrain)
  213. model.UseScene("Default")
  214. .CreateNode().WithMesh(model.LogicalMeshes[0]);
  215. // save the model as GLB
  216. model.AttachToCurrentTest("terrain.glb");
  217. model.AttachToCurrentTest("result.plotly");
  218. }
  219. [Test(Description = "Creates a scene with 1 million points cloud.")]
  220. public void CreateSceneWithPointCloud()
  221. {
  222. TestContext.CurrentContext.AttachGltfValidatorLinks();
  223. var material = new MaterialBuilder("material1").WithUnlitShader();
  224. var mesh = new MeshBuilder<VPOS, Geometry.VertexTypes.VertexColor1>("points");
  225. mesh.VertexPreprocessor.SetValidationPreprocessors();
  226. // create a point cloud primitive
  227. var pointCloud = mesh.UsePrimitive(material, 1);
  228. var rnd = new Random(178);
  229. for (int i = 0; i < 1000000; ++i)
  230. {
  231. var x = (float)(rnd.NextDouble() * 2 - 1);
  232. var y = (float)(rnd.NextDouble() * 2 - 1);
  233. var z = (float)(rnd.NextDouble() * 2 - 1);
  234. var opacity = Math.Max(Math.Max(Math.Abs(x), Math.Abs(y)), Math.Abs(z));
  235. opacity = opacity * opacity * opacity * opacity;
  236. var r = (float)rnd.NextDouble() * opacity;
  237. var g = (float)rnd.NextDouble() * opacity;
  238. var b = (float)rnd.NextDouble() * opacity;
  239. x *= 50;
  240. y *= 50;
  241. z *= 50;
  242. pointCloud.AddPoint((new Vector3(x, y + 60, z), new Vector4(r, g, b, 1)));
  243. }
  244. // adds 4 lines as the base of the points
  245. mesh.UsePrimitive(material, 2).AddLine((new Vector3(-50, 0, -50), Vector4.One), (new Vector3(+50, 0, -50), Vector4.UnitW));
  246. mesh.UsePrimitive(material, 2).AddLine((new Vector3(+50, 0, -50), Vector4.One), (new Vector3(+50, 0, +50), Vector4.UnitW));
  247. mesh.UsePrimitive(material, 2).AddLine((new Vector3(+50, 0, +50), Vector4.One), (new Vector3(-50, 0, +50), Vector4.UnitW));
  248. mesh.UsePrimitive(material, 2).AddLine((new Vector3(-50, 0, +50), Vector4.One), (new Vector3(-50, 0, -50), Vector4.UnitW));
  249. // create a new gltf model
  250. var model = ModelRoot.CreateModel();
  251. // add all meshes (just one in this case) to the model
  252. model.CreateMeshes(mesh);
  253. // create a scene, a node, and assign the first mesh (the terrain)
  254. model.UseScene("Default")
  255. .CreateNode().WithMesh(model.LogicalMeshes[0]);
  256. // save the model as GLB
  257. model.AttachToCurrentTest("PointCloud.glb");
  258. }
  259. [Test(Description ="Creates a single mesh with multiple cubes.")]
  260. public void CreateMeshWithRandomCubes()
  261. {
  262. TestContext.CurrentContext.AttachGltfValidatorLinks();
  263. var rnd = new Random();
  264. var materials = Enumerable
  265. .Range(0, 10)
  266. .Select(idx => MaterialBuilder.CreateDefault()
  267. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(rnd.NextVector3(),1)))
  268. .ToList();
  269. // create a mesh
  270. var cubes = new MeshBuilder<VPOSNRM>("cube");
  271. cubes.VertexPreprocessor.SetValidationPreprocessors();
  272. for (int i=0; i < 100; ++i)
  273. {
  274. var r = rnd.NextVector3() * 5;
  275. var m = materials[rnd.Next(0, 10)];
  276. var xform = Matrix4x4.CreateFromYawPitchRoll(r.X,r.Y,r.Z) * Matrix4x4.CreateTranslation(rnd.NextVector3() * 25);
  277. cubes.AddCube(m, xform);
  278. }
  279. cubes.Validate();
  280. var scene = new SceneBuilder();
  281. scene.AddRigidMesh(cubes, Matrix4x4.Identity);
  282. // save the model as GLB
  283. scene.AttachToCurrentTest("cubes.glb");
  284. scene.AttachToCurrentTest("cubes.gltf");
  285. scene.AttachToCurrentTest("cubes.plotly");
  286. }
  287. [Test(Description ="Simulates animating mesh visibility by setting scale to (0,0,0)")]
  288. public void CreateSceneWithAnimatedVisibility()
  289. {
  290. TestContext.CurrentContext.AttachGltfValidatorLinks();
  291. // create a mesh
  292. var cube = new MeshBuilder<VPOSNRM>("cube");
  293. cube.VertexPreprocessor.SetValidationPreprocessors();
  294. cube.AddCube(MaterialBuilder.CreateDefault(), Matrix4x4.Identity);
  295. cube.Validate();
  296. // create a node and animate it
  297. var pivot = new NodeBuilder();
  298. var flatx = new Vector3(0, 1, 1);
  299. var flaty = new Vector3(1, 0, 1);
  300. var flatz = new Vector3(1, 1, 0);
  301. pivot.UseScale("Track1")
  302. .WithPoint(0, Vector3.One)
  303. .WithPoint(1 - 0.0001f, Vector3.One)
  304. .WithPoint(1, Vector3.Zero)
  305. .WithPoint(2 - 0.0001f, Vector3.Zero)
  306. .WithPoint(2, Vector3.One)
  307. .WithPoint(3, Vector3.One)
  308. .WithPoint(4, -Vector3.One)
  309. .WithPoint(5, -Vector3.One)
  310. .WithPoint(6, Vector3.One)
  311. .WithPoint(7, flatx)
  312. .WithPoint(8, flatx)
  313. .WithPoint(9, flaty)
  314. .WithPoint(10, flaty)
  315. .WithPoint(11, flatz)
  316. .WithPoint(12, flatz)
  317. .WithPoint(13, Vector3.One);
  318. // create the scene
  319. var scene = new SceneBuilder();
  320. scene.AddRigidMesh(cube, pivot);
  321. // save the model
  322. scene.AttachToCurrentTest("animatedvisibility.glb");
  323. scene.AttachToCurrentTest("animatedvisibility.gltf");
  324. }
  325. }
  326. }