|
352 | 352 | subtree: true, |
353 | 353 | }); |
354 | 354 |
|
| 355 | +class RiggedModel { |
| 356 | + constructor(avatarMesh) { |
| 357 | + this.avatarMesh = avatarMesh; |
| 358 | + } |
| 359 | + setState(hmd, gamepads) { |
| 360 | + this.avatarMesh.position.fromArray(hmd.position).sub(localVector.set(0, 1.5, 0)); |
| 361 | + localQuaternion.fromArray(hmd.quaternion); |
| 362 | + localEuler.setFromQuaternion(localQuaternion, localEuler.order); |
| 363 | + localEuler.y += Math.PI; |
| 364 | + localEuler.x *= -1; |
| 365 | + this.avatarMesh.quaternion.setFromEuler(localEuler); |
| 366 | + } |
| 367 | + update() { |
| 368 | + // console.log('per-frame update called'); |
| 369 | + } |
| 370 | +} |
355 | 371 | const _makePeerConnection = peerConnectionId => { |
356 | 372 | const peerConnectionConfig = { |
357 | 373 | iceServers: [ |
|
365 | 381 | console.log('got track', e); |
366 | 382 | }; |
367 | 383 |
|
368 | | - let skinMesh = null; |
| 384 | + let avatarMesh = null; |
369 | 385 | const sendChannel = peerConnection.createDataChannel('sendChannel'); |
370 | 386 | peerConnection.sendChannel = sendChannel; |
371 | 387 | let pingInterval = 0; |
372 | 388 | let updateInterval = 0; |
373 | 389 | sendChannel.onopen = () => { |
374 | 390 | // console.log('data channel local open'); |
375 | 391 |
|
376 | | - skinMesh = _makeSkinMesh(); |
377 | | - skinMesh.setSkinUrl(DEFAULT_SKIN_URL); |
378 | | - skinMeshes.push(skinMesh); |
| 392 | + avatarMesh = new THREE.Object3D(); |
| 393 | + avatarMesh.riggedModel = null; |
| 394 | + const loader = new THREE.GLTFLoader(); |
| 395 | + loader.load('https://modulesio.github.io/models/miku.glb', object => { |
| 396 | + if (avatarMesh) { |
| 397 | + avatarMesh.riggedModel = new RiggedModel(object.scene); |
| 398 | + avatarMesh.add(object.scene); |
| 399 | + } |
| 400 | + }, xhr => {}, err => { |
| 401 | + console.warn(err); |
| 402 | + }); |
| 403 | + scene.add(avatarMesh); |
| 404 | + avatarMeshes.push(avatarMesh); |
379 | 405 |
|
380 | 406 | peerConnection.open = true; |
381 | 407 |
|
|
459 | 485 | const data = JSON.parse(e.data); |
460 | 486 | const {method} = data; |
461 | 487 | if (method === 'pose') { |
462 | | - if (skinMesh) { |
| 488 | + if (avatarMesh && avatarMesh.riggedModel) { |
463 | 489 | const {hmd, gamepads} = data; |
464 | | - skinMesh.setState(hmd, gamepads); |
| 490 | + avatarMesh.riggedModel.setState(hmd, gamepads); |
465 | 491 | } |
466 | 492 | } else if (landState && method === 'initState') { |
467 | 493 | const {state} = data; |
|
507 | 533 | if (index !== -1) { |
508 | 534 | peerConnections.splice(index, 1); |
509 | 535 | } |
510 | | - if (skinMesh) { |
511 | | - skinMesh.destroy(); |
512 | | - skinMeshes.splice(skinMeshes.indexOf(skinMesh), 1); |
513 | | - skinMesh = null; |
| 536 | + if (avatarMesh) { |
| 537 | + scene.remove(avatarMesh); |
| 538 | + avatarMeshes.splice(avatarMeshes.indexOf(avatarMesh), 1); |
| 539 | + avatarMesh = null; |
514 | 540 | } |
515 | 541 | if (landState) { |
516 | 542 | // remove owned xr-iframes |
|
6095 | 6121 | }; |
6096 | 6122 | _updateSceneMeshes(); */ |
6097 | 6123 |
|
6098 | | - const _updateSkinMeshes = () => { |
6099 | | - for (let i = 0; i < skinMeshes.length; i++) { |
6100 | | - skinMeshes[i].update(); |
| 6124 | + const _updateAvatarMeshes = () => { |
| 6125 | + for (let i = 0; i < avatarMeshes.length; i++) { |
| 6126 | + const avatarMesh = avatarMeshes[i]; |
| 6127 | + avatarMesh.riggedModel && avatarMesh.riggedModel.update(); |
6101 | 6128 | } |
6102 | 6129 | }; |
6103 | | - _updateSkinMeshes(); |
| 6130 | + _updateAvatarMeshes(); |
6104 | 6131 |
|
6105 | 6132 | const _handleTrigger = (gamepad, i, pressed, lastPressed) => { |
6106 | 6133 | const start = pressed && !lastPressed; |
|
6870 | 6897 | console.warn(err.stack); |
6871 | 6898 | }); |
6872 | 6899 |
|
6873 | | -const skinMeshes = []; |
| 6900 | +const avatarMeshes = []; |
6874 | 6901 | const _mod = (v, d) => { |
6875 | 6902 | const n = v % d; |
6876 | 6903 | return n < 0 ? (d + n) : n; |
6877 | 6904 | }; |
6878 | 6905 | const _angleDiff = (a, b) => _mod((b - a) + Math.PI, Math.PI * 2) - Math.PI; |
6879 | | -const _makeSkinMesh = () => { |
6880 | | - const object = {}; |
6881 | | - let onsetstate = null; |
6882 | | - const uniforms = THREE.UniformsUtils.clone(skin.SKIN_SHADER.uniforms); |
6883 | | - object.setSkinUrl = skinUrl => { |
6884 | | - if (skinUrl) { |
6885 | | - const mesh = skin({ |
6886 | | - limbs: true, |
6887 | | - }); |
6888 | | - mesh.frustumCulled = false; |
6889 | | - |
6890 | | - mesh.onBeforeRender = (onBeforeRender => function() { |
6891 | | - mesh.material.uniforms.headRotation.value.copy(uniforms.headRotation.value); |
6892 | | - mesh.material.uniforms.leftArmRotation.value.copy(uniforms.leftArmRotation.value); |
6893 | | - mesh.material.uniforms.rightArmRotation.value.copy(uniforms.rightArmRotation.value); |
6894 | | - mesh.material.uniforms.theta.value = uniforms.theta.value; |
6895 | | - mesh.material.uniforms.headVisible.value = uniforms.headVisible.value; |
6896 | | - mesh.material.uniforms.hit.value = uniforms.hit.value; |
6897 | | - |
6898 | | - onBeforeRender.apply(this, arguments); |
6899 | | - })(mesh.onBeforeRender); |
6900 | | - |
6901 | | - return new Promise((accept, reject) => { |
6902 | | - const skinImg = new Image(); |
6903 | | - skinImg.crossOrigin = 'Anonymous'; |
6904 | | - skinImg.src = skinUrl; |
6905 | | - skinImg.onload = () => { |
6906 | | - accept(skinImg); |
6907 | | - }; |
6908 | | - skinImg.onerror = err => { |
6909 | | - reject(err); |
6910 | | - }; |
6911 | | - }) |
6912 | | - .then(skinImg => { |
6913 | | - mesh.setImage(skinImg); |
6914 | | - |
6915 | | - onsetstate = (hmd, gamepads) => { |
6916 | | - const hmdPosition = localVector.fromArray(hmd.position); |
6917 | | - const hmdQuaternion = localQuaternion.fromArray(hmd.quaternion); |
6918 | | - |
6919 | | - const hmdEuler = localEuler.setFromQuaternion(hmdQuaternion, localEuler.order); |
6920 | | - const playerEuler = localEuler2.setFromQuaternion(mesh.quaternion, localEuler2.order); |
6921 | | - const angleDiff = _angleDiff(hmdEuler.y, playerEuler.y); |
6922 | | - const angleDiffAbs = Math.abs(angleDiff); |
6923 | | - if (angleDiffAbs > Math.PI / 2) { |
6924 | | - playerEuler.y += (angleDiffAbs - (Math.PI / 2)) * (angleDiff < 0 ? 1 : -1); |
6925 | | - mesh.quaternion.setFromEuler(playerEuler); |
6926 | | - } |
6927 | | - |
6928 | | - const oldWorldPosition = mesh.getWorldPosition(localVector2); |
6929 | | - mesh.position.copy(hmdPosition) |
6930 | | - .sub(mesh.eye.getWorldPosition(localVector3)) |
6931 | | - .add(oldWorldPosition); |
6932 | | - |
6933 | | - const playerQuaternionInverse = localQuaternion2.copy(mesh.quaternion).inverse(); |
6934 | | - mesh.head.quaternion.copy(playerQuaternionInverse).multiply(hmdQuaternion); |
6935 | | - mesh.updateMatrixWorld(); |
6936 | | - |
6937 | | - const headQuaternionInverse = localQuaternion3.copy(mesh.head.quaternion).inverse(); |
6938 | | - uniforms.headRotation.value.set(headQuaternionInverse.x, headQuaternionInverse.y, headQuaternionInverse.z, headQuaternionInverse.w); |
6939 | | - |
6940 | | - for (let i = 0; i < 2; i++) { |
6941 | | - const armRotation = uniforms[i === 0 ? 'leftArmRotation' : 'rightArmRotation']; |
6942 | | - |
6943 | | - const gamepad = gamepads[i]; |
6944 | | - if (gamepad.visible) { |
6945 | | - const gamepadPosition = localVector.fromArray(gamepad.position); |
6946 | | - const gamepadQuaternion = localQuaternion.fromArray(gamepad.quaternion); |
6947 | | - |
6948 | | - const armQuaternionInverse = localQuaternion3.setFromRotationMatrix( |
6949 | | - localMatrix.lookAt( |
6950 | | - mesh.arms[i === 0 ? 'left' : 'right'] |
6951 | | - .getWorldPosition(localVector2), |
6952 | | - gamepadPosition, |
6953 | | - localVector3 |
6954 | | - .set(0, 1, 0) |
6955 | | - .applyQuaternion(gamepadQuaternion) |
6956 | | - ) |
6957 | | - ) |
6958 | | - .multiply(armQuaternionOffset) |
6959 | | - .premultiply(playerQuaternionInverse) |
6960 | | - .inverse(); |
6961 | | - armRotation.value.set(armQuaternionInverse.x, armQuaternionInverse.y, armQuaternionInverse.z, armQuaternionInverse.w); |
6962 | | - // console.log('arm rotation', i, armRotation.value.toArray().join(',')); |
6963 | | - } else { |
6964 | | - armRotation.value.set(0, 0, 0, 1); |
6965 | | - } |
6966 | | - } |
6967 | | - }; |
6968 | | - |
6969 | | - if (object.skinMesh) { |
6970 | | - object.skinMesh.parent.remove(object.skinMesh); |
6971 | | - object.skinMesh = null; |
6972 | | - } |
6973 | | - |
6974 | | - scene.add(mesh); |
6975 | | - object.skinMesh = mesh; |
6976 | | - }); |
6977 | | - } else { |
6978 | | - onsetstate = null; |
6979 | | - |
6980 | | - if (object.skinMesh) { |
6981 | | - object.skinMesh.parent.remove(object.skinMesh); |
6982 | | - object.skinMesh = null; |
6983 | | - } |
6984 | | - } |
6985 | | - }; |
6986 | | - object.setState = (hmd, gamepads) => { |
6987 | | - onsetstate && onsetstate(hmd, gamepads); |
6988 | | - }; |
6989 | | - object.animate = thetaValue => { |
6990 | | - uniforms.theta.value = thetaValue; |
6991 | | - }; |
6992 | | - object.update = () => { |
6993 | | - // XXX |
6994 | | - }; |
6995 | | - object.destroy = () => { |
6996 | | - onsetstate = null; |
6997 | | - |
6998 | | - if (object.skinMesh) { |
6999 | | - object.skinMesh.parent.remove(object.skinMesh); |
7000 | | - object.skinMesh = null; |
7001 | | - } |
7002 | | - }; |
7003 | | - object.skinMesh = null; |
7004 | | - return object; |
7005 | | -}; |
7006 | 6906 |
|
7007 | 6907 | scene.add(defaultGltf); |
7008 | 6908 | const _loadDefaultGltf = () => { |
|
0 commit comments