babylon.glTFSerializer.tests.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /**
  2. * Describes the test suite
  3. */
  4. describe('Babylon glTF Serializer', () => {
  5. let subject: BABYLON.Engine;
  6. /**
  7. * Loads the dependencies
  8. */
  9. before(function (done) {
  10. this.timeout(180000);
  11. (BABYLONDEVTOOLS).Loader
  12. .useDist()
  13. .load(function () {
  14. // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
  15. BABYLON.PromisePolyfill.Apply(true);
  16. done();
  17. });
  18. });
  19. /**
  20. * Create a null engine subject before each test.
  21. */
  22. beforeEach(function () {
  23. subject = new BABYLON.NullEngine({
  24. renderHeight: 256,
  25. renderWidth: 256,
  26. textureSize: 256,
  27. deterministicLockstep: false,
  28. lockstepMaxSteps: 1
  29. });
  30. });
  31. /**
  32. * This tests the glTF serializer help functions
  33. */
  34. describe('#GLTF', () => {
  35. it('should get alpha mode from Babylon metallic roughness', () => {
  36. let alphaMode: string;
  37. const scene = new BABYLON.Scene(subject);
  38. const babylonMaterial = new BABYLON.PBRMetallicRoughnessMaterial("metallicroughness", scene);
  39. babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_OPAQUE;
  40. alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
  41. alphaMode.should.be.equal('OPAQUE');
  42. babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
  43. alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
  44. alphaMode.should.be.equal('BLEND');
  45. babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
  46. alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
  47. alphaMode.should.be.equal('BLEND');
  48. babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST;
  49. alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
  50. alphaMode.should.be.equal('MASK');
  51. });
  52. it('should convert Babylon standard material to metallic roughness', () => {
  53. const scene = new BABYLON.Scene(subject);
  54. const babylonStandardMaterial = new BABYLON.StandardMaterial("specGloss", scene);
  55. babylonStandardMaterial.diffuseColor = BABYLON.Color3.White();
  56. babylonStandardMaterial.specularColor = BABYLON.Color3.Black();
  57. babylonStandardMaterial.specularPower = 64;
  58. babylonStandardMaterial.alpha = 1;
  59. const metalRough = BABYLON.GLTF2._GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
  60. metalRough.baseColorFactor.should.deep.equal([0.5, 0.5, 0.5, 1]);
  61. metalRough.metallicFactor.should.be.equal(0);
  62. metalRough.roughnessFactor.should.be.approximately(0.328809, 1e-6);
  63. });
  64. it('should solve for metallic', () => {
  65. BABYLON.GLTF2._GLTFMaterial._SolveMetallic(1.0, 0.0, 1.0).should.be.equal(0);
  66. BABYLON.GLTF2._GLTFMaterial._SolveMetallic(0.0, 1.0, 1.0).should.be.approximately(1, 1e-6);
  67. });
  68. it('should serialize empty Babylon scene to glTF with only asset property', () => {
  69. const scene = new BABYLON.Scene(subject);
  70. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  71. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  72. const jsonData = JSON.parse(jsonString);
  73. Object.keys(jsonData).length.should.be.equal(1);
  74. jsonData.asset.version.should.be.equal("2.0");
  75. jsonData.asset.generator.should.be.equal("BabylonJS");
  76. });
  77. });
  78. it('should serialize sphere geometry in scene to glTF', () => {
  79. const scene = new BABYLON.Scene(subject);
  80. BABYLON.Mesh.CreateSphere('sphere', 16, 2, scene);
  81. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test')
  82. .then(glTFData => {
  83. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  84. const jsonData = JSON.parse(jsonString);
  85. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials
  86. Object.keys(jsonData).length.should.be.equal(9);
  87. // positions, normals, indices
  88. jsonData.accessors.length.should.be.equal(3);
  89. // generator, version
  90. Object.keys(jsonData.asset).length.should.be.equal(2);
  91. jsonData.buffers.length.should.be.equal(1);
  92. // positions, normals, texture coords, indices
  93. jsonData.bufferViews.length.should.be.equal(4);
  94. jsonData.meshes.length.should.be.equal(1);
  95. jsonData.nodes.length.should.be.equal(1);
  96. jsonData.scenes.length.should.be.equal(1);
  97. jsonData.scene.should.be.equal(0);
  98. });
  99. });
  100. it('should serialize alpha mode and cutoff', () => {
  101. const scene = new BABYLON.Scene(subject);
  102. const plane = BABYLON.Mesh.CreatePlane('plane', 120, scene);
  103. const babylonPBRMetalRoughMaterial = new BABYLON.PBRMetallicRoughnessMaterial('metalRoughMat', scene);
  104. babylonPBRMetalRoughMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST;
  105. const alphaCutoff = 0.8;
  106. babylonPBRMetalRoughMaterial.alphaCutOff = alphaCutoff;
  107. plane.material = babylonPBRMetalRoughMaterial;
  108. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  109. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  110. const jsonData = JSON.parse(jsonString);
  111. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials
  112. Object.keys(jsonData).length.should.be.equal(9);
  113. jsonData.materials.length.should.be.equal(2);
  114. jsonData.materials[0].alphaMode.should.be.equal('MASK');
  115. jsonData.materials[0].alphaCutoff.should.be.equal(alphaCutoff);
  116. });
  117. });
  118. it('should serialize single component translation animation to glTF', () => {
  119. const scene = new BABYLON.Scene(subject);
  120. const box = BABYLON.Mesh.CreateBox('box', 1, scene);
  121. let keys: BABYLON.IAnimationKey[] = [];
  122. keys.push({
  123. frame: 0,
  124. value: 1
  125. });
  126. keys.push({
  127. frame: 20,
  128. value: 0.2
  129. });
  130. keys.push({
  131. frame: 40,
  132. value: 1
  133. });
  134. let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'position.y', 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  135. animationBoxT.setKeys(keys);
  136. box.animations.push(animationBoxT);
  137. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  138. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  139. const jsonData = JSON.parse(jsonString);
  140. jsonData.animations.length.should.be.equal(1);
  141. const animation = jsonData.animations[0];
  142. animation.channels.length.should.be.equal(1);
  143. animation.channels[0].sampler.should.be.equal(0);
  144. animation.channels[0].target.node.should.be.equal(0);
  145. animation.channels[0].target.path.should.be.equal('translation');
  146. jsonData.animations[0].samplers.length.should.be.equal(1);
  147. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
  148. Object.keys(jsonData).length.should.be.equal(10);
  149. // positions, normals, indices, animation keyframe data, animation data
  150. jsonData.accessors.length.should.be.equal(5);
  151. // generator, version
  152. Object.keys(jsonData.asset).length.should.be.equal(2);
  153. jsonData.buffers.length.should.be.equal(1);
  154. // positions, normals, texture coords, indices, animation keyframe data, animation data
  155. jsonData.bufferViews.length.should.be.equal(6);
  156. jsonData.meshes.length.should.be.equal(1);
  157. jsonData.nodes.length.should.be.equal(1);
  158. jsonData.scenes.length.should.be.equal(1);
  159. jsonData.scene.should.be.equal(0);
  160. });
  161. });
  162. it('should serialize translation animation to glTF', () => {
  163. const scene = new BABYLON.Scene(subject);
  164. const box = BABYLON.Mesh.CreateBox('box', 1, scene);
  165. let keys: BABYLON.IAnimationKey[] = [];
  166. keys.push({
  167. frame: 0,
  168. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  169. });
  170. keys.push({
  171. frame: 20,
  172. value: BABYLON.Vector3.One()
  173. });
  174. keys.push({
  175. frame: 40,
  176. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  177. });
  178. let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'position', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  179. animationBoxT.setKeys(keys);
  180. box.animations.push(animationBoxT);
  181. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  182. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  183. const jsonData = JSON.parse(jsonString);
  184. jsonData.animations.length.should.be.equal(1);
  185. const animation = jsonData.animations[0];
  186. animation.channels.length.should.be.equal(1);
  187. animation.channels[0].sampler.should.be.equal(0);
  188. animation.channels[0].target.node.should.be.equal(0);
  189. animation.channels[0].target.path.should.be.equal('translation');
  190. animation.samplers.length.should.be.equal(1);
  191. animation.samplers[0].interpolation.should.be.equal('LINEAR');
  192. animation.samplers[0].input.should.be.equal(3);
  193. animation.samplers[0].output.should.be.equal(4);
  194. jsonData.animations[0].samplers.length.should.be.equal(1);
  195. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
  196. Object.keys(jsonData).length.should.be.equal(10);
  197. // positions, normals, indices, animation keyframe data, animation data
  198. jsonData.accessors.length.should.be.equal(5);
  199. // generator, version
  200. Object.keys(jsonData.asset).length.should.be.equal(2);
  201. jsonData.buffers.length.should.be.equal(1);
  202. // positions, normals, texture coords, indices, animation keyframe data, animation data
  203. jsonData.bufferViews.length.should.be.equal(6);
  204. jsonData.meshes.length.should.be.equal(1);
  205. jsonData.nodes.length.should.be.equal(1);
  206. jsonData.scenes.length.should.be.equal(1);
  207. jsonData.scene.should.be.equal(0);
  208. });
  209. });
  210. it('should serialize scale animation to glTF', () => {
  211. const scene = new BABYLON.Scene(subject);
  212. const box = BABYLON.Mesh.CreateBox('box', 1, scene);
  213. let keys: BABYLON.IAnimationKey[] = [];
  214. keys.push({
  215. frame: 0,
  216. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  217. });
  218. keys.push({
  219. frame: 20,
  220. value: BABYLON.Vector3.One()
  221. });
  222. keys.push({
  223. frame: 40,
  224. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  225. });
  226. let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'scaling', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  227. animationBoxT.setKeys(keys);
  228. box.animations.push(animationBoxT);
  229. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  230. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  231. const jsonData = JSON.parse(jsonString);
  232. jsonData.animations.length.should.be.equal(1);
  233. const animation = jsonData.animations[0];
  234. animation.channels.length.should.be.equal(1);
  235. animation.channels[0].sampler.should.be.equal(0);
  236. animation.channels[0].target.node.should.be.equal(0);
  237. animation.channels[0].target.path.should.be.equal('scale');
  238. animation.samplers.length.should.be.equal(1);
  239. animation.samplers[0].interpolation.should.be.equal('LINEAR');
  240. animation.samplers[0].input.should.be.equal(3);
  241. animation.samplers[0].output.should.be.equal(4);
  242. jsonData.animations[0].samplers.length.should.be.equal(1);
  243. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
  244. Object.keys(jsonData).length.should.be.equal(10);
  245. // positions, normals, indices, animation keyframe data, animation data
  246. jsonData.accessors.length.should.be.equal(5);
  247. // generator, version
  248. Object.keys(jsonData.asset).length.should.be.equal(2);
  249. jsonData.buffers.length.should.be.equal(1);
  250. // positions, normals, texture coords, indices, animation keyframe data, animation data
  251. jsonData.bufferViews.length.should.be.equal(6);
  252. jsonData.meshes.length.should.be.equal(1);
  253. jsonData.nodes.length.should.be.equal(1);
  254. jsonData.scenes.length.should.be.equal(1);
  255. jsonData.scene.should.be.equal(0);
  256. });
  257. });
  258. it('should serialize rotation quaternion animation to glTF', () => {
  259. const scene = new BABYLON.Scene(subject);
  260. const box = BABYLON.Mesh.CreateBox('box', 1, scene);
  261. let keys: BABYLON.IAnimationKey[] = [];
  262. keys.push({
  263. frame: 0,
  264. value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
  265. });
  266. keys.push({
  267. frame: 20,
  268. value: BABYLON.Quaternion.Identity()
  269. });
  270. keys.push({
  271. frame: 40,
  272. value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
  273. });
  274. let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'rotationQuaternion', 30, BABYLON.Animation.ANIMATIONTYPE_QUATERNION, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  275. animationBoxT.setKeys(keys);
  276. box.animations.push(animationBoxT);
  277. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  278. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  279. const jsonData = JSON.parse(jsonString);
  280. jsonData.animations.length.should.be.equal(1);
  281. const animation = jsonData.animations[0];
  282. animation.channels.length.should.be.equal(1);
  283. animation.channels[0].sampler.should.be.equal(0);
  284. animation.channels[0].target.node.should.be.equal(0);
  285. animation.channels[0].target.path.should.be.equal('rotation');
  286. animation.samplers.length.should.be.equal(1);
  287. animation.samplers[0].interpolation.should.be.equal('LINEAR');
  288. animation.samplers[0].input.should.be.equal(3);
  289. animation.samplers[0].output.should.be.equal(4);
  290. jsonData.animations[0].samplers.length.should.be.equal(1);
  291. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
  292. Object.keys(jsonData).length.should.be.equal(10);
  293. // positions, normals, indices, animation keyframe data, animation data
  294. jsonData.accessors.length.should.be.equal(5);
  295. // generator, version
  296. Object.keys(jsonData.asset).length.should.be.equal(2);
  297. jsonData.buffers.length.should.be.equal(1);
  298. // positions, normals, texture coords, indices, animation keyframe data, animation data
  299. jsonData.bufferViews.length.should.be.equal(6);
  300. jsonData.meshes.length.should.be.equal(1);
  301. jsonData.nodes.length.should.be.equal(1);
  302. jsonData.scenes.length.should.be.equal(1);
  303. jsonData.scene.should.be.equal(0);
  304. });
  305. });
  306. it('should serialize combined animations to glTF', () => {
  307. const scene = new BABYLON.Scene(subject);
  308. const box = BABYLON.Mesh.CreateBox('box', 1, scene);
  309. const rotationKeyFrames: BABYLON.IAnimationKey[] = [];
  310. rotationKeyFrames.push({
  311. frame: 0,
  312. value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
  313. });
  314. rotationKeyFrames.push({
  315. frame: 20,
  316. value: BABYLON.Quaternion.Identity()
  317. });
  318. rotationKeyFrames.push({
  319. frame: 40,
  320. value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
  321. });
  322. const scaleKeyFrames: BABYLON.IAnimationKey[] = [];
  323. scaleKeyFrames.push({
  324. frame: 0,
  325. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  326. });
  327. scaleKeyFrames.push({
  328. frame: 20,
  329. value: BABYLON.Vector3.One()
  330. });
  331. scaleKeyFrames.push({
  332. frame: 40,
  333. value: new BABYLON.Vector3(0.1, 0.1, 0.1)
  334. });
  335. let rotationAnimationBox = new BABYLON.Animation('boxAnimation_rotation', 'rotationQuaternion', 30, BABYLON.Animation.ANIMATIONTYPE_QUATERNION, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  336. rotationAnimationBox.setKeys(rotationKeyFrames);
  337. box.animations.push(rotationAnimationBox);
  338. let scaleAnimationBox = new BABYLON.Animation('boxAnimation_scale', 'scaling', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  339. scaleAnimationBox.setKeys(scaleKeyFrames);
  340. box.animations.push(scaleAnimationBox);
  341. return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
  342. const jsonString = glTFData.glTFFiles['test.gltf'] as string;
  343. const jsonData = JSON.parse(jsonString);
  344. jsonData.animations.length.should.be.equal(2);
  345. let animation = jsonData.animations[0];
  346. animation.channels.length.should.be.equal(1);
  347. animation.channels[0].sampler.should.be.equal(0);
  348. animation.channels[0].target.node.should.be.equal(0);
  349. animation.channels[0].target.path.should.be.equal('rotation');
  350. animation.samplers.length.should.be.equal(1);
  351. animation.samplers[0].interpolation.should.be.equal('LINEAR');
  352. animation.samplers[0].input.should.be.equal(3);
  353. animation.samplers[0].output.should.be.equal(4);
  354. animation = jsonData.animations[1];
  355. animation.channels[0].sampler.should.be.equal(0);
  356. animation.channels[0].target.node.should.be.equal(0);
  357. animation.channels[0].target.path.should.be.equal('scale');
  358. animation.samplers.length.should.be.equal(1);
  359. animation.samplers[0].interpolation.should.be.equal('LINEAR');
  360. animation.samplers[0].input.should.be.equal(5);
  361. animation.samplers[0].output.should.be.equal(6);
  362. // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
  363. Object.keys(jsonData).length.should.be.equal(10);
  364. // positions, normals, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data
  365. jsonData.accessors.length.should.be.equal(7);
  366. // generator, version
  367. Object.keys(jsonData.asset).length.should.be.equal(2);
  368. jsonData.buffers.length.should.be.equal(1);
  369. // positions, normals, texture coords, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data
  370. jsonData.bufferViews.length.should.be.equal(8);
  371. jsonData.meshes.length.should.be.equal(1);
  372. jsonData.nodes.length.should.be.equal(1);
  373. jsonData.scenes.length.should.be.equal(1);
  374. jsonData.scene.should.be.equal(0);
  375. });
  376. });
  377. });
  378. });