Object3D.tests.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /* global QUnit */
  2. import { Object3D } from '../../../../src/core/Object3D';
  3. import { Vector3 } from '../../../../src/math/Vector3';
  4. import { Euler } from '../../../../src/math/Euler';
  5. import { Quaternion } from '../../../../src/math/Quaternion';
  6. import { Matrix4 } from '../../../../src/math/Matrix4';
  7. import {
  8. x,
  9. y,
  10. z,
  11. w,
  12. eps
  13. } from '../math/Constants.tests';
  14. export default QUnit.module( 'Core', () => {
  15. QUnit.module( 'Object3D', () => {
  16. var RadToDeg = 180 / Math.PI;
  17. var eulerEquals = function ( a, b, tolerance ) {
  18. tolerance = tolerance || 0.0001;
  19. if ( a.order != b.order ) {
  20. return false;
  21. }
  22. return (
  23. Math.abs( a.x - b.x ) <= tolerance &&
  24. Math.abs( a.y - b.y ) <= tolerance &&
  25. Math.abs( a.z - b.z ) <= tolerance
  26. );
  27. };
  28. // INHERITANCE
  29. QUnit.todo( "Extending", ( assert ) => {
  30. assert.ok( false, "everything's gonna be alright" );
  31. } );
  32. // INSTANCING
  33. QUnit.todo( "Instancing", ( assert ) => {
  34. assert.ok( false, "everything's gonna be alright" );
  35. } );
  36. // STATIC STUFF
  37. QUnit.todo( "DefaultUp", ( assert ) => {
  38. assert.ok( false, "everything's gonna be alright" );
  39. } );
  40. QUnit.todo( "DefaultMatrixAutoUpdate", ( assert ) => {
  41. assert.ok( false, "everything's gonna be alright" );
  42. } );
  43. // PUBLIC STUFF
  44. QUnit.todo( "isObject3D", ( assert ) => {
  45. assert.ok( false, "everything's gonna be alright" );
  46. } );
  47. QUnit.todo( "onBeforeRender", ( assert ) => {
  48. assert.ok( false, "everything's gonna be alright" );
  49. } );
  50. QUnit.todo( "onAfterRender", ( assert ) => {
  51. assert.ok( false, "everything's gonna be alright" );
  52. } );
  53. QUnit.test( "applyMatrix4", ( assert ) => {
  54. var a = new Object3D();
  55. var m = new Matrix4();
  56. var expectedPos = new Vector3( x, y, z );
  57. var expectedQuat = new Quaternion( 0.5 * Math.sqrt( 2 ), 0, 0, 0.5 * Math.sqrt( 2 ) );
  58. m.makeRotationX( Math.PI / 2 );
  59. m.setPosition( new Vector3( x, y, z ) );
  60. a.applyMatrix4( m );
  61. assert.deepEqual( a.position, expectedPos, "Position has the expected values" );
  62. assert.ok(
  63. Math.abs( a.quaternion.x - expectedQuat.x ) <= eps &&
  64. Math.abs( a.quaternion.y - expectedQuat.y ) <= eps &&
  65. Math.abs( a.quaternion.z - expectedQuat.z ) <= eps,
  66. "Quaternion has the expected values"
  67. );
  68. } );
  69. QUnit.test( "applyQuaternion", ( assert ) => {
  70. var a = new Object3D();
  71. var sqrt = 0.5 * Math.sqrt( 2 );
  72. var quat = new Quaternion( 0, sqrt, 0, sqrt );
  73. var expected = new Quaternion( sqrt / 2, sqrt / 2, 0, 0 );
  74. a.quaternion.set( 0.25, 0.25, 0.25, 0.25 );
  75. a.applyQuaternion( quat );
  76. assert.ok(
  77. Math.abs( a.quaternion.x - expected.x ) <= eps &&
  78. Math.abs( a.quaternion.y - expected.y ) <= eps &&
  79. Math.abs( a.quaternion.z - expected.z ) <= eps,
  80. "Quaternion has the expected values"
  81. );
  82. } );
  83. QUnit.test( "setRotationFromAxisAngle", ( assert ) => {
  84. var a = new Object3D();
  85. var axis = new Vector3( 0, 1, 0 );
  86. var angle = Math.PI;
  87. var expected = new Euler( - Math.PI, 0, - Math.PI );
  88. var euler = new Euler();
  89. a.setRotationFromAxisAngle( axis, angle );
  90. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  91. assert.ok( eulerEquals( euler, expected ), "Correct values after rotation" );
  92. axis.set( 1, 0, 0 );
  93. var angle = 0;
  94. expected.set( 0, 0, 0 );
  95. a.setRotationFromAxisAngle( axis, angle );
  96. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  97. assert.ok( eulerEquals( euler, expected ), "Correct values after zeroing" );
  98. } );
  99. QUnit.test( "setRotationFromEuler", ( assert ) => {
  100. var a = new Object3D();
  101. var rotation = new Euler( ( 45 / RadToDeg ), 0, Math.PI );
  102. var expected = rotation.clone(); // bit obvious
  103. var euler = new Euler();
  104. a.setRotationFromEuler( rotation );
  105. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  106. assert.ok( eulerEquals( euler, expected ), "Correct values after rotation" );
  107. } );
  108. QUnit.test( "setRotationFromMatrix", ( assert ) => {
  109. var a = new Object3D();
  110. var m = new Matrix4();
  111. var eye = new Vector3( 0, 0, 0 );
  112. var target = new Vector3( 0, 1, - 1 );
  113. var up = new Vector3( 0, 1, 0 );
  114. var euler = new Euler();
  115. m.lookAt( eye, target, up );
  116. a.setRotationFromMatrix( m );
  117. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  118. assert.numEqual( euler.x * RadToDeg, 45, "Correct rotation angle" );
  119. } );
  120. QUnit.test( "setRotationFromQuaternion", ( assert ) => {
  121. var a = new Object3D();
  122. var rotation = new Quaternion().setFromEuler( new Euler( Math.PI, 0, - Math.PI ) );
  123. var euler = new Euler();
  124. a.setRotationFromQuaternion( rotation );
  125. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  126. assert.ok( eulerEquals( euler, new Euler( Math.PI, 0, - Math.PI ) ), "Correct values after rotation" );
  127. } );
  128. QUnit.todo( "rotateOnAxis", ( assert ) => {
  129. assert.ok( false, "everything's gonna be alright" );
  130. } );
  131. QUnit.todo( "rotateOnWorldAxis", ( assert ) => {
  132. assert.ok( false, "everything's gonna be alright" );
  133. } );
  134. QUnit.test( "rotateX", ( assert ) => {
  135. var obj = new Object3D();
  136. var angleInRad = 1.562;
  137. obj.rotateX( angleInRad );
  138. assert.numEqual( obj.rotation.x, angleInRad, "x is equal" );
  139. } );
  140. QUnit.test( "rotateY", ( assert ) => {
  141. var obj = new Object3D();
  142. var angleInRad = - 0.346;
  143. obj.rotateY( angleInRad );
  144. assert.numEqual( obj.rotation.y, angleInRad, "y is equal" );
  145. } );
  146. QUnit.test( "rotateZ", ( assert ) => {
  147. var obj = new Object3D();
  148. var angleInRad = 1;
  149. obj.rotateZ( angleInRad );
  150. assert.numEqual( obj.rotation.z, angleInRad, "z is equal" );
  151. } );
  152. QUnit.test( "translateOnAxis", ( assert ) => {
  153. var obj = new Object3D();
  154. obj.translateOnAxis( new Vector3( 1, 0, 0 ), 1 );
  155. obj.translateOnAxis( new Vector3( 0, 1, 0 ), 1.23 );
  156. obj.translateOnAxis( new Vector3( 0, 0, 1 ), - 4.56 );
  157. assert.propEqual( obj.position, {
  158. x: 1,
  159. y: 1.23,
  160. z: - 4.56
  161. } );
  162. } );
  163. QUnit.test( "translateX", ( assert ) => {
  164. var obj = new Object3D();
  165. obj.translateX( 1.234 );
  166. assert.numEqual( obj.position.x, 1.234, "x is equal" );
  167. } );
  168. QUnit.test( "translateY", ( assert ) => {
  169. var obj = new Object3D();
  170. obj.translateY( 1.234 );
  171. assert.numEqual( obj.position.y, 1.234, "y is equal" );
  172. } );
  173. QUnit.test( "translateZ", ( assert ) => {
  174. var obj = new Object3D();
  175. obj.translateZ( 1.234 );
  176. assert.numEqual( obj.position.z, 1.234, "z is equal" );
  177. } );
  178. QUnit.todo( "localToWorld", ( assert ) => {
  179. assert.ok( false, "everything's gonna be alright" );
  180. } );
  181. QUnit.todo( "worldToLocal", ( assert ) => {
  182. assert.ok( false, "everything's gonna be alright" );
  183. } );
  184. QUnit.test( "lookAt", ( assert ) => {
  185. var obj = new Object3D();
  186. obj.lookAt( new Vector3( 0, - 1, 1 ) );
  187. assert.numEqual( obj.rotation.x * RadToDeg, 45, "x is equal" );
  188. } );
  189. QUnit.test( "add/remove/clear", ( assert ) => {
  190. var a = new Object3D();
  191. var child1 = new Object3D();
  192. var child2 = new Object3D();
  193. assert.strictEqual( a.children.length, 0, "Starts with no children" );
  194. a.add( child1 );
  195. assert.strictEqual( a.children.length, 1, "The first child was added" );
  196. assert.strictEqual( a.children[ 0 ], child1, "It's the right one" );
  197. a.add( child2 );
  198. assert.strictEqual( a.children.length, 2, "The second child was added" );
  199. assert.strictEqual( a.children[ 1 ], child2, "It's the right one" );
  200. assert.strictEqual( a.children[ 0 ], child1, "The first one is still there" );
  201. a.remove( child1 );
  202. assert.strictEqual( a.children.length, 1, "The first child was removed" );
  203. assert.strictEqual( a.children[ 0 ], child2, "The second one is still there" );
  204. a.add( child1 );
  205. a.remove( child1, child2 );
  206. assert.strictEqual( a.children.length, 0, "Both children were removed at once" );
  207. child1.add( child2 );
  208. assert.strictEqual( child1.children.length, 1, "The second child was added to the first one" );
  209. a.add( child2 );
  210. assert.strictEqual( a.children.length, 1, "The second one was added to the parent (no remove)" );
  211. assert.strictEqual( a.children[ 0 ], child2, "The second one is now the parent's child again" );
  212. assert.strictEqual( child1.children.length, 0, "The first one no longer has any children" );
  213. a.add( child1 );
  214. assert.strictEqual( a.children.length, 2, "The first child was added to the parent" );
  215. a.clear();
  216. assert.strictEqual( a.children.length, 0, "All childrens were removed" );
  217. assert.strictEqual( child1.parent, null, "First child has no parent" );
  218. assert.strictEqual( child2.parent, null, "Second child has no parent" );
  219. } );
  220. QUnit.test( "getObjectById/getObjectByName/getObjectByProperty", ( assert ) => {
  221. var parent = new Object3D();
  222. var childName = new Object3D();
  223. var childId = new Object3D(); // id = parent.id + 2
  224. var childNothing = new Object3D();
  225. parent.prop = true;
  226. childName.name = "foo";
  227. parent.add( childName, childId, childNothing );
  228. assert.strictEqual( parent.getObjectByProperty( 'prop', true ), parent, "Get parent by its own property" );
  229. assert.strictEqual( parent.getObjectByName( "foo" ), childName, "Get child by name" );
  230. assert.strictEqual( parent.getObjectById( parent.id + 2 ), childId, "Get child by Id" );
  231. assert.strictEqual(
  232. parent.getObjectByProperty( 'no-property', 'no-value' ), undefined,
  233. "Unknown property results in undefined"
  234. );
  235. } );
  236. QUnit.test( "getWorldPosition", ( assert ) => {
  237. var a = new Object3D();
  238. var b = new Object3D();
  239. var expectedSingle = new Vector3( x, y, z );
  240. var expectedParent = new Vector3( x, y, 0 );
  241. var expectedChild = new Vector3( x, y, 7 + ( z - z ) );
  242. var position = new Vector3();
  243. a.translateX( x );
  244. a.translateY( y );
  245. a.translateZ( z );
  246. assert.deepEqual( a.getWorldPosition( position ), expectedSingle, "WorldPosition as expected for single object" );
  247. // translate child and then parent
  248. b.translateZ( 7 );
  249. a.add( b );
  250. a.translateZ( - z );
  251. assert.deepEqual( a.getWorldPosition( position ), expectedParent, "WorldPosition as expected for parent" );
  252. assert.deepEqual( b.getWorldPosition( position ), expectedChild, "WorldPosition as expected for child" );
  253. } );
  254. QUnit.todo( "getWorldQuaternion", ( assert ) => {
  255. assert.ok( false, "everything's gonna be alright" );
  256. } );
  257. QUnit.test( "getWorldScale", ( assert ) => {
  258. var a = new Object3D();
  259. var m = new Matrix4().makeScale( x, y, z );
  260. var expected = new Vector3( x, y, z );
  261. a.applyMatrix4( m );
  262. assert.deepEqual( a.getWorldScale( new Vector3() ), expected, "WorldScale as expected" );
  263. } );
  264. QUnit.test( "getWorldDirection", ( assert ) => {
  265. var a = new Object3D();
  266. var expected = new Vector3( 0, - 0.5 * Math.sqrt( 2 ), 0.5 * Math.sqrt( 2 ) );
  267. var direction = new Vector3();
  268. a.lookAt( new Vector3( 0, - 1, 1 ) );
  269. a.getWorldDirection( direction );
  270. assert.ok(
  271. Math.abs( direction.x - expected.x ) <= eps &&
  272. Math.abs( direction.y - expected.y ) <= eps &&
  273. Math.abs( direction.z - expected.z ) <= eps,
  274. "Direction has the expected values"
  275. );
  276. } );
  277. QUnit.test( "localTransformVariableInstantiation", ( assert ) => {
  278. var a = new Object3D();
  279. var b = new Object3D();
  280. var c = new Object3D();
  281. var d = new Object3D();
  282. a.getWorldDirection( new Vector3() );
  283. a.lookAt( new Vector3( 0, - 1, 1 ) );
  284. assert.ok( true, "Calling lookAt after getWorldDirection does not create errors" );
  285. b.getWorldPosition( new Vector3() );
  286. b.lookAt( new Vector3( 0, - 1, 1 ) );
  287. assert.ok( true, "Calling lookAt after getWorldPosition does not create errors" );
  288. c.getWorldQuaternion( new Quaternion() );
  289. c.lookAt( new Vector3( 0, - 1, 1 ) );
  290. assert.ok( true, "Calling lookAt after getWorldQuaternion does not create errors" );
  291. d.getWorldScale( new Vector3() );
  292. d.lookAt( new Vector3( 0, - 1, 1 ) );
  293. assert.ok( true, "Calling lookAt after getWorldScale does not create errors" );
  294. } );
  295. QUnit.todo( "raycast", ( assert ) => {
  296. assert.ok( false, "everything's gonna be alright" );
  297. } );
  298. QUnit.test( "traverse/traverseVisible/traverseAncestors", ( assert ) => {
  299. var a = new Object3D();
  300. var b = new Object3D();
  301. var c = new Object3D();
  302. var d = new Object3D();
  303. var names = [];
  304. var expectedNormal = [ "parent", "child", "childchild 1", "childchild 2" ];
  305. var expectedVisible = [ "parent", "child", "childchild 2" ];
  306. var expectedAncestors = [ "child", "parent" ];
  307. a.name = "parent";
  308. b.name = "child";
  309. c.name = "childchild 1";
  310. c.visible = false;
  311. d.name = "childchild 2";
  312. b.add( c );
  313. b.add( d );
  314. a.add( b );
  315. a.traverse( function ( obj ) {
  316. names.push( obj.name );
  317. } );
  318. assert.deepEqual( names, expectedNormal, "Traversed objects in expected order" );
  319. var names = [];
  320. a.traverseVisible( function ( obj ) {
  321. names.push( obj.name );
  322. } );
  323. assert.deepEqual( names, expectedVisible, "Traversed visible objects in expected order" );
  324. var names = [];
  325. c.traverseAncestors( function ( obj ) {
  326. names.push( obj.name );
  327. } );
  328. assert.deepEqual( names, expectedAncestors, "Traversed ancestors in expected order" );
  329. } );
  330. QUnit.test( "updateMatrix", ( assert ) => {
  331. const a = new Object3D();
  332. a.position.set( 2, 3, 4 );
  333. a.quaternion.set( 5, 6, 7, 8 );
  334. a.scale.set( 9, 10, 11 );
  335. assert.deepEqual( a.matrix.elements, [
  336. 1, 0, 0, 0,
  337. 0, 1, 0, 0,
  338. 0, 0, 1, 0,
  339. 0, 0, 0, 1
  340. ], "Updating position, quaternion, or scale has no effect to matrix until calling updateMatrix()" );
  341. a.updateMatrix();
  342. assert.deepEqual( a.matrix.elements, [
  343. -1521, 1548, -234, 0,
  344. -520, -1470, 1640, 0,
  345. 1826, 44, -1331, 0,
  346. 2, 3, 4, 1
  347. ], "matrix is calculated from position, quaternion, and scale" );
  348. assert.equal( a.matrixWorldNeedsUpdate, true, "The flag indicating world matrix needs to be updated should be true" );
  349. } );
  350. QUnit.todo( "updateMatrixWorld", ( assert ) => {
  351. assert.ok( false, "everything's gonna be alright" );
  352. } );
  353. QUnit.test( "toJSON", ( assert ) => {
  354. var a = new Object3D();
  355. var child = new Object3D();
  356. var childChild = new Object3D();
  357. a.name = "a's name";
  358. a.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  359. a.visible = false;
  360. a.castShadow = true;
  361. a.receiveShadow = true;
  362. a.userData[ "foo" ] = "bar";
  363. child.uuid = "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD";
  364. childChild.uuid = "B43854B3-E970-4E85-BD41-AAF8D7BFA189";
  365. child.add( childChild );
  366. a.add( child );
  367. var gold = {
  368. "metadata": {
  369. "version": 4.5,
  370. "type": "Object",
  371. "generator": "Object3D.toJSON"
  372. },
  373. "object": {
  374. "uuid": "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2",
  375. "type": "Object3D",
  376. "name": "a's name",
  377. "castShadow": true,
  378. "receiveShadow": true,
  379. "visible": false,
  380. "userData": { "foo": "bar" },
  381. "layers": 1,
  382. "matrix": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  383. "children": [
  384. {
  385. "uuid": "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD",
  386. "type": "Object3D",
  387. "layers": 1,
  388. "matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  389. "children": [
  390. {
  391. "uuid": "B43854B3-E970-4E85-BD41-AAF8D7BFA189",
  392. "type": "Object3D",
  393. "layers": 1,
  394. "matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
  395. }
  396. ]
  397. }
  398. ]
  399. }
  400. };
  401. // hacks
  402. var out = a.toJSON();
  403. out.object.uuid = "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2";
  404. assert.deepEqual( out, gold, "JSON is as expected" );
  405. } );
  406. QUnit.test( "clone", ( assert ) => {
  407. var a;
  408. var b = new Object3D();
  409. assert.strictEqual( a, undefined, "Undefined pre-clone()" );
  410. a = b.clone();
  411. assert.notStrictEqual( a, b, "Defined but seperate instances post-clone()" );
  412. a.uuid = b.uuid;
  413. assert.deepEqual( a, b, "But identical properties" );
  414. } );
  415. QUnit.test( "copy", ( assert ) => {
  416. var a = new Object3D();
  417. var b = new Object3D();
  418. var child = new Object3D();
  419. var childChild = new Object3D();
  420. a.name = "original";
  421. b.name = "to-be-copied";
  422. b.position.set( x, y, z );
  423. b.quaternion.set( x, y, z, w );
  424. b.scale.set( 2, 3, 4 );
  425. // bogus QUnit.test values
  426. b.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  427. b.matrixWorld.set( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 );
  428. b.matrixAutoUpdate = false;
  429. b.matrixWorldNeedsUpdate = true;
  430. b.layers.mask = 2;
  431. b.visible = false;
  432. b.castShadow = true;
  433. b.receiveShadow = true;
  434. b.frustumCulled = false;
  435. b.renderOrder = 1;
  436. b.userData[ "foo" ] = "bar";
  437. child.add( childChild );
  438. b.add( child );
  439. assert.notDeepEqual( a, b, "Objects are not equal pre-copy()" );
  440. a.copy( b, true );
  441. // check they're all unique instances
  442. assert.ok(
  443. a.uuid !== b.uuid &&
  444. a.children[ 0 ].uuid !== b.children[ 0 ].uuid &&
  445. a.children[ 0 ].children[ 0 ].uuid !== b.children[ 0 ].children[ 0 ].uuid,
  446. "UUIDs are all different"
  447. );
  448. // and now fix that
  449. a.uuid = b.uuid;
  450. a.children[ 0 ].uuid = b.children[ 0 ].uuid;
  451. a.children[ 0 ].children[ 0 ].uuid = b.children[ 0 ].children[ 0 ].uuid;
  452. assert.deepEqual( a, b, "Objects are equal post-copy()" );
  453. } );
  454. } );
  455. } );