skeleton.tests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. import { ObjectLoader } from '../../../src/loaders/ObjectLoader.js';
  2. export default QUnit.module( 'JSON4 Format', () => {
  3. QUnit.module( 'Skeleton', () => {
  4. QUnit.test( 'SkinnedMesh basic', ( assert ) => {
  5. const json = {
  6. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  7. geometries: [ {
  8. uuid: 'geom-1',
  9. type: 'BufferGeometry',
  10. data: {
  11. attributes: {
  12. position: {
  13. itemSize: 3,
  14. type: 'Float32Array',
  15. array: [ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, 0 ],
  16. normalized: false
  17. },
  18. skinIndex: {
  19. itemSize: 4,
  20. type: 'Uint16Array',
  21. array: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 ],
  22. normalized: false
  23. },
  24. skinWeight: {
  25. itemSize: 4,
  26. type: 'Float32Array',
  27. array: [ 1, 0, 0, 0, 1, 0, 0, 0, 0.5, 0.5, 0, 0, 1, 0, 0, 0 ],
  28. normalized: false
  29. }
  30. }
  31. }
  32. } ],
  33. materials: [ {
  34. uuid: 'mat-1',
  35. type: 'MeshBasicMaterial',
  36. color: 16711680,
  37. skinning: true
  38. } ],
  39. skeletons: [ {
  40. uuid: 'skeleton-1',
  41. bones: [ 'bone-1', 'bone-2' ],
  42. boneInverses: [
  43. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  44. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 1 ]
  45. ]
  46. } ],
  47. object: {
  48. uuid: 'scene-1',
  49. type: 'Scene',
  50. children: [
  51. {
  52. uuid: 'skinned-1',
  53. type: 'SkinnedMesh',
  54. geometry: 'geom-1',
  55. material: 'mat-1',
  56. bindMode: 'attached',
  57. bindMatrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  58. skeleton: 'skeleton-1',
  59. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  60. layers: 1,
  61. children: [
  62. {
  63. uuid: 'bone-1',
  64. type: 'Bone',
  65. name: 'Root',
  66. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  67. layers: 1,
  68. children: [
  69. {
  70. uuid: 'bone-2',
  71. type: 'Bone',
  72. name: 'Child',
  73. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ],
  74. layers: 1
  75. }
  76. ]
  77. }
  78. ]
  79. }
  80. ]
  81. }
  82. };
  83. const loader = new ObjectLoader();
  84. const scene = loader.parse( json );
  85. const skinnedMesh = scene.children[ 0 ];
  86. assert.ok( skinnedMesh.isSkinnedMesh, 'Is SkinnedMesh' );
  87. assert.ok( skinnedMesh.skeleton, 'Has skeleton' );
  88. assert.strictEqual( skinnedMesh.bindMode, 'attached', 'BindMode is attached' );
  89. } );
  90. QUnit.test( 'Skeleton bones array', ( assert ) => {
  91. const json = {
  92. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  93. geometries: [ {
  94. uuid: 'geom-1',
  95. type: 'BufferGeometry',
  96. data: {
  97. attributes: {
  98. position: {
  99. itemSize: 3,
  100. type: 'Float32Array',
  101. array: [ 0, 0, 0, 1, 0, 0, 0, 1, 0 ],
  102. normalized: false
  103. },
  104. skinIndex: {
  105. itemSize: 4,
  106. type: 'Uint16Array',
  107. array: [ 0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 0, 0 ],
  108. normalized: false
  109. },
  110. skinWeight: {
  111. itemSize: 4,
  112. type: 'Float32Array',
  113. array: [ 1, 0, 0, 0, 0.5, 0.5, 0, 0, 0.3, 0.7, 0, 0 ],
  114. normalized: false
  115. }
  116. }
  117. }
  118. } ],
  119. materials: [ {
  120. uuid: 'mat-1',
  121. type: 'MeshBasicMaterial',
  122. color: 16711680
  123. } ],
  124. skeletons: [ {
  125. uuid: 'skeleton-1',
  126. bones: [ 'bone-1', 'bone-2', 'bone-3' ],
  127. boneInverses: [
  128. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  129. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 1 ],
  130. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, - 2, 0, 1 ]
  131. ]
  132. } ],
  133. object: {
  134. uuid: 'scene-1',
  135. type: 'Scene',
  136. children: [
  137. {
  138. uuid: 'skinned-1',
  139. type: 'SkinnedMesh',
  140. geometry: 'geom-1',
  141. material: 'mat-1',
  142. bindMode: 'attached',
  143. bindMatrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  144. skeleton: 'skeleton-1',
  145. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  146. layers: 1,
  147. children: [
  148. {
  149. uuid: 'bone-1',
  150. type: 'Bone',
  151. name: 'Root',
  152. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  153. layers: 1,
  154. children: [
  155. {
  156. uuid: 'bone-2',
  157. type: 'Bone',
  158. name: 'Middle',
  159. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ],
  160. layers: 1,
  161. children: [
  162. {
  163. uuid: 'bone-3',
  164. type: 'Bone',
  165. name: 'Tip',
  166. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ],
  167. layers: 1
  168. }
  169. ]
  170. }
  171. ]
  172. }
  173. ]
  174. }
  175. ]
  176. }
  177. };
  178. const loader = new ObjectLoader();
  179. const scene = loader.parse( json );
  180. const skinnedMesh = scene.children[ 0 ];
  181. assert.strictEqual( skinnedMesh.skeleton.bones.length, 3, 'Skeleton has 3 bones' );
  182. assert.strictEqual( skinnedMesh.skeleton.bones[ 0 ].name, 'Root', 'First bone name' );
  183. assert.strictEqual( skinnedMesh.skeleton.bones[ 1 ].name, 'Middle', 'Second bone name' );
  184. assert.strictEqual( skinnedMesh.skeleton.bones[ 2 ].name, 'Tip', 'Third bone name' );
  185. } );
  186. QUnit.test( 'Bone hierarchy (parent-child chain)', ( assert ) => {
  187. const json = {
  188. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  189. geometries: [ {
  190. uuid: 'geom-1',
  191. type: 'BufferGeometry',
  192. data: {
  193. attributes: {
  194. position: {
  195. itemSize: 3,
  196. type: 'Float32Array',
  197. array: [ 0, 0, 0, 1, 0, 0, 0, 1, 0 ],
  198. normalized: false
  199. },
  200. skinIndex: {
  201. itemSize: 4,
  202. type: 'Uint16Array',
  203. array: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
  204. normalized: false
  205. },
  206. skinWeight: {
  207. itemSize: 4,
  208. type: 'Float32Array',
  209. array: [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ],
  210. normalized: false
  211. }
  212. }
  213. }
  214. } ],
  215. materials: [ {
  216. uuid: 'mat-1',
  217. type: 'MeshBasicMaterial',
  218. color: 16711680
  219. } ],
  220. skeletons: [ {
  221. uuid: 'skeleton-1',
  222. bones: [ 'bone-1', 'bone-2' ],
  223. boneInverses: [
  224. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  225. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 1 ]
  226. ]
  227. } ],
  228. object: {
  229. uuid: 'scene-1',
  230. type: 'Scene',
  231. children: [
  232. {
  233. uuid: 'skinned-1',
  234. type: 'SkinnedMesh',
  235. geometry: 'geom-1',
  236. material: 'mat-1',
  237. bindMode: 'attached',
  238. bindMatrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  239. skeleton: 'skeleton-1',
  240. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  241. layers: 1,
  242. children: [
  243. {
  244. uuid: 'bone-1',
  245. type: 'Bone',
  246. name: 'Parent',
  247. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  248. layers: 1,
  249. children: [
  250. {
  251. uuid: 'bone-2',
  252. type: 'Bone',
  253. name: 'Child',
  254. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ],
  255. layers: 1
  256. }
  257. ]
  258. }
  259. ]
  260. }
  261. ]
  262. }
  263. };
  264. const loader = new ObjectLoader();
  265. const scene = loader.parse( json );
  266. const skinnedMesh = scene.children[ 0 ];
  267. const bone1 = skinnedMesh.skeleton.bones[ 0 ];
  268. const bone2 = skinnedMesh.skeleton.bones[ 1 ];
  269. assert.ok( bone1.isBone, 'First is Bone' );
  270. assert.ok( bone2.isBone, 'Second is Bone' );
  271. assert.strictEqual( bone2.parent, bone1, 'Child bone parent is first bone' );
  272. assert.strictEqual( bone1.children[ 0 ], bone2, 'First bone has child' );
  273. } );
  274. QUnit.test( 'Bone type', ( assert ) => {
  275. const json = {
  276. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  277. object: {
  278. uuid: 'bone-1',
  279. type: 'Bone',
  280. name: 'TestBone',
  281. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  282. layers: 1
  283. }
  284. };
  285. const loader = new ObjectLoader();
  286. const bone = loader.parse( json );
  287. assert.ok( bone.isBone, 'Is Bone' );
  288. assert.strictEqual( bone.type, 'Bone', 'Type is Bone' );
  289. assert.strictEqual( bone.name, 'TestBone', 'Bone name' );
  290. } );
  291. QUnit.test( 'SkinnedMesh bindMatrix', ( assert ) => {
  292. const json = {
  293. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  294. geometries: [ {
  295. uuid: 'geom-1',
  296. type: 'BufferGeometry',
  297. data: {
  298. attributes: {
  299. position: {
  300. itemSize: 3,
  301. type: 'Float32Array',
  302. array: [ 0, 0, 0, 1, 0, 0, 0, 1, 0 ],
  303. normalized: false
  304. },
  305. skinIndex: {
  306. itemSize: 4,
  307. type: 'Uint16Array',
  308. array: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
  309. normalized: false
  310. },
  311. skinWeight: {
  312. itemSize: 4,
  313. type: 'Float32Array',
  314. array: [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ],
  315. normalized: false
  316. }
  317. }
  318. }
  319. } ],
  320. materials: [ {
  321. uuid: 'mat-1',
  322. type: 'MeshBasicMaterial',
  323. color: 16711680
  324. } ],
  325. skeletons: [ {
  326. uuid: 'skeleton-1',
  327. bones: [ 'bone-1' ],
  328. boneInverses: [
  329. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
  330. ]
  331. } ],
  332. object: {
  333. uuid: 'scene-1',
  334. type: 'Scene',
  335. children: [
  336. {
  337. uuid: 'skinned-1',
  338. type: 'SkinnedMesh',
  339. geometry: 'geom-1',
  340. material: 'mat-1',
  341. bindMode: 'attached',
  342. bindMatrix: [ 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 1, 2, 3, 1 ],
  343. skeleton: 'skeleton-1',
  344. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  345. layers: 1,
  346. children: [
  347. {
  348. uuid: 'bone-1',
  349. type: 'Bone',
  350. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  351. layers: 1
  352. }
  353. ]
  354. }
  355. ]
  356. }
  357. };
  358. const loader = new ObjectLoader();
  359. const scene = loader.parse( json );
  360. const skinnedMesh = scene.children[ 0 ];
  361. const elements = skinnedMesh.bindMatrix.elements;
  362. assert.strictEqual( elements[ 0 ], 2, 'bindMatrix scale x' );
  363. assert.strictEqual( elements[ 12 ], 1, 'bindMatrix translate x' );
  364. assert.strictEqual( elements[ 13 ], 2, 'bindMatrix translate y' );
  365. assert.strictEqual( elements[ 14 ], 3, 'bindMatrix translate z' );
  366. } );
  367. QUnit.test( 'Skeleton boneInverses', ( assert ) => {
  368. const json = {
  369. metadata: { version: 4.7, type: 'Object', generator: 'Object3D.toJSON' },
  370. geometries: [ {
  371. uuid: 'geom-1',
  372. type: 'BufferGeometry',
  373. data: {
  374. attributes: {
  375. position: {
  376. itemSize: 3,
  377. type: 'Float32Array',
  378. array: [ 0, 0, 0, 1, 0, 0, 0, 1, 0 ],
  379. normalized: false
  380. },
  381. skinIndex: {
  382. itemSize: 4,
  383. type: 'Uint16Array',
  384. array: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
  385. normalized: false
  386. },
  387. skinWeight: {
  388. itemSize: 4,
  389. type: 'Float32Array',
  390. array: [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ],
  391. normalized: false
  392. }
  393. }
  394. }
  395. } ],
  396. materials: [ {
  397. uuid: 'mat-1',
  398. type: 'MeshBasicMaterial',
  399. color: 16711680
  400. } ],
  401. skeletons: [ {
  402. uuid: 'skeleton-1',
  403. bones: [ 'bone-1', 'bone-2' ],
  404. boneInverses: [
  405. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  406. [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, - 5, 0, 1 ]
  407. ]
  408. } ],
  409. object: {
  410. uuid: 'scene-1',
  411. type: 'Scene',
  412. children: [
  413. {
  414. uuid: 'skinned-1',
  415. type: 'SkinnedMesh',
  416. geometry: 'geom-1',
  417. material: 'mat-1',
  418. bindMode: 'attached',
  419. bindMatrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  420. skeleton: 'skeleton-1',
  421. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  422. layers: 1,
  423. children: [
  424. {
  425. uuid: 'bone-1',
  426. type: 'Bone',
  427. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  428. layers: 1,
  429. children: [
  430. {
  431. uuid: 'bone-2',
  432. type: 'Bone',
  433. matrix: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 5, 0, 1 ],
  434. layers: 1
  435. }
  436. ]
  437. }
  438. ]
  439. }
  440. ]
  441. }
  442. };
  443. const loader = new ObjectLoader();
  444. const scene = loader.parse( json );
  445. const skinnedMesh = scene.children[ 0 ];
  446. assert.strictEqual( skinnedMesh.skeleton.boneInverses.length, 2, 'Has 2 boneInverses' );
  447. assert.strictEqual( skinnedMesh.skeleton.boneInverses[ 1 ].elements[ 13 ], - 5, 'Second boneInverse translate y' );
  448. } );
  449. } );
  450. } );
粤ICP备19079148号