USDZExporter.tests.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import { USDZExporter } from '../../../../examples/jsm/exporters/USDZExporter.js';
  2. import { USDLoader } from '../../../../examples/jsm/loaders/USDLoader.js';
  3. import {
  4. unzipSync,
  5. strFromU8,
  6. } from '../../../../examples/jsm/libs/fflate.module.js';
  7. import {
  8. BoxGeometry,
  9. Mesh,
  10. MeshStandardMaterial,
  11. Scene,
  12. SphereGeometry,
  13. } from '../../../../src/Three.js';
  14. function isValidUSDA( usda ) {
  15. const header = usda.split( '\n' )[ 0 ];
  16. if ( header !== '#usda 1.0' ) return false;
  17. return true;
  18. }
  19. export default QUnit.module( 'Addons', () => {
  20. QUnit.module( 'Exporters', () => {
  21. QUnit.module( 'USDZExporter', () => {
  22. QUnit.test( 'methods', ( assert ) => {
  23. const exporter = new USDZExporter();
  24. assert.ok(
  25. exporter instanceof USDZExporter,
  26. 'USDZExporter can be instantiated'
  27. );
  28. assert.ok(
  29. typeof exporter.parseAsync === 'function',
  30. 'parseAsync method exists'
  31. );
  32. assert.ok( typeof exporter.parse === 'function', 'parse method exists' );
  33. assert.ok(
  34. typeof exporter.setTextureUtils === 'function',
  35. 'setTextureUtils method exists'
  36. );
  37. } );
  38. QUnit.test( 'export basic scene', async ( assert ) => {
  39. const exporter = new USDZExporter();
  40. const scene = new Scene();
  41. const geometry = new BoxGeometry( 1, 1, 1 );
  42. const material = new MeshStandardMaterial( {
  43. color: 0x00ff00,
  44. roughness: 0.5,
  45. metalness: 0.8,
  46. } );
  47. const mesh = new Mesh( geometry, material );
  48. mesh.name = 'box';
  49. scene.add( mesh );
  50. const result = await exporter.parseAsync( scene );
  51. assert.ok(
  52. result.buffer instanceof ArrayBuffer,
  53. 'Export returns a ArrayBuffer'
  54. );
  55. assert.ok(
  56. result.buffer.byteLength > 0,
  57. 'ArrayBuffer has non-zero length'
  58. );
  59. const unzipped = unzipSync( result );
  60. const fileNames = Object.keys( unzipped );
  61. const modelFileName = 'model.usda';
  62. assert.ok( fileNames.length > 0, 'ZIP contains at least one file' );
  63. assert.equal(
  64. fileNames[ 0 ],
  65. modelFileName,
  66. `First file is ${modelFileName}`
  67. );
  68. assert.ok(
  69. isValidUSDA( strFromU8( unzipped[ modelFileName ] ) ),
  70. `${modelFileName} has content`
  71. );
  72. } );
  73. QUnit.test( 'export scene with onlyVisible option', async ( assert ) => {
  74. const exporter = new USDZExporter( );
  75. const scene = new Scene();
  76. const geometry = new BoxGeometry( 1, 1, 1 );
  77. const material1 = new MeshStandardMaterial( { color: 0xff0000 } );
  78. const material2 = new MeshStandardMaterial( { color: 0x00ff00 } );
  79. const box1 = new Mesh( geometry, material1 );
  80. box1.name = 'box1';
  81. box1.position.set( - 1, 0, 0 );
  82. const box2 = new Mesh( geometry, material2 );
  83. box2.name = 'box2';
  84. box2.position.set( 1, 0, 0 );
  85. box2.visible = false;
  86. scene.add( box1 );
  87. scene.add( box2 );
  88. // onlyVisible = true
  89. const options = {
  90. onlyVisible: true,
  91. };
  92. const exportResult = await exporter.parseAsync( scene, options );
  93. assert.ok(
  94. exportResult.buffer instanceof ArrayBuffer,
  95. 'Export returns an ArrayBuffer'
  96. );
  97. assert.ok(
  98. exportResult.buffer.byteLength > 0,
  99. 'ArrayBuffer has non-zero length'
  100. );
  101. const unzipped = unzipSync( exportResult );
  102. const fileNames = Object.keys( unzipped );
  103. const modelFileName = 'model.usda';
  104. assert.ok( fileNames.includes( modelFileName ), `ZIP contains ${modelFileName}` );
  105. const usdaContent = strFromU8( unzipped[ modelFileName ] );
  106. assert.ok( isValidUSDA( usdaContent ), `${modelFileName} is valid USDA` );
  107. assert.ok( usdaContent.includes( 'box1' ), 'USDA contains box1' );
  108. assert.ok( ! usdaContent.includes( 'box2' ), 'USDA does not contain box2' );
  109. // onlyVisible = false
  110. options.onlyVisible = false;
  111. const exportResult2 = await exporter.parseAsync( scene, options );
  112. assert.ok(
  113. exportResult2.buffer instanceof ArrayBuffer,
  114. 'Export returns an ArrayBuffer'
  115. );
  116. assert.ok(
  117. exportResult2.buffer.byteLength > 0,
  118. 'ArrayBuffer has non-zero length'
  119. );
  120. const unzipped2 = unzipSync( exportResult2 );
  121. const fileNames2 = Object.keys( unzipped2 );
  122. assert.ok( fileNames2.includes( modelFileName ), `ZIP contains ${modelFileName}` );
  123. const usdaContent2 = strFromU8( unzipped2[ modelFileName ] );
  124. assert.ok( isValidUSDA( usdaContent2 ), `${modelFileName} is valid USDA` );
  125. assert.ok( usdaContent2.includes( 'box1' ), 'USDA contains box1' );
  126. assert.ok( usdaContent2.includes( 'box2' ), 'USDA contains box2' );
  127. } );
  128. QUnit.test( 'export and import', async ( assert ) => {
  129. const exporter = new USDZExporter();
  130. const originalScene = new Scene();
  131. const boxGeometry = new BoxGeometry( 1, 1, 1 );
  132. const boxMaterial = new MeshStandardMaterial( {
  133. color: 0x00ff00,
  134. roughness: 0.5,
  135. metalness: 0.8,
  136. } );
  137. const box = new Mesh( boxGeometry, boxMaterial );
  138. box.name = 'box1';
  139. box.position.set( 1, 2, 3 );
  140. box.scale.set( 0.5, 1.5, 2.0 );
  141. box.rotation.set( Math.PI / 4, Math.PI / 3, Math.PI / 2 );
  142. originalScene.add( box );
  143. const sphereGeometry = new SphereGeometry( 1, 8, 6 );
  144. const sphereMaterial = new MeshStandardMaterial( {
  145. color: 0x0000ff,
  146. roughness: 0.9,
  147. metalness: 0.1,
  148. } );
  149. const sphere = new Mesh( sphereGeometry, sphereMaterial );
  150. sphere.name = 'sphere1';
  151. sphere.position.set( 0, 0, 0 );
  152. originalScene.add( sphere );
  153. const meshes = [ box, sphere ];
  154. originalScene.updateMatrixWorld( true );
  155. const exportResult = await exporter.parseAsync( originalScene );
  156. assert.ok(
  157. exportResult.buffer instanceof ArrayBuffer,
  158. 'Export returns an ArrayBuffer'
  159. );
  160. const loader = new USDLoader();
  161. const importedScene = loader.parse( exportResult.buffer );
  162. assert.ok( importedScene, 'Loader successfully parses exported data' );
  163. for ( const mesh of meshes ) {
  164. const name = mesh.name;
  165. const importedMesh = importedScene.getObjectByName( name );
  166. assert.ok( importedMesh, 'Found imported mesh in scene' );
  167. assert.equal( importedMesh.name, name, 'Mesh name preserved' );
  168. const tolerance = 0.0000001;
  169. const vectorCloseTo = ( a, b, tolerance ) => {
  170. assert.closeTo( a.x, b.x, tolerance, 'X matches' );
  171. assert.closeTo( a.y, b.y, tolerance, 'Y matches' );
  172. assert.closeTo( a.z, b.z, tolerance, 'Z matches' );
  173. };
  174. vectorCloseTo( importedMesh.position, mesh.position, tolerance );
  175. vectorCloseTo( importedMesh.scale, mesh.scale, tolerance );
  176. vectorCloseTo( importedMesh.rotation, mesh.rotation, tolerance );
  177. assert.ok( importedMesh.geometry, 'Geometry exists' );
  178. assert.ok(
  179. importedMesh.geometry.attributes.position,
  180. 'Position attribute exists'
  181. );
  182. assert.ok( importedMesh.material, 'Material exists' );
  183. assert.ok( importedMesh.material.isMeshStandardMaterial, 'Material is a MeshStandardMaterial' );
  184. assert.closeTo( importedMesh.material.color.r, mesh.material.color.r, tolerance, 'Material color r matches' );
  185. assert.closeTo( importedMesh.material.color.g, mesh.material.color.g, tolerance, 'Material color g matches' );
  186. assert.closeTo( importedMesh.material.color.b, mesh.material.color.b, tolerance, 'Material color b matches' );
  187. assert.closeTo( importedMesh.material.roughness, mesh.material.roughness, tolerance, 'Material roughness matches' );
  188. assert.closeTo( importedMesh.material.metalness, mesh.material.metalness, tolerance, 'Material metalness matches' );
  189. }
  190. } );
  191. } );
  192. } );
  193. } );
粤ICP备19079148号