babylon.sceneLoader.tests.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /**
  2. * Describes the test suite.
  3. */
  4. describe('Babylon Scene Loader', function () {
  5. let subject: BABYLON.Engine;
  6. this.timeout(10000);
  7. /**
  8. * Loads the dependencies.
  9. */
  10. before(function (done) {
  11. this.timeout(180000);
  12. (BABYLONDEVTOOLS).Loader
  13. .useDist()
  14. .load(function () {
  15. // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
  16. BABYLON.PromisePolyfill.Apply(true);
  17. done();
  18. });
  19. });
  20. /**
  21. * Create a new engine subject before each test.
  22. */
  23. beforeEach(function () {
  24. subject = new BABYLON.NullEngine({
  25. renderHeight: 256,
  26. renderWidth: 256,
  27. textureSize: 256,
  28. deterministicLockstep: false,
  29. lockstepMaxSteps: 1
  30. });
  31. // Avoid creating normals in PBR materials.
  32. subject.getCaps().standardDerivatives = true;
  33. });
  34. /**
  35. * Integration tests for loading glTF assets.
  36. */
  37. describe('#glTF', () => {
  38. it('Load BoomBox', () => {
  39. const scene = new BABYLON.Scene(subject);
  40. return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(scene => {
  41. expect(scene.meshes.length, "scene.meshes.length").to.equal(2);
  42. expect(scene.materials.length, "scene.materials.length").to.equal(1);
  43. });
  44. });
  45. it('Load BoomBox GLB', () => {
  46. const scene = new BABYLON.Scene(subject);
  47. return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/", "BoomBox.glb", scene).then(scene => {
  48. expect(scene.meshes.length, "scene.meshes.length").to.equal(2);
  49. expect(scene.materials.length, "scene.materials.length").to.equal(1);
  50. });
  51. });
  52. it('Load BoomBox with ImportMesh', () => {
  53. const scene = new BABYLON.Scene(subject);
  54. return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(result => {
  55. expect(result.meshes.length, "meshes.length").to.equal(scene.meshes.length);
  56. expect(result.particleSystems.length, "particleSystems.length").to.equal(0);
  57. expect(result.skeletons.length, "skeletons.length").to.equal(0);
  58. expect(result.animationGroups.length, "animationGroups.length").to.equal(0);
  59. });
  60. });
  61. it('Load BoomBox with callbacks', () => {
  62. let parsedCount = 0;
  63. let meshCount = 0;
  64. let materialCount = 0;
  65. let textureCounts: { [name: string]: number } = {};
  66. let ready = false;
  67. const promises = new Array<Promise<void>>();
  68. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  69. loader.onParsed = data => {
  70. parsedCount++;
  71. };
  72. loader.onMeshLoaded = mesh => {
  73. meshCount++;
  74. };
  75. loader.onMaterialLoaded = material => {
  76. materialCount++;
  77. };
  78. loader.onTextureLoaded = texture => {
  79. textureCounts[texture.name] = textureCounts[texture.name] || 0;
  80. textureCounts[texture.name]++;
  81. };
  82. promises.push(loader.whenCompleteAsync().then(() => {
  83. expect(ready, "ready").to.be.true;
  84. }));
  85. }, undefined, undefined, undefined, true);
  86. const scene = new BABYLON.Scene(subject);
  87. promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
  88. ready = true;
  89. expect(parsedCount, "parsedCount").to.equal(1);
  90. expect(meshCount, "meshCount").to.equal(scene.meshes.length);
  91. expect(materialCount, "materialCount").to.equal(scene.materials.length);
  92. const expectedTextureLoadCounts = {
  93. "baseColor": 1,
  94. "occlusionRoughnessMetallic": 2,
  95. "normal": 1,
  96. "emissive": 1
  97. };
  98. expect(Object.keys(textureCounts), "Object.keys(textureCounts)").to.have.lengthOf(Object.keys(expectedTextureLoadCounts).length);
  99. for (const textureName in expectedTextureLoadCounts) {
  100. expect(textureCounts, "textureCounts").to.have.property(textureName, expectedTextureLoadCounts[textureName]);
  101. }
  102. }));
  103. return Promise.all(promises);
  104. });
  105. it('Load BoomBox with dispose', () => {
  106. let ready = false;
  107. let disposed = false;
  108. const promises = new Array<Promise<void>>();
  109. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  110. loader.onDispose = () => {
  111. disposed = true;
  112. };
  113. promises.push(BABYLON.Tools.DelayAsync(50).then(() => {
  114. loader.dispose();
  115. expect(ready, "ready").to.be.false;
  116. expect(disposed, "disposed").to.be.true;
  117. }));
  118. }, undefined, undefined, undefined, true);
  119. const scene = new BABYLON.Scene(subject);
  120. promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox2.gltf", scene).then(() => {
  121. ready = true;
  122. }));
  123. return Promise.race(promises);
  124. });
  125. it('Load BoomBox with rootMesh.isEnabled check', () => {
  126. const scene = new BABYLON.Scene(subject);
  127. let rootMesh: BABYLON.AbstractMesh;
  128. subject.runRenderLoop(() => {
  129. if (!rootMesh) {
  130. for (const mesh of scene.meshes) {
  131. if (!mesh.parent) {
  132. rootMesh = mesh;
  133. break;
  134. }
  135. }
  136. }
  137. if (rootMesh) {
  138. expect(rootMesh.isEnabled(), "rootMesh.isEnabled").to.be.false;
  139. }
  140. });
  141. return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(scene => {
  142. expect(rootMesh.isEnabled(), "rootMesh.isEnabled").to.be.true;
  143. subject.stopRenderLoop();
  144. });
  145. });
  146. it('Load CompileMaterials', () => {
  147. const scene = new BABYLON.Scene(subject);
  148. const promises = new Array<Promise<void>>();
  149. let createShaderProgramSpy: sinon.SinonSpy;
  150. subject.runRenderLoop(() => {
  151. for (const mesh of scene.meshes) {
  152. if (mesh.material && mesh.isEnabled()) {
  153. expect(mesh.material.isReady(mesh), "mesh material is ready").to.be.true;
  154. }
  155. }
  156. });
  157. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  158. loader.compileMaterials = true;
  159. promises.push(loader.whenCompleteAsync().then(() => {
  160. const called = createShaderProgramSpy.called;
  161. createShaderProgramSpy.restore();
  162. expect(called, "createShaderProgramCalled").to.be.false;
  163. }));
  164. }, undefined, undefined, undefined, true);
  165. promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/CompileMaterials/", "Test.gltf", scene).then(() => {
  166. createShaderProgramSpy = sinon.spy(subject, "createShaderProgram");
  167. }));
  168. return Promise.all(promises);
  169. });
  170. it('Load BrainStem with compileMaterials', () => {
  171. const scene = new BABYLON.Scene(subject);
  172. const promises = new Array<Promise<void>>();
  173. let createShaderProgramSpy: sinon.SinonSpy;
  174. subject.runRenderLoop(() => {
  175. for (const mesh of scene.meshes) {
  176. if (mesh.material && mesh.isEnabled()) {
  177. expect(mesh.material.isReady(mesh), "mesh material is ready").to.be.true;
  178. }
  179. }
  180. });
  181. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  182. loader.compileMaterials = true;
  183. promises.push(loader.whenCompleteAsync().then(() => {
  184. const called = createShaderProgramSpy.called;
  185. createShaderProgramSpy.restore();
  186. expect(called, "createShaderProgramCalled").to.be.false;
  187. }));
  188. }, undefined, undefined, undefined, true);
  189. promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BrainStem/", "BrainStem.gltf", scene).then(() => {
  190. createShaderProgramSpy = sinon.spy(subject, "createShaderProgram");
  191. }));
  192. return Promise.all(promises);
  193. });
  194. it('Load Alien', () => {
  195. const scene = new BABYLON.Scene(subject);
  196. return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/Alien/", "Alien.gltf", scene).then(result => {
  197. const skeletonsMapping = {
  198. "AlienHead": "skeleton0",
  199. "Collar": "skeleton1",
  200. "LeftEye": "skeleton2",
  201. "RightEye": "skeleton3",
  202. "CollarClasp": "skeleton1",
  203. "Shirt": "skeleton1",
  204. "ShirtPlate": "skeleton1",
  205. "Teeth": "skeleton1",
  206. };
  207. expect(scene.skeletons, "scene.skeletons").to.have.lengthOf(4);
  208. expect(result.skeletons, "skeletons").to.have.lengthOf(4);
  209. for (const meshName in skeletonsMapping) {
  210. const skeletonName = skeletonsMapping[meshName];
  211. expect(scene.getMeshByName(meshName).skeleton.name, `skeleton name of mesh '${meshName}'`).to.equal(skeletonName);
  212. }
  213. const alienHeadMesh = scene.getMeshByName("AlienHead") as BABYLON.Mesh;
  214. expect(alienHeadMesh.morphTargetManager.numTargets, "alienHeadMesh.morphTargetManager.numTargets").to.equal(2);
  215. expect(scene.animationGroups, "scene.animationGroups").to.have.lengthOf(1);
  216. expect(result.animationGroups, "animationGroups").to.have.lengthOf(1);
  217. const animationGroup = result.animationGroups[0];
  218. expect(animationGroup.name, "animationGroup.name").to.equal("TwoTargetBlend");
  219. expect(animationGroup.targetedAnimations, "animationGroup.targetedAnimations").to.have.lengthOf(7);
  220. const influenceAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "influence");
  221. expect(influenceAnimations, "influenceAnimations").to.have.lengthOf(2);
  222. const rotationAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "rotationQuaternion");
  223. expect(rotationAnimations, "rotationAnimations").to.have.lengthOf(4);
  224. const positionAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "position");
  225. expect(positionAnimations, "positionAnimations").to.have.lengthOf(1);
  226. });
  227. });
  228. it('Load TwoQuads with LODs', () => {
  229. const scene = new BABYLON.Scene(subject);
  230. const promises = new Array<Promise<void>>();
  231. subject.runRenderLoop(() => {
  232. for (const mesh of scene.meshes) {
  233. if (mesh.material && mesh.isEnabled()) {
  234. expect(mesh.material.isReady(mesh), "mesh material is ready").to.be.true;
  235. }
  236. }
  237. });
  238. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  239. loader.compileMaterials = true;
  240. promises.push(loader.whenCompleteAsync().then(() => {
  241. const meshes = [
  242. scene.getMeshByName("node0"),
  243. scene.getMeshByName("node1")
  244. ];
  245. expect(meshes[0].material.name, "Material for node 0").to.equal("LOD0");
  246. expect(meshes[1].material.name, "Material for node 1").to.equal("LOD0");
  247. expect(scene.materials, "scene.materials").to.have.lengthOf(1);
  248. const materials = [
  249. scene.getMaterialByName("LOD0")
  250. ];
  251. expect(materials[0].isReady(meshes[0]), "Material of LOD 0 is ready for node 0").to.be.true;
  252. expect(materials[0].isReady(meshes[1]), "Material of LOD 0 is ready for node 1").to.be.true;
  253. }));
  254. }, undefined, undefined, undefined, true);
  255. promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
  256. const meshes = [
  257. scene.getMeshByName("node0"),
  258. scene.getMeshByName("node1")
  259. ];
  260. expect(meshes[0].material.name, "Material for node 0").to.equal("LOD2");
  261. expect(meshes[1].material.name, "Material for node 1").to.equal("LOD2");
  262. expect(scene.materials, "scene.materials").to.have.lengthOf(3);
  263. const materials = [
  264. scene.getMaterialByName("LOD0"),
  265. scene.getMaterialByName("LOD1"),
  266. scene.getMaterialByName("LOD2")
  267. ];
  268. expect(materials[0].isReady(meshes[0]), "Material of LOD 0 is ready for node 0").to.be.false;
  269. expect(materials[0].isReady(meshes[1]), "Material of LOD 0 is ready for node 1").to.be.false;
  270. expect(materials[1].isReady(meshes[0]), "Material of LOD 1 is ready for node 0").to.be.false;
  271. expect(materials[1].isReady(meshes[1]), "Material of LOD 1 is ready for node 1").to.be.false;
  272. expect(materials[2].isReady(meshes[0]), "Material of LOD 2 is ready for node 0").to.be.true;
  273. expect(materials[2].isReady(meshes[1]), "Material of LOD 2 is ready for node 1").to.be.true;
  274. }));
  275. return Promise.all(promises);
  276. });
  277. it('Load MultiPrimitive', () => {
  278. const scene = new BABYLON.Scene(subject);
  279. return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then(result => {
  280. expect(result.meshes, "meshes").to.have.lengthOf(4);
  281. const node = scene.getMeshByName("node");
  282. expect(node, "node").to.exist;
  283. expect(node, "node").to.be.an.instanceof(BABYLON.Mesh);
  284. const mesh = node as BABYLON.Mesh;
  285. expect(mesh.geometry).to.not.exist;
  286. expect(mesh.material).to.not.exist;
  287. expect(mesh.getChildren(), "mesh children").to.have.lengthOf(2);
  288. for (const childNode of mesh.getChildren()) {
  289. expect(childNode, "child node").to.be.an.instanceof(BABYLON.Mesh);
  290. const childMesh = childNode as BABYLON.Mesh;
  291. expect(childMesh.geometry).to.exist;
  292. expect(childMesh.material).to.exist;
  293. }
  294. });
  295. });
  296. it('Load BrainStem', () => {
  297. const scene = new BABYLON.Scene(subject);
  298. return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/BrainStem/", "BrainStem.gltf", scene).then(result => {
  299. expect(result.skeletons, "skeletons").to.have.lengthOf(1);
  300. const node1 = scene.getMeshByName("node1");
  301. for (const childMesh of node1.getChildMeshes()) {
  302. expect(childMesh.skeleton, "mesh skeleton").to.exist;
  303. expect(childMesh.skeleton.name, "mesh skeleton name").to.equal(result.skeletons[0].name);
  304. }
  305. });
  306. });
  307. it('Load BoomBox with transparencyAsCoverage', () => {
  308. const scene = new BABYLON.Scene(subject);
  309. const promises = new Array<Promise<any>>();
  310. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  311. var specularOverAlpha = false;
  312. var radianceOverAlpha = false;
  313. loader.transparencyAsCoverage = true;
  314. loader.onMaterialLoaded = material => {
  315. specularOverAlpha = specularOverAlpha || (material as BABYLON.PBRMaterial).useSpecularOverAlpha;
  316. radianceOverAlpha = radianceOverAlpha || (material as BABYLON.PBRMaterial).useRadianceOverAlpha;
  317. };
  318. promises.push(loader.whenCompleteAsync().then(() => {
  319. expect(specularOverAlpha, "specularOverAlpha").to.be.false;
  320. expect(radianceOverAlpha, "radianceOverAlpha").to.be.false;
  321. }));
  322. }, undefined, undefined, undefined, true);
  323. promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene));
  324. return Promise.all(promises);
  325. });
  326. it('Load BoomBox without transparencyAsCoverage', () => {
  327. const scene = new BABYLON.Scene(subject);
  328. const promises = new Array<Promise<any>>();
  329. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  330. var specularOverAlpha = true;
  331. var radianceOverAlpha = true;
  332. loader.transparencyAsCoverage = false;
  333. loader.onMaterialLoaded = material => {
  334. specularOverAlpha = specularOverAlpha && (material as BABYLON.PBRMaterial).useSpecularOverAlpha;
  335. radianceOverAlpha = radianceOverAlpha && (material as BABYLON.PBRMaterial).useRadianceOverAlpha;
  336. };
  337. promises.push(loader.whenCompleteAsync().then(() => {
  338. expect(specularOverAlpha, "specularOverAlpha").to.be.true;
  339. expect(radianceOverAlpha, "radianceOverAlpha").to.be.true;
  340. }));
  341. }, undefined, undefined, undefined, true);
  342. promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene));
  343. return Promise.all(promises);
  344. });
  345. it('Load BoomBox twice and check texture instancing', () => {
  346. const scene = new BABYLON.Scene(subject);
  347. return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
  348. const createTextureSpy = sinon.spy(subject, "createTexture");
  349. return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
  350. const called = createTextureSpy.called;
  351. createTextureSpy.restore();
  352. expect(called, "createTextureSpyCalled").to.be.false;
  353. });
  354. });
  355. });
  356. // TODO: test animation group callback
  357. // TODO: test material instancing
  358. // TODO: test ImportMesh with specific node name
  359. // TODO: test KHR_materials_pbrSpecularGlossiness
  360. // TODO: test KHR_lights
  361. });
  362. describe('#AssetContainer', () => {
  363. it('should be loaded from BoomBox GLTF', () => {
  364. var scene = new BABYLON.Scene(subject);
  365. return BABYLON.SceneLoader.LoadAssetContainerAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(container => {
  366. expect(container.meshes.length).to.eq(2);
  367. });
  368. });
  369. it('should be adding and removing objects from scene', () => {
  370. // Create a scene with some assets
  371. var scene = new BABYLON.Scene(subject);
  372. var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
  373. var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
  374. var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
  375. var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
  376. // Move all the assets from the scene into a container
  377. var container = new BABYLON.AssetContainer(scene);
  378. var keepAssets = new BABYLON.KeepAssets();
  379. keepAssets.cameras.push(camera);
  380. container.moveAllFromScene(keepAssets);
  381. expect(scene.cameras.length).to.eq(1);
  382. expect(scene.meshes.length).to.eq(0);
  383. expect(scene.lights.length).to.eq(0);
  384. expect(container.cameras.length).to.eq(0);
  385. expect(container.meshes.length).to.eq(2);
  386. expect(container.lights.length).to.eq(1);
  387. // Add them back and then remove again
  388. container.addAllToScene();
  389. expect(scene.cameras.length).to.eq(1);
  390. expect(scene.meshes.length).to.eq(2);
  391. expect(scene.lights.length).to.eq(1);
  392. container.removeAllFromScene();
  393. expect(scene.cameras.length).to.eq(1);
  394. expect(scene.meshes.length).to.eq(0);
  395. expect(scene.lights.length).to.eq(0);
  396. });
  397. });
  398. });