|
26 | 26 | </details> |
27 | 27 | </header> |
28 | 28 |
|
| 29 | + <script type="importmap"> |
| 30 | + { |
| 31 | + "imports": { |
| 32 | + "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js", |
| 33 | + "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/" |
| 34 | + } |
| 35 | + } |
| 36 | + </script> |
| 37 | + |
29 | 38 | <script type="module"> |
30 | 39 | // Code adapted from three.js' WebXR hit test sample. |
31 | 40 | // three.js is covered by MIT license which can be found at: |
32 | 41 | // https://github.com/mrdoob/three.js/blob/master/LICENSE |
33 | 42 |
|
34 | 43 |
|
35 | | - import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js'; |
| 44 | + import * as THREE from 'three'; |
| 45 | + import {BoxLineGeometry} from 'three/addons/geometries/BoxLineGeometry.js'; |
36 | 46 | import {WebXRButton} from '../js/util/webxr-button.js'; |
37 | 47 | import {hitTest, filterHitTestResults} from '../js/hit-test.js'; |
38 | 48 |
|
39 | 49 | let xrButton = null; |
40 | 50 | let camera, scene, renderer; |
41 | | - let room, spheres; |
| 51 | + let room, spheres, skeleton; |
42 | 52 | const scalehand = new THREE.Matrix4().makeScale(3, 3, 3); |
43 | 53 |
|
| 54 | + const jointHierarchy = { |
| 55 | + 'spine-lower': 'spine-middle', |
| 56 | + 'spine-middle': 'spine-upper', |
| 57 | + 'spine-upper' : 'chest', |
| 58 | + |
| 59 | + 'left-upper-leg': 'hips', |
| 60 | + 'left-lower-leg': 'left-upper-leg', |
| 61 | + |
| 62 | + 'right-upper-leg': 'hips', |
| 63 | + 'right-lower-leg': 'left-upper-leg', |
| 64 | + |
| 65 | + 'left-arm-lower': 'left-arm-upper', |
| 66 | + 'left-arm-upper': 'left-shoulder', |
| 67 | + 'left-scapula': 'chest', |
| 68 | + |
| 69 | + 'right-arm-lower': 'right-arm-upper', |
| 70 | + 'right-arm-upper': 'right-shoulder', |
| 71 | + 'right-scapula': 'chest' |
| 72 | + }; |
| 73 | + |
44 | 74 | init(); |
45 | 75 |
|
46 | | - function init() { |
| 76 | + function dist(p) { |
| 77 | + return Math.sqrt(p.x*p.x+p.y*p.y+p.z*p.z); |
| 78 | + } |
| 79 | + |
| 80 | + function init(){ |
47 | 81 | scene = new THREE.Scene(); |
48 | | - scene.background = new THREE.Color( 0x505050 ); |
| 82 | + scene.background = new THREE.Color(0x505050); |
49 | 83 |
|
50 | | - camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 50 ); |
| 84 | + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 50); |
51 | 85 |
|
52 | 86 | const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1); |
53 | 87 | light.position.set(0.5, 1, 0.25); |
54 | 88 | scene.add(light); |
55 | | - |
56 | | - renderer = new THREE.WebGLRenderer( { antialias: true } ); |
57 | | - renderer.setPixelRatio( window.devicePixelRatio ); |
58 | | - renderer.setSize( window.innerWidth, window.innerHeight ); |
59 | | - renderer.setAnimationLoop( render ); |
| 89 | +/* |
| 90 | + room = new THREE.LineSegments( |
| 91 | + new BoxLineGeometry( 6, 6, 6, 10, 10, 10 ), |
| 92 | + new THREE.LineBasicMaterial( { color: 0x808080 } ) |
| 93 | + ); |
| 94 | + room.geometry.translate( 0, 3, 0 ); |
| 95 | + scene.add( room ); |
| 96 | +*/ |
| 97 | + renderer = new THREE.WebGLRenderer({ antialias: true }); |
| 98 | + renderer.setPixelRatio(window.devicePixelRatio); |
| 99 | + renderer.setSize(window.innerWidth, window.innerHeight); |
| 100 | + renderer.setAnimationLoop(render); |
60 | 101 | renderer.xr.enabled = true; |
61 | 102 | renderer.autoClear = false; |
62 | | - document.body.appendChild( renderer.domElement ); |
| 103 | + document.body.appendChild(renderer.domElement); |
63 | 104 |
|
64 | 105 | xrButton = new WebXRButton({ |
65 | 106 | onRequestSession: onRequestSession, |
|
71 | 112 |
|
72 | 113 | document.querySelector('header').appendChild(xrButton.domElement); |
73 | 114 |
|
74 | | - if (navigator.xr) { |
| 115 | + if (navigator.xr){ |
75 | 116 | navigator.xr.isSessionSupported('immersive-ar') |
76 | 117 | .then((supported) => { |
77 | 118 | xrButton.enabled = supported; |
78 | 119 | }); |
79 | 120 | } |
80 | 121 |
|
81 | | - window.addEventListener( 'resize', onWindowResize ); |
| 122 | + window.addEventListener('resize', onWindowResize); |
82 | 123 |
|
83 | 124 | } |
84 | 125 |
|
85 | | - function onRequestSession() { |
| 126 | + function onRequestSession(){ |
86 | 127 | let sessionInit = { |
87 | 128 | requiredFeatures: ['body-tracking'], |
88 | | - optionalFeatures: ['local-floor', 'bounded-floor', 'unbounded'], |
| 129 | + optionalFeatures: ['local-floor', 'bounded-floor'], |
89 | 130 | }; |
90 | 131 | navigator.xr.requestSession('immersive-ar', sessionInit).then((session) => { |
91 | 132 | session.mode = 'immersive-ar'; |
|
94 | 135 | }); |
95 | 136 | } |
96 | 137 |
|
97 | | - function onSessionStarted(session) { |
| 138 | + function onSessionStarted(session){ |
98 | 139 | session.addEventListener('end', onSessionEnded); |
99 | 140 |
|
100 | 141 | renderer.xr.setSession(session); |
101 | 142 |
|
102 | 143 | renderer.setAnimationLoop(render); |
103 | 144 | } |
104 | 145 |
|
105 | | - function onEndSession(session) { |
| 146 | + function onEndSession(session){ |
106 | 147 | session.end(); |
107 | 148 | } |
108 | 149 |
|
109 | | - function onSessionEnded(event) { |
| 150 | + function onSessionEnded(event){ |
110 | 151 | xrButton.setSession(null); |
111 | 152 |
|
112 | 153 | renderer.setAnimationLoop(null); |
113 | 154 | } |
114 | 155 |
|
115 | | - function onWindowResize() { |
| 156 | + function onWindowResize(){ |
116 | 157 | camera.aspect = window.innerWidth / window.innerHeight; |
117 | 158 | camera.updateProjectionMatrix(); |
118 | 159 |
|
119 | 160 | renderer.setSize(window.innerWidth, window.innerHeight); |
120 | 161 | } |
121 | 162 |
|
| 163 | + function render(timestamp, frame){ |
| 164 | + if (frame){ |
122 | 165 |
|
123 | | - function render(timestamp, frame) { |
124 | | - if (frame) { |
125 | | - |
126 | | - if ( frame.body ) { |
| 166 | + if (frame.body){ |
127 | 167 |
|
128 | 168 | let body = frame.body; |
129 | 169 |
|
130 | | - if (spheres == undefined) { |
131 | | - |
132 | | - const geometry = new THREE.IcosahedronGeometry( 0.01, 3 ); |
| 170 | + if (spheres == undefined){ |
| 171 | + const geometry = new THREE.IcosahedronGeometry(0.01, 3); |
133 | 172 | const material = new THREE.MeshLambertMaterial(); |
134 | 173 |
|
135 | | - spheres = new THREE.InstancedMesh( geometry, material, body.size ); |
136 | | - spheres.translateZ( -1 ).setRotationFromMatrix (new THREE.Matrix4().makeRotationY(Math.PI)); |
137 | | - spheres.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame |
138 | | - scene.add( spheres ); |
| 174 | + spheres = new THREE.InstancedMesh(geometry, material, body.size); |
| 175 | + spheres.translateZ(-1).setRotationFromMatrix (new THREE.Matrix4().makeRotationY(Math.PI)); |
| 176 | + spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame |
| 177 | + scene.add(spheres); |
139 | 178 |
|
140 | 179 | const color = new THREE.Color(); |
141 | 180 |
|
142 | | - for ( let i = 0; i < spheres.count; i ++ ) { |
143 | | - |
144 | | - spheres.setColorAt( i, color.setHex( 0xffffff * Math.random() ) ); |
145 | | - |
| 181 | + for (let i = 0; i < spheres.count; i ++){ |
| 182 | + spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); |
146 | 183 | } |
147 | 184 |
|
148 | | - spheres.instanceMatrix.needsUpdate = true; |
| 185 | + const boxgeometry = new THREE.BoxGeometry(1, 1, 1); |
| 186 | + boxgeometry.translate( -.5, 0, 0); |
| 187 | + const boxmaterial = new THREE.MeshBasicMaterial({color: 0xffffff}); |
| 188 | + skeleton = new THREE.InstancedMesh(boxgeometry, boxmaterial, Object.keys(jointHierarchy).length); |
| 189 | + skeleton.translateZ(-1).setRotationFromMatrix (new THREE.Matrix4().makeRotationY(Math.PI)); |
| 190 | + skeleton.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame |
| 191 | + scene.add(skeleton); |
149 | 192 |
|
| 193 | + spheres.instanceMatrix.needsUpdate = true; |
| 194 | + skeleton.instanceMatrix.needsUpdate = true; |
150 | 195 | } |
151 | 196 |
|
152 | 197 | let i = 0; |
153 | 198 | const matrix = new THREE.Matrix4(); |
154 | 199 | let space = renderer.xr.getReferenceSpace(); |
155 | 200 | body.forEach(part => { |
156 | | - |
157 | 201 | const pose = frame.getPose(part, space); |
158 | 202 | const position = pose.transform.position; |
159 | 203 |
|
160 | | - if (!part.jointName.includes("hand")) { |
| 204 | + if (!part.jointName.includes("hand")){ |
161 | 205 | matrix.copy(scalehand); |
162 | 206 | } else { |
163 | 207 | matrix.identity(); |
164 | 208 | } |
165 | 209 |
|
166 | | - matrix.setPosition( -position.x, position.y, position.z ); |
167 | | - spheres.setMatrixAt( i++, matrix ); |
168 | | - |
| 210 | + matrix.setPosition(position.x, position.y, position.z); |
| 211 | + spheres.setMatrixAt(i++, matrix); |
169 | 212 | }); |
170 | 213 |
|
171 | | - spheres.instanceMatrix.needsUpdate = true; |
| 214 | + i = 0; |
| 215 | + for (const [key, value] of Object.entries(jointHierarchy)) { |
| 216 | + let distance = dist(frame.getPose(body.get(key), body.get(value)).transform.position); |
| 217 | + if (key.includes('right')) { |
| 218 | + distance = -distance; |
| 219 | + } |
| 220 | + if (key.includes('scapula')) { |
| 221 | + distance = -distance; |
| 222 | + } |
| 223 | + const pose = frame.getPose(body.get(key), space); |
| 224 | + const position = pose.transform.position; |
| 225 | + const orientation = pose.transform.orientation; |
| 226 | + |
| 227 | + matrix.compose( |
| 228 | + new THREE.Vector3(position.x, position.y, position.z), |
| 229 | + new THREE.Quaternion(orientation.x, orientation.y, orientation.z, orientation.w), |
| 230 | + new THREE.Vector3(distance, 0.02, 0.02) |
| 231 | + ); |
172 | 232 |
|
| 233 | + skeleton.setMatrixAt(i++, matrix); |
173 | 234 | } |
174 | 235 |
|
| 236 | + spheres.instanceMatrix.needsUpdate = true; |
| 237 | + skeleton.instanceMatrix.needsUpdate = true; |
| 238 | + } |
175 | 239 | renderer.render(scene, camera); |
176 | 240 | } |
177 | 241 | } |
|
0 commit comments