Object3D.tests.js 33 KB

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