Object3D.tests.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /* global QUnit */
  2. import { Object3D } from '../../../../src/core/Object3D.js';
  3. import { Vector3 } from '../../../../src/math/Vector3.js';
  4. import { Euler } from '../../../../src/math/Euler.js';
  5. import { Quaternion } from '../../../../src/math/Quaternion.js';
  6. import { Matrix4 } from '../../../../src/math/Matrix4.js';
  7. import {
  8. x,
  9. y,
  10. z,
  11. w,
  12. eps
  13. } from '../math/Constants.tests.js';
  14. const matrixEquals4 = ( a, b ) => {
  15. for ( let i = 0; i < 16; i ++ ) {
  16. if ( Math.abs( a.elements[ i ] - b.elements[ i ] ) >= eps ) {
  17. return false;
  18. }
  19. }
  20. return true;
  21. };
  22. export default QUnit.module( 'Core', () => {
  23. QUnit.module( 'Object3D', () => {
  24. var RadToDeg = 180 / Math.PI;
  25. var eulerEquals = function ( a, b, tolerance ) {
  26. tolerance = tolerance || 0.0001;
  27. if ( a.order != b.order ) {
  28. return false;
  29. }
  30. return (
  31. Math.abs( a.x - b.x ) <= tolerance &&
  32. Math.abs( a.y - b.y ) <= tolerance &&
  33. Math.abs( a.z - b.z ) <= tolerance
  34. );
  35. };
  36. // INHERITANCE
  37. QUnit.todo( 'Extending', ( assert ) => {
  38. assert.ok( false, 'everything\'s gonna be alright' );
  39. } );
  40. // INSTANCING
  41. QUnit.todo( 'Instancing', ( assert ) => {
  42. assert.ok( false, 'everything\'s gonna be alright' );
  43. } );
  44. // STATIC STUFF
  45. QUnit.test( 'DefaultUp', ( assert ) => {
  46. const currentDefaultUp = new Vector3().copy( Object3D.DefaultUp );
  47. const v = new Vector3();
  48. try {
  49. assert.deepEqual( Object3D.DefaultUp, v.set( 0, 1, 0 ), 'default DefaultUp is Y-up' );
  50. const object = new Object3D();
  51. assert.deepEqual( object.up, v.set( 0, 1, 0 ), '.up of a new object inherits Object3D.DefaultUp = Y-up' );
  52. Object3D.DefaultUp.set( 0, 0, 1 );
  53. const object2 = new Object3D();
  54. assert.deepEqual( object2.up, v.set( 0, 0, 1 ), '.up of a new object inherits Object3D.DefaultUp = Z-up' );
  55. } finally {
  56. Object3D.DefaultUp.copy( currentDefaultUp );
  57. }
  58. } );
  59. QUnit.test( 'DefaultMatrixAutoUpdate', ( assert ) => {
  60. const currentDefaultMatrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
  61. try {
  62. assert.equal( currentDefaultMatrixAutoUpdate, true, 'default DefaultMatrixAutoUpdate is true' );
  63. const object = new Object3D();
  64. assert.equal( object.matrixAutoUpdate, true, '.matrixAutoUpdate of a new object inherits Object3D.DefaultMatrixAutoUpdate = true' );
  65. Object3D.DefaultMatrixAutoUpdate = false;
  66. const object2 = new Object3D();
  67. assert.equal( object2.matrixAutoUpdate, false, '.matrixAutoUpdate of a new object inherits Object3D.DefaultMatrixAutoUpdate = false' );
  68. } finally {
  69. Object3D.DefaultMatrixAutoUpdate = currentDefaultMatrixAutoUpdate;
  70. }
  71. } );
  72. // PUBLIC STUFF
  73. QUnit.test( 'isObject3D', ( assert ) => {
  74. const object = new Object3D();
  75. assert.ok( object.isObject3D, 'Object3D.isObject3D should be true' );
  76. const object2 = {};
  77. assert.ok( object2.isObject3D === undefined, 'other object isObject3D should be undefined' );
  78. } );
  79. QUnit.todo( 'onBeforeRender', ( assert ) => {
  80. assert.ok( false, 'everything\'s gonna be alright' );
  81. } );
  82. QUnit.todo( 'onAfterRender', ( assert ) => {
  83. assert.ok( false, 'everything\'s gonna be alright' );
  84. } );
  85. QUnit.test( 'applyMatrix4', ( assert ) => {
  86. var a = new Object3D();
  87. var m = new Matrix4();
  88. var expectedPos = new Vector3( x, y, z );
  89. var expectedQuat = new Quaternion( 0.5 * Math.sqrt( 2 ), 0, 0, 0.5 * Math.sqrt( 2 ) );
  90. m.makeRotationX( Math.PI / 2 );
  91. m.setPosition( new Vector3( x, y, z ) );
  92. a.applyMatrix4( m );
  93. assert.deepEqual( a.position, expectedPos, 'Position has the expected values' );
  94. assert.ok(
  95. Math.abs( a.quaternion.x - expectedQuat.x ) <= eps &&
  96. Math.abs( a.quaternion.y - expectedQuat.y ) <= eps &&
  97. Math.abs( a.quaternion.z - expectedQuat.z ) <= eps,
  98. 'Quaternion has the expected values'
  99. );
  100. } );
  101. QUnit.test( 'applyQuaternion', ( assert ) => {
  102. var a = new Object3D();
  103. var sqrt = 0.5 * Math.sqrt( 2 );
  104. var quat = new Quaternion( 0, sqrt, 0, sqrt );
  105. var expected = new Quaternion( sqrt / 2, sqrt / 2, 0, 0 );
  106. a.quaternion.set( 0.25, 0.25, 0.25, 0.25 );
  107. a.applyQuaternion( quat );
  108. assert.ok(
  109. Math.abs( a.quaternion.x - expected.x ) <= eps &&
  110. Math.abs( a.quaternion.y - expected.y ) <= eps &&
  111. Math.abs( a.quaternion.z - expected.z ) <= eps,
  112. 'Quaternion has the expected values'
  113. );
  114. } );
  115. QUnit.test( 'setRotationFromAxisAngle', ( assert ) => {
  116. var a = new Object3D();
  117. var axis = new Vector3( 0, 1, 0 );
  118. var angle = Math.PI;
  119. var expected = new Euler( - Math.PI, 0, - Math.PI );
  120. var euler = new Euler();
  121. a.setRotationFromAxisAngle( axis, angle );
  122. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  123. assert.ok( eulerEquals( euler, expected ), 'Correct values after rotation' );
  124. axis.set( 1, 0, 0 );
  125. var angle = 0;
  126. expected.set( 0, 0, 0 );
  127. a.setRotationFromAxisAngle( axis, angle );
  128. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  129. assert.ok( eulerEquals( euler, expected ), 'Correct values after zeroing' );
  130. } );
  131. QUnit.test( 'setRotationFromEuler', ( assert ) => {
  132. var a = new Object3D();
  133. var rotation = new Euler( ( 45 / RadToDeg ), 0, Math.PI );
  134. var expected = rotation.clone(); // bit obvious
  135. var euler = new Euler();
  136. a.setRotationFromEuler( rotation );
  137. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  138. assert.ok( eulerEquals( euler, expected ), 'Correct values after rotation' );
  139. } );
  140. QUnit.test( 'setRotationFromMatrix', ( assert ) => {
  141. var a = new Object3D();
  142. var m = new Matrix4();
  143. var eye = new Vector3( 0, 0, 0 );
  144. var target = new Vector3( 0, 1, - 1 );
  145. var up = new Vector3( 0, 1, 0 );
  146. var euler = new Euler();
  147. m.lookAt( eye, target, up );
  148. a.setRotationFromMatrix( m );
  149. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  150. assert.numEqual( euler.x * RadToDeg, 45, 'Correct rotation angle' );
  151. } );
  152. QUnit.test( 'setRotationFromQuaternion', ( assert ) => {
  153. var a = new Object3D();
  154. var rotation = new Quaternion().setFromEuler( new Euler( Math.PI, 0, - Math.PI ) );
  155. var euler = new Euler();
  156. a.setRotationFromQuaternion( rotation );
  157. euler.setFromQuaternion( a.getWorldQuaternion( new Quaternion() ) );
  158. assert.ok( eulerEquals( euler, new Euler( Math.PI, 0, - Math.PI ) ), 'Correct values after rotation' );
  159. } );
  160. QUnit.todo( 'rotateOnAxis', ( assert ) => {
  161. assert.ok( false, 'everything\'s gonna be alright' );
  162. } );
  163. QUnit.todo( 'rotateOnWorldAxis', ( assert ) => {
  164. assert.ok( false, 'everything\'s gonna be alright' );
  165. } );
  166. QUnit.test( 'rotateX', ( assert ) => {
  167. var obj = new Object3D();
  168. var angleInRad = 1.562;
  169. obj.rotateX( angleInRad );
  170. assert.numEqual( obj.rotation.x, angleInRad, 'x is equal' );
  171. } );
  172. QUnit.test( 'rotateY', ( assert ) => {
  173. var obj = new Object3D();
  174. var angleInRad = - 0.346;
  175. obj.rotateY( angleInRad );
  176. assert.numEqual( obj.rotation.y, angleInRad, 'y is equal' );
  177. } );
  178. QUnit.test( 'rotateZ', ( assert ) => {
  179. var obj = new Object3D();
  180. var angleInRad = 1;
  181. obj.rotateZ( angleInRad );
  182. assert.numEqual( obj.rotation.z, angleInRad, 'z is equal' );
  183. } );
  184. QUnit.test( 'translateOnAxis', ( assert ) => {
  185. var obj = new Object3D();
  186. obj.translateOnAxis( new Vector3( 1, 0, 0 ), 1 );
  187. obj.translateOnAxis( new Vector3( 0, 1, 0 ), 1.23 );
  188. obj.translateOnAxis( new Vector3( 0, 0, 1 ), - 4.56 );
  189. assert.propEqual( obj.position, {
  190. x: 1,
  191. y: 1.23,
  192. z: - 4.56,
  193. } );
  194. } );
  195. QUnit.test( 'translateX', ( assert ) => {
  196. var obj = new Object3D();
  197. obj.translateX( 1.234 );
  198. assert.numEqual( obj.position.x, 1.234, 'x is equal' );
  199. } );
  200. QUnit.test( 'translateY', ( assert ) => {
  201. var obj = new Object3D();
  202. obj.translateY( 1.234 );
  203. assert.numEqual( obj.position.y, 1.234, 'y is equal' );
  204. } );
  205. QUnit.test( 'translateZ', ( assert ) => {
  206. var obj = new Object3D();
  207. obj.translateZ( 1.234 );
  208. assert.numEqual( obj.position.z, 1.234, 'z is equal' );
  209. } );
  210. QUnit.test( 'localToWorld', ( assert ) => {
  211. const v = new Vector3();
  212. const expectedPosition = new Vector3( 5, - 1, - 4 );
  213. const parent = new Object3D();
  214. const child = new Object3D();
  215. parent.position.set( 1, 0, 0 );
  216. parent.rotation.set( 0, Math.PI / 2, 0 );
  217. parent.scale.set( 2, 1, 1 );
  218. child.position.set( 0, 1, 0 );
  219. child.rotation.set( Math.PI / 2, 0, 0 );
  220. child.scale.set( 1, 2, 1 );
  221. parent.add( child );
  222. parent.updateMatrixWorld();
  223. child.localToWorld( v.set( 2, 2, 2 ) );
  224. assert.ok(
  225. Math.abs( v.x - expectedPosition.x ) <= eps &&
  226. Math.abs( v.y - expectedPosition.y ) <= eps &&
  227. Math.abs( v.z - expectedPosition.z ) <= eps,
  228. 'local vector is converted to world'
  229. );
  230. } );
  231. QUnit.test( 'worldToLocal', ( assert ) => {
  232. const v = new Vector3();
  233. const expectedPosition = new Vector3( - 1, 0.5, - 1 );
  234. const parent = new Object3D();
  235. const child = new Object3D();
  236. parent.position.set( 1, 0, 0 );
  237. parent.rotation.set( 0, Math.PI / 2, 0 );
  238. parent.scale.set( 2, 1, 1 );
  239. child.position.set( 0, 1, 0 );
  240. child.rotation.set( Math.PI / 2, 0, 0 );
  241. child.scale.set( 1, 2, 1 );
  242. parent.add( child );
  243. parent.updateMatrixWorld();
  244. child.worldToLocal( v.set( 2, 2, 2 ) );
  245. assert.ok(
  246. Math.abs( v.x - expectedPosition.x ) <= eps &&
  247. Math.abs( v.y - expectedPosition.y ) <= eps &&
  248. Math.abs( v.z - expectedPosition.z ) <= eps,
  249. 'world vector is converted to local'
  250. );
  251. } );
  252. QUnit.test( 'lookAt', ( assert ) => {
  253. var obj = new Object3D();
  254. obj.lookAt( new Vector3( 0, - 1, 1 ) );
  255. assert.numEqual( obj.rotation.x * RadToDeg, 45, 'x is equal' );
  256. } );
  257. QUnit.test( 'add/remove/clear', ( assert ) => {
  258. var a = new Object3D();
  259. var child1 = new Object3D();
  260. var child2 = new Object3D();
  261. assert.strictEqual( a.children.length, 0, 'Starts with no children' );
  262. a.add( child1 );
  263. assert.strictEqual( a.children.length, 1, 'The first child was added' );
  264. assert.strictEqual( a.children[ 0 ], child1, 'It\'s the right one' );
  265. a.add( child2 );
  266. assert.strictEqual( a.children.length, 2, 'The second child was added' );
  267. assert.strictEqual( a.children[ 1 ], child2, 'It\'s the right one' );
  268. assert.strictEqual( a.children[ 0 ], child1, 'The first one is still there' );
  269. a.remove( child1 );
  270. assert.strictEqual( a.children.length, 1, 'The first child was removed' );
  271. assert.strictEqual( a.children[ 0 ], child2, 'The second one is still there' );
  272. a.add( child1 );
  273. a.remove( child1, child2 );
  274. assert.strictEqual( a.children.length, 0, 'Both children were removed at once' );
  275. child1.add( child2 );
  276. assert.strictEqual( child1.children.length, 1, 'The second child was added to the first one' );
  277. a.add( child2 );
  278. assert.strictEqual( a.children.length, 1, 'The second one was added to the parent (no remove)' );
  279. assert.strictEqual( a.children[ 0 ], child2, 'The second one is now the parent\'s child again' );
  280. assert.strictEqual( child1.children.length, 0, 'The first one no longer has any children' );
  281. a.add( child1 );
  282. assert.strictEqual( a.children.length, 2, 'The first child was added to the parent' );
  283. a.clear();
  284. assert.strictEqual( a.children.length, 0, 'All childrens were removed' );
  285. assert.strictEqual( child1.parent, null, 'First child has no parent' );
  286. assert.strictEqual( child2.parent, null, 'Second child has no parent' );
  287. a.add( child1 );
  288. assert.strictEqual( a.children.length, 1, 'The child was added to the parent' );
  289. child1.removeFromParent();
  290. assert.strictEqual( a.children.length, 0, 'The child was removed' );
  291. assert.strictEqual( child1.parent, null, 'Child has no parent' );
  292. } );
  293. QUnit.test( 'attach', ( assert ) => {
  294. const object = new Object3D();
  295. const oldParent = new Object3D();
  296. const newParent = new Object3D();
  297. const expectedMatrixWorld = new Matrix4();
  298. // Attach to a parent
  299. object.position.set( 1, 2, 3 );
  300. object.rotation.set( Math.PI / 2, Math.PI / 3, Math.PI / 4 );
  301. object.scale.set( 2, 3, 4 );
  302. newParent.position.set( 4, 5, 6 );
  303. newParent.rotation.set( Math.PI / 5, Math.PI / 6, Math.PI / 7 );
  304. newParent.scale.set( 5, 5, 5 );
  305. object.updateMatrixWorld();
  306. newParent.updateMatrixWorld();
  307. expectedMatrixWorld.copy( object.matrixWorld );
  308. newParent.attach( object );
  309. assert.ok( object.parent && object.parent == newParent &&
  310. oldParent.children.indexOf( object ) === - 1,
  311. 'object is a child of a new parent' );
  312. assert.ok( matrixEquals4( expectedMatrixWorld, object.matrixWorld ), 'object\'s world matrix is maintained' );
  313. // Attach to a new parent from an old parent
  314. object.position.set( 1, 2, 3 );
  315. object.rotation.set( Math.PI / 2, Math.PI / 3, Math.PI / 4 );
  316. object.scale.set( 2, 3, 4 );
  317. oldParent.position.set( 4, 5, 6 );
  318. oldParent.rotation.set( Math.PI / 5, Math.PI / 6, Math.PI / 7 );
  319. oldParent.scale.set( 5, 5, 5 );
  320. newParent.position.set( 7, 8, 9 );
  321. newParent.rotation.set( Math.PI / 8, Math.PI / 9, Math.PI / 10 );
  322. newParent.scale.set( 6, 6, 6 );
  323. oldParent.add( object );
  324. oldParent.updateMatrixWorld();
  325. newParent.updateMatrixWorld();
  326. expectedMatrixWorld.copy( object.matrixWorld );
  327. newParent.attach( object );
  328. assert.ok( object.parent && object.parent == newParent &&
  329. newParent.children.indexOf( object ) !== - 1 &&
  330. oldParent.children.indexOf( object ) === - 1,
  331. 'object is no longer a child of an old parent and is a child of a new parent now' );
  332. assert.ok( matrixEquals4( expectedMatrixWorld, object.matrixWorld ),
  333. 'object\'s world matrix is maintained even it had a parent' );
  334. } );
  335. QUnit.test( 'getObjectById/getObjectByName/getObjectByProperty', ( assert ) => {
  336. var parent = new Object3D();
  337. var childName = new Object3D();
  338. var childId = new Object3D(); // id = parent.id + 2
  339. var childNothing = new Object3D();
  340. parent.prop = true;
  341. childName.name = 'foo';
  342. parent.add( childName, childId, childNothing );
  343. assert.strictEqual( parent.getObjectByProperty( 'prop', true ), parent, 'Get parent by its own property' );
  344. assert.strictEqual( parent.getObjectByName( 'foo' ), childName, 'Get child by name' );
  345. assert.strictEqual( parent.getObjectById( parent.id + 2 ), childId, 'Get child by Id' );
  346. assert.strictEqual(
  347. parent.getObjectByProperty( 'no-property', 'no-value' ), undefined,
  348. 'Unknown property results in undefined'
  349. );
  350. } );
  351. QUnit.test( 'getWorldPosition', ( assert ) => {
  352. var a = new Object3D();
  353. var b = new Object3D();
  354. var expectedSingle = new Vector3( x, y, z );
  355. var expectedParent = new Vector3( x, y, 0 );
  356. var expectedChild = new Vector3( x, y, 7 );
  357. var position = new Vector3();
  358. a.translateX( x );
  359. a.translateY( y );
  360. a.translateZ( z );
  361. assert.deepEqual( a.getWorldPosition( position ), expectedSingle, 'WorldPosition as expected for single object' );
  362. // translate child and then parent
  363. b.translateZ( 7 );
  364. a.add( b );
  365. a.translateZ( - z );
  366. assert.deepEqual( a.getWorldPosition( position ), expectedParent, 'WorldPosition as expected for parent' );
  367. assert.deepEqual( b.getWorldPosition( position ), expectedChild, 'WorldPosition as expected for child' );
  368. } );
  369. QUnit.todo( 'getWorldQuaternion', ( assert ) => {
  370. assert.ok( false, 'everything\'s gonna be alright' );
  371. } );
  372. QUnit.test( 'getWorldScale', ( assert ) => {
  373. var a = new Object3D();
  374. var m = new Matrix4().makeScale( x, y, z );
  375. var expected = new Vector3( x, y, z );
  376. a.applyMatrix4( m );
  377. assert.deepEqual( a.getWorldScale( new Vector3() ), expected, 'WorldScale as expected' );
  378. } );
  379. QUnit.test( 'getWorldDirection', ( assert ) => {
  380. var a = new Object3D();
  381. var expected = new Vector3( 0, - 0.5 * Math.sqrt( 2 ), 0.5 * Math.sqrt( 2 ) );
  382. var direction = new Vector3();
  383. a.lookAt( new Vector3( 0, - 1, 1 ) );
  384. a.getWorldDirection( direction );
  385. assert.ok(
  386. Math.abs( direction.x - expected.x ) <= eps &&
  387. Math.abs( direction.y - expected.y ) <= eps &&
  388. Math.abs( direction.z - expected.z ) <= eps,
  389. 'Direction has the expected values'
  390. );
  391. } );
  392. QUnit.test( 'localTransformVariableInstantiation', ( assert ) => {
  393. var a = new Object3D();
  394. var b = new Object3D();
  395. var c = new Object3D();
  396. var d = new Object3D();
  397. a.getWorldDirection( new Vector3() );
  398. a.lookAt( new Vector3( 0, - 1, 1 ) );
  399. assert.ok( true, 'Calling lookAt after getWorldDirection does not create errors' );
  400. b.getWorldPosition( new Vector3() );
  401. b.lookAt( new Vector3( 0, - 1, 1 ) );
  402. assert.ok( true, 'Calling lookAt after getWorldPosition does not create errors' );
  403. c.getWorldQuaternion( new Quaternion() );
  404. c.lookAt( new Vector3( 0, - 1, 1 ) );
  405. assert.ok( true, 'Calling lookAt after getWorldQuaternion does not create errors' );
  406. d.getWorldScale( new Vector3() );
  407. d.lookAt( new Vector3( 0, - 1, 1 ) );
  408. assert.ok( true, 'Calling lookAt after getWorldScale does not create errors' );
  409. } );
  410. QUnit.todo( 'raycast', ( assert ) => {
  411. assert.ok( false, 'everything\'s gonna be alright' );
  412. } );
  413. QUnit.test( 'traverse/traverseVisible/traverseAncestors', ( assert ) => {
  414. var a = new Object3D();
  415. var b = new Object3D();
  416. var c = new Object3D();
  417. var d = new Object3D();
  418. var names = [];
  419. var expectedNormal = [ 'parent', 'child', 'childchild 1', 'childchild 2' ];
  420. var expectedVisible = [ 'parent', 'child', 'childchild 2' ];
  421. var expectedAncestors = [ 'child', 'parent' ];
  422. a.name = 'parent';
  423. b.name = 'child';
  424. c.name = 'childchild 1';
  425. c.visible = false;
  426. d.name = 'childchild 2';
  427. b.add( c );
  428. b.add( d );
  429. a.add( b );
  430. a.traverse( function ( obj ) {
  431. names.push( obj.name );
  432. } );
  433. assert.deepEqual( names, expectedNormal, 'Traversed objects in expected order' );
  434. var names = [];
  435. a.traverseVisible( function ( obj ) {
  436. names.push( obj.name );
  437. } );
  438. assert.deepEqual( names, expectedVisible, 'Traversed visible objects in expected order' );
  439. var names = [];
  440. c.traverseAncestors( function ( obj ) {
  441. names.push( obj.name );
  442. } );
  443. assert.deepEqual( names, expectedAncestors, 'Traversed ancestors in expected order' );
  444. } );
  445. QUnit.test( 'updateMatrix', ( assert ) => {
  446. const a = new Object3D();
  447. a.position.set( 2, 3, 4 );
  448. a.quaternion.set( 5, 6, 7, 8 );
  449. a.scale.set( 9, 10, 11 );
  450. assert.deepEqual( a.matrix.elements, [
  451. 1, 0, 0, 0,
  452. 0, 1, 0, 0,
  453. 0, 0, 1, 0,
  454. 0, 0, 0, 1
  455. ], 'Updating position, quaternion, or scale has no effect to matrix until calling updateMatrix()' );
  456. a.updateMatrix();
  457. assert.deepEqual( a.matrix.elements, [
  458. - 1521, 1548, - 234, 0,
  459. - 520, - 1470, 1640, 0,
  460. 1826, 44, - 1331, 0,
  461. 2, 3, 4, 1
  462. ], 'matrix is calculated from position, quaternion, and scale' );
  463. assert.equal( a.matrixWorldNeedsUpdate, true, 'The flag indicating world matrix needs to be updated should be true' );
  464. } );
  465. QUnit.test( 'updateMatrixWorld', ( assert ) => {
  466. const parent = new Object3D();
  467. const child = new Object3D();
  468. // -- Standard usage test
  469. parent.position.set( 1, 2, 3 );
  470. child.position.set( 4, 5, 6 );
  471. parent.add( child );
  472. parent.updateMatrixWorld();
  473. assert.deepEqual( parent.matrix.elements, [
  474. 1, 0, 0, 0,
  475. 0, 1, 0, 0,
  476. 0, 0, 1, 0,
  477. 1, 2, 3, 1
  478. ], 'updateMatrixWorld() updates local matrix' );
  479. assert.deepEqual( parent.matrixWorld.elements, [
  480. 1, 0, 0, 0,
  481. 0, 1, 0, 0,
  482. 0, 0, 1, 0,
  483. 1, 2, 3, 1
  484. ], 'updateMatrixWorld() updates world matrix' );
  485. assert.deepEqual( child.matrix.elements, [
  486. 1, 0, 0, 0,
  487. 0, 1, 0, 0,
  488. 0, 0, 1, 0,
  489. 4, 5, 6, 1
  490. ], 'updateMatrixWorld() updates children\'s local matrix' );
  491. assert.deepEqual( child.matrixWorld.elements, [
  492. 1, 0, 0, 0,
  493. 0, 1, 0, 0,
  494. 0, 0, 1, 0,
  495. 5, 7, 9, 1
  496. ], 'updateMatrixWorld() updates children\'s world matrices from their parent world matrix and their local matrices' );
  497. assert.equal( parent.matrixWorldNeedsUpdate || child.matrixWorldNeedsUpdate, false, 'The flag indicating world matrix needs to be updated should be false after updating world matrix' );
  498. // -- No sync between local position/quaternion/scale/matrix and world matrix test
  499. parent.position.set( 0, 0, 0 );
  500. parent.updateMatrix();
  501. assert.deepEqual( parent.matrixWorld.elements, [
  502. 1, 0, 0, 0,
  503. 0, 1, 0, 0,
  504. 0, 0, 1, 0,
  505. 1, 2, 3, 1
  506. ], 'Updating position, quaternion, scale, or local matrix has no effect to world matrix until calling updateWorldMatrix()' );
  507. // -- matrixAutoUpdate = false test
  508. // Resetting local and world matrices to the origin
  509. child.position.set( 0, 0, 0 );
  510. parent.updateMatrixWorld();
  511. parent.position.set( 1, 2, 3 );
  512. parent.matrixAutoUpdate = false;
  513. child.matrixAutoUpdate = false;
  514. parent.updateMatrixWorld();
  515. assert.deepEqual( parent.matrix.elements, [
  516. 1, 0, 0, 0,
  517. 0, 1, 0, 0,
  518. 0, 0, 1, 0,
  519. 0, 0, 0, 1
  520. ], 'updateMatrixWorld() doesn\'t update local matrix if matrixAutoUpdate is false' );
  521. assert.deepEqual( parent.matrixWorld.elements, [
  522. 1, 0, 0, 0,
  523. 0, 1, 0, 0,
  524. 0, 0, 1, 0,
  525. 0, 0, 0, 1
  526. ], 'World matrix isn\'t updated because local matrix isn\'t updated and the flag indicating world matrix needs to be updated didn\'t rise' );
  527. assert.deepEqual( child.matrixWorld.elements, [
  528. 1, 0, 0, 0,
  529. 0, 1, 0, 0,
  530. 0, 0, 1, 0,
  531. 0, 0, 0, 1
  532. ], 'No effect to child world matrix if parent local and world matrices and child local matrix are not updated' );
  533. // -- matrixWorldAutoUpdate = false test
  534. parent.position.set( 3, 2, 1 );
  535. parent.updateMatrix();
  536. parent.matrixWorldNeedsUpdate = false;
  537. child.matrixWorldAutoUpdate = false;
  538. parent.updateMatrixWorld();
  539. assert.deepEqual( child.matrixWorld.elements, [
  540. 1, 0, 0, 0,
  541. 0, 1, 0, 0,
  542. 0, 0, 1, 0,
  543. 0, 0, 0, 1
  544. ], 'No effect to child world matrix when matrixWorldAutoUpdate is set to false' );
  545. // -- Propagation to children world matrices test
  546. child.position.set( 0, 0, 0 );
  547. parent.position.set( 1, 2, 3 );
  548. child.matrixWorldAutoUpdate = true;
  549. parent.matrixAutoUpdate = true;
  550. parent.updateMatrixWorld();
  551. assert.deepEqual( child.matrixWorld.elements, [
  552. 1, 0, 0, 0,
  553. 0, 1, 0, 0,
  554. 0, 0, 1, 0,
  555. 1, 2, 3, 1
  556. ], 'Updating parent world matrix has effect to children world matrices even if children local matrices aren\'t changed' );
  557. // -- force argument test
  558. // Resetting the local and world matrices to the origin
  559. child.position.set( 0, 0, 0 );
  560. child.matrixAutoUpdate = true;
  561. parent.updateMatrixWorld();
  562. parent.position.set( 1, 2, 3 );
  563. parent.updateMatrix();
  564. parent.matrixAutoUpdate = false;
  565. parent.matrixWorldNeedsUpdate = false;
  566. parent.updateMatrixWorld( true );
  567. assert.deepEqual( parent.matrixWorld.elements, [
  568. 1, 0, 0, 0,
  569. 0, 1, 0, 0,
  570. 0, 0, 1, 0,
  571. 1, 2, 3, 1
  572. ], 'force = true forces to update world matrix even if local matrix is not changed' );
  573. // -- Restriction test: No effect to parent matrices
  574. // Resetting the local and world matrices to the origin
  575. parent.position.set( 0, 0, 0 );
  576. child.position.set( 0, 0, 0 );
  577. parent.matrixAutoUpdate = true;
  578. child.matrixAutoUpdate = true;
  579. parent.updateMatrixWorld();
  580. parent.position.set( 1, 2, 3 );
  581. child.position.set( 4, 5, 6 );
  582. child.updateMatrixWorld();
  583. assert.deepEqual( parent.matrix.elements, [
  584. 1, 0, 0, 0,
  585. 0, 1, 0, 0,
  586. 0, 0, 1, 0,
  587. 0, 0, 0, 1
  588. ], 'updateMatrixWorld() doesn\'t update parent local matrix' );
  589. assert.deepEqual( parent.matrixWorld.elements, [
  590. 1, 0, 0, 0,
  591. 0, 1, 0, 0,
  592. 0, 0, 1, 0,
  593. 0, 0, 0, 1
  594. ], 'updateMatrixWorld() doesn\'t update parent world matrix' );
  595. assert.deepEqual( child.matrixWorld.elements, [
  596. 1, 0, 0, 0,
  597. 0, 1, 0, 0,
  598. 0, 0, 1, 0,
  599. 4, 5, 6, 1
  600. ], 'updateMatrixWorld() calculates world matrix from the current parent world matrix' );
  601. } );
  602. QUnit.test( 'updateWorldMatrix', ( assert ) => {
  603. const object = new Object3D();
  604. const parent = new Object3D();
  605. const child = new Object3D();
  606. const m = new Matrix4();
  607. const v = new Vector3();
  608. parent.add( object );
  609. object.add( child );
  610. parent.position.set( 1, 2, 3 );
  611. object.position.set( 4, 5, 6 );
  612. child.position.set( 7, 8, 9 );
  613. // Update the world matrix of an object
  614. object.updateWorldMatrix();
  615. assert.deepEqual( parent.matrix.elements,
  616. m.elements,
  617. 'No effect to parents\' local matrices' );
  618. assert.deepEqual( parent.matrixWorld.elements,
  619. m.elements,
  620. 'No effect to parents\' world matrices' );
  621. assert.deepEqual( object.matrix.elements,
  622. m.setPosition( object.position ).elements,
  623. 'Object\'s local matrix is updated' );
  624. assert.deepEqual( object.matrixWorld.elements,
  625. m.setPosition( object.position ).elements,
  626. 'Object\'s world matrix is updated' );
  627. assert.deepEqual( child.matrix.elements,
  628. m.identity().elements,
  629. 'No effect to children\'s local matrices' );
  630. assert.deepEqual( child.matrixWorld.elements,
  631. m.elements,
  632. 'No effect to children\'s world matrices' );
  633. // Update the world matrices of an object and its parents
  634. object.matrix.identity();
  635. object.matrixWorld.identity();
  636. object.updateWorldMatrix( true, false );
  637. assert.deepEqual( parent.matrix.elements,
  638. m.setPosition( parent.position ).elements,
  639. 'Parents\' local matrices are updated' );
  640. assert.deepEqual( parent.matrixWorld.elements,
  641. m.setPosition( parent.position ).elements,
  642. 'Parents\' world matrices are updated' );
  643. assert.deepEqual( object.matrix.elements,
  644. m.setPosition( object.position ).elements,
  645. 'Object\'s local matrix is updated' );
  646. assert.deepEqual( object.matrixWorld.elements,
  647. m.setPosition( v.copy( parent.position ).add( object.position ) ).elements,
  648. 'Object\'s world matrix is updated' );
  649. assert.deepEqual( child.matrix.elements,
  650. m.identity().elements,
  651. 'No effect to children\'s local matrices' );
  652. assert.deepEqual( child.matrixWorld.elements,
  653. m.identity().elements,
  654. 'No effect to children\'s world matrices' );
  655. // Update the world matrices of an object and its children
  656. parent.matrix.identity();
  657. parent.matrixWorld.identity();
  658. object.matrix.identity();
  659. object.matrixWorld.identity();
  660. object.updateWorldMatrix( false, true );
  661. assert.deepEqual( parent.matrix.elements,
  662. m.elements,
  663. 'No effect to parents\' local matrices' );
  664. assert.deepEqual( parent.matrixWorld.elements,
  665. m.elements,
  666. 'No effect to parents\' world matrices' );
  667. assert.deepEqual( object.matrix.elements,
  668. m.setPosition( object.position ).elements,
  669. 'Object\'s local matrix is updated' );
  670. assert.deepEqual( object.matrixWorld.elements,
  671. m.setPosition( object.position ).elements,
  672. 'Object\'s world matrix is updated' );
  673. assert.deepEqual( child.matrix.elements,
  674. m.setPosition( child.position ).elements,
  675. 'Children\'s local matrices are updated' );
  676. assert.deepEqual( child.matrixWorld.elements,
  677. m.setPosition( v.copy( object.position ).add( child.position ) ).elements,
  678. 'Children\'s world matrices are updated' );
  679. // Update the world matrices of an object and its parents and children
  680. object.matrix.identity();
  681. object.matrixWorld.identity();
  682. child.matrix.identity();
  683. child.matrixWorld.identity();
  684. object.updateWorldMatrix( true, true );
  685. assert.deepEqual( parent.matrix.elements,
  686. m.setPosition( parent.position ).elements,
  687. 'Parents\' local matrices are updated' );
  688. assert.deepEqual( parent.matrixWorld.elements,
  689. m.setPosition( parent.position ).elements,
  690. 'Parents\' world matrices are updated' );
  691. assert.deepEqual( object.matrix.elements,
  692. m.setPosition( object.position ).elements,
  693. 'Object\'s local matrix is updated' );
  694. assert.deepEqual( object.matrixWorld.elements,
  695. m.setPosition( v.copy( parent.position ).add( object.position ) ).elements,
  696. 'Object\'s world matrix is updated' );
  697. assert.deepEqual( child.matrix.elements,
  698. m.setPosition( child.position ).elements,
  699. 'Children\'s local matrices are updated' );
  700. assert.deepEqual( child.matrixWorld.elements,
  701. m.setPosition( v.copy( parent.position ).add( object.position ).add( child.position ) ).elements,
  702. 'Children\'s world matrices are updated' );
  703. // object.matrixAutoUpdate = false test
  704. object.matrix.identity();
  705. object.matrixWorld.identity();
  706. object.matrixAutoUpdate = false;
  707. object.updateWorldMatrix( true, false );
  708. assert.deepEqual( object.matrix.elements,
  709. m.identity().elements,
  710. 'No effect to object\'s local matrix if matrixAutoUpdate is false' );
  711. assert.deepEqual( object.matrixWorld.elements,
  712. m.setPosition( parent.position ).elements,
  713. 'object\'s world matrix is updated even if matrixAutoUpdate is false' );
  714. // object.matrixWorldAutoUpdate = false test
  715. parent.matrixWorldAutoUpdate = false;
  716. child.matrixWorldAutoUpdate = false;
  717. child.matrixWorld.identity();
  718. parent.matrixWorld.identity();
  719. object.updateWorldMatrix( true, true );
  720. assert.deepEqual( child.matrixWorld.elements,
  721. m.identity().elements,
  722. 'No effect to child\'s world matrix if matrixWorldAutoUpdate is false' );
  723. assert.deepEqual( parent.matrixWorld.elements,
  724. m.identity().elements,
  725. 'No effect to parent\'s world matrix if matrixWorldAutoUpdate is false' );
  726. } );
  727. QUnit.test( 'toJSON', ( assert ) => {
  728. var a = new Object3D();
  729. var child = new Object3D();
  730. var childChild = new Object3D();
  731. a.name = 'a\'s name';
  732. a.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  733. a.visible = false;
  734. a.castShadow = true;
  735. a.receiveShadow = true;
  736. a.userData[ 'foo' ] = 'bar';
  737. child.uuid = '5D4E9AE8-DA61-4912-A575-71A5BE3D72CD';
  738. childChild.uuid = 'B43854B3-E970-4E85-BD41-AAF8D7BFA189';
  739. child.add( childChild );
  740. a.add( child );
  741. var gold = {
  742. 'metadata': {
  743. 'version': 4.5,
  744. 'type': 'Object',
  745. 'generator': 'Object3D.toJSON'
  746. },
  747. 'object': {
  748. 'uuid': '0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2',
  749. 'type': 'Object3D',
  750. 'name': 'a\'s name',
  751. 'castShadow': true,
  752. 'receiveShadow': true,
  753. 'visible': false,
  754. 'userData': { 'foo': 'bar' },
  755. 'layers': 1,
  756. 'matrix': [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  757. 'children': [
  758. {
  759. 'uuid': '5D4E9AE8-DA61-4912-A575-71A5BE3D72CD',
  760. 'type': 'Object3D',
  761. 'layers': 1,
  762. 'matrix': [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  763. 'children': [
  764. {
  765. 'uuid': 'B43854B3-E970-4E85-BD41-AAF8D7BFA189',
  766. 'type': 'Object3D',
  767. 'layers': 1,
  768. 'matrix': [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
  769. }
  770. ]
  771. }
  772. ]
  773. }
  774. };
  775. // hacks
  776. var out = a.toJSON();
  777. out.object.uuid = '0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2';
  778. assert.deepEqual( out, gold, 'JSON is as expected' );
  779. } );
  780. QUnit.test( 'clone', ( assert ) => {
  781. var a;
  782. var b = new Object3D();
  783. assert.strictEqual( a, undefined, 'Undefined pre-clone()' );
  784. a = b.clone();
  785. assert.notStrictEqual( a, b, 'Defined but seperate instances post-clone()' );
  786. a.uuid = b.uuid;
  787. assert.deepEqual( a, b, 'But identical properties' );
  788. } );
  789. QUnit.test( 'copy', ( assert ) => {
  790. var a = new Object3D();
  791. var b = new Object3D();
  792. var child = new Object3D();
  793. var childChild = new Object3D();
  794. a.name = 'original';
  795. b.name = 'to-be-copied';
  796. b.position.set( x, y, z );
  797. b.quaternion.set( x, y, z, w );
  798. b.scale.set( 2, 3, 4 );
  799. // bogus QUnit.test values
  800. b.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  801. b.matrixWorld.set( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 );
  802. b.matrixAutoUpdate = false;
  803. b.matrixWorldNeedsUpdate = true;
  804. b.layers.mask = 2;
  805. b.visible = false;
  806. b.castShadow = true;
  807. b.receiveShadow = true;
  808. b.frustumCulled = false;
  809. b.renderOrder = 1;
  810. b.userData[ 'foo' ] = 'bar';
  811. child.add( childChild );
  812. b.add( child );
  813. assert.notDeepEqual( a, b, 'Objects are not equal pre-copy()' );
  814. a.copy( b, true );
  815. // check they're all unique instances
  816. assert.ok(
  817. a.uuid !== b.uuid &&
  818. a.children[ 0 ].uuid !== b.children[ 0 ].uuid &&
  819. a.children[ 0 ].children[ 0 ].uuid !== b.children[ 0 ].children[ 0 ].uuid,
  820. 'UUIDs are all different'
  821. );
  822. // and now fix that
  823. a.uuid = b.uuid;
  824. a.children[ 0 ].uuid = b.children[ 0 ].uuid;
  825. a.children[ 0 ].children[ 0 ].uuid = b.children[ 0 ].children[ 0 ].uuid;
  826. assert.deepEqual( a, b, 'Objects are equal post-copy()' );
  827. } );
  828. } );
  829. } );
粤ICP备19079148号