Object3D.tests.js 36 KB

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