-
Notifications
You must be signed in to change notification settings - Fork 20
/
index.html
276 lines (248 loc) · 18.6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
<html>
<head>
<script>
// WebXR requires https: to work so ensure redirected if needed.
if (location.hostname !== 'localhost' && window.location.protocol === 'http:') window.location.protocol = 'https:';
</script>
<!-- the AFrame library and 3rd party components -->
<script src="https://cdn.jsdelivr.net/npm/aframe@1.6.0/dist/aframe-master.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-environment-component@1.3.7/dist/aframe-environment-component.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.0/dist/components/sphere-collider.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.0/dist/aframe-extras.controls.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/c-frame/physx@v0.1.2/dist/physx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-blink-controls@0.4.3/dist/aframe-blink-controls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/handy-work@3.1.10/build/handy-controls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/handy-work@3.1.10/build/magnet-helpers.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-htmlmesh@2.2.0/build/aframe-html.min.js"></script>
<script src="ar-shadow-helper.js"></script>
<script src="ar-cursor.js"></script>
<script src="simple-navmesh-constraint.js"></script>
<script src="model-utils.js"></script>
<!-- Our custom behaviour -->
<script src="main.js"></script>
<title>AFrame Handy Demo</title>
<meta property="og:title" content="AFrame Handy Work Demo" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@AdaRoseCannon" />
<meta name="twitter:creator" content="@AdaRoseCannon" />
<meta property="og:url" content="https://aframe-xr-starterkit.glitch.me/" />
<meta name="description" content="A sample scene for working with WebXR Hand Tracking" />
<meta property="og:description" content="A sample scene for working with WebXR Hand Tracking" />
<meta property="og:image" content="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/snapshot.png?v=1645201177438" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<a-scene
physx="autoLoad: true; delay: 1000; wasmUrl: https://cdn.jsdelivr.net/gh/c-frame/physx@v0.1.2/wasm/physx.release.wasm; useDefaultScene: false;"
webxr="overlayElement:#dom-overlay;"
background="color:skyblue;"
reflection="directionalLight:#dirlight;"
renderer="alpha:true;physicallyCorrectLights:true;colorManagement:true;exposure:2;toneMapping:ACESFilmic;"
ar-hit-test="target:#my-ar-objects;type:footprint;footprintDepth:0.2;"
shadow="type: pcfsoft"
gltf-model="dracoDecoderPath: https://www.gstatic.com/draco/versioned/decoders/1.5.7/;"
ar-cursor raycaster="objects: #my-ar-objects a-sphere"
xr-mode-ui="XRMode:xr"
>
<a-assets>
<a-asset-item id="building-glb" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/venue.glb?v=1644331843500"></a-asset-item>
<a-asset-item id="navmesh-glb" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/navmesh.glb?v=1644329586500"></a-asset-item>
<a-asset-item id="right-gltf" src="https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/skeleton-right-hand-webxr/model.gltf"></a-asset-item>
<a-asset-item id="left-gltf" src="https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/skeleton-left-hand-webxr/model.gltf"></a-asset-item>
<a-asset-item id="watch-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/watch.glb?v=1645016979219"></a-asset-item>
<a-asset-item id="sword-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/katana.glb?v=1648465043810"></a-asset-item>
<a-asset-item id="watergun-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/watergun.glb?v=1646916260646"></a-asset-item>
<a-asset-item id="stew-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/model.gltf?v=1690887932932"></a-asset-item>
<a-asset-item id="table-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/small_wooden_table_01_1k-v1.glb?v=1647263187998"></a-asset-item>
<a-asset-item id="clock-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/vintage_grandfather_clock_01_1k-v2.glb?v=1647265174189"></a-asset-item>
<a-asset-item id="ladder-gltf" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/ladder.glb?v=1648465045608"></a-asset-item>
<img id="bake" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/Bake(3).webp?v=1644331344700" crossorigin="anonymous">
<a-mixin id="animations" animation__click="property: components.material.material.color; type: color; to: blue; startEvents: click; dur: 500;"></a-mixin>
<a-mixin id="blink" blink-controls="rotateOnTeleport:false;cameraRig: #cameraRig; teleportOrigin: #head; collisionEntities:.navmesh;"></a-mixin>
<a-mixin id="handle-visual" geometry="width:0.05;height:0.05;depth:0.2"></a-mixin>
</a-assets>
<a-entity
id="cameraRig"
simple-navmesh-constraint="navmesh:.navmesh;fall:0.5;height:0;exclude:.navmesh-hole;"
movement-controls="speed:0.15;camera:#head;"
position="-1 0 1" rotation="0 45 0" origin-on-ar-start
>
<!-- camera -->
<a-entity id="head"
camera="near:0.01;"
look-controls="pointerLockEnabled: false"
position="0 1.65 0"
></a-entity>
<a-entity xr-follow>
<a-gltf-model
id="sword" src="#sword-gltf" shadow="receive:false;"
data-pick-up class="magnet-left magnet-right"
position="-0.2 -0.4 0" rotation="-30 180 0" scale="0.6,0.6,1"
animation__restore_position="startEvents:putdown;pauseEvents:pickup;property:position;to:-0.2 -0.4 0;easing:easeOutBack;"
animation__restore_rotation="startEvents:putdown;pauseEvents:pickup;property:rotation;to:-30 180 0;easing:easeOutBack;"
>
<a-box physx-body="type: kinematic;" width="0.03" height="0.03" depth="0.790" rotation="-16 0 0" position="0 -0.062 -0.331" visible="false"></a-box>
</a-gltf-model>
<a-gltf-model
shadow="receive:false;" id="watergun" src="#watergun-gltf"
physx-body-from-model="type: kinematic;"
class="magnet-left magnet-right" data-pick-up
position="0.2 -0.4 0" rotation="30 180 0"
linear-constraint="axis:0 1 0;min:-0.15;max:0;part:Slider;"
animation__restore_position="startEvents:putdown;pauseEvents:pickup;property:position;to:0.2 -0.4 0;easing:easeOutBack;"
animation__restore_rotation="startEvents:putdown;pauseEvents:pickup;property:rotation;to:30 180 0;easing:easeOutBack;"
>
<a-entity id="watergun-slider-magnet" rotation="-74 0 0" attach-to-model="Slider"></a-entity>
</a-gltf-model>
</a-entity>
<!-- Hand tracking -->
<a-entity handy-controls="right:#right-gltf;materialOverride:right;" material="color:gold;metalness:1;roughness:0;">
<!-- For screen space inputs like mobile AR -->
<a-torus radius="0.008" radius-tubular="0.001" material="shader:flat;color:blue" data-none="screen-0"></a-torus>
<a-torus radius="0.008" radius-tubular="0.001" material="shader:flat;color:green" data-none="screen-1"></a-torus>
<a-torus radius="0.008" radius-tubular="0.001" material="shader:flat;color:red" data-none="screen-2"></a-torus>
<!-- Put an exit button on the wrist for handtracking -->
<a-gltf-model src="#watch-gltf" data-left="wrist" position="-1000 0 0">
<a-sphere radius="0.02" position="0 0.02 0" sphere-collider="radius:0.02;objects:[data-right$=-tip];" exit-on="hitend" visible="false"></a-sphere>
</a-gltf-model>
<!-- Add a golden ring on the finger -->
<a-entity data-left="ring-finger-phalanx-proximal">
<a-torus position="0 0 -0.03" radius="0.008" radius-tubular="0.001" scale="1 1 1.5" material="color:gold;metalness:1;roughness:0;"></a-torus>
</a-entity>
<!-- Use the finger tips for teleporting when the user points -->
<a-entity data-right="index-finger-tip" mixin="blink" blink-controls="snapTurn:false;startEvents:pose_point_fuseShort;endEvents:pose_point_fuseLong;cancelEvents:pose_cancel_point;"></a-entity>
<a-entity data-left="index-finger-tip" mixin="blink" blink-controls="snapTurn:false;startEvents:pose_point_fuseShort;endEvents:pose_point_fuseLong;cancelEvents:pose_cancel_point;"></a-entity>
<!-- The direction hands are facing, we will also attach labels to show the currently detected pose or controller button -->
<!-- These also do teleportaion for Blink controls in VR -->
<a-entity data-right="ray" mixin="blink" cursor="" raycaster="objects:[html];far:0.3;showLine:false;lineColor:black;">
<a-entity position="0 0 -0.22" visible="false" class="pose-label" text="value: Hello World; align: center;"></a-entity>
</a-entity>
<a-entity data-left="ray" mixin="blink" cursor="" raycaster="objects:[html];far:0.3;showLine:false;lineColor:black;">
<a-entity position="0 0 -0.22" visible="false" class="pose-label" text="value: Hello World; align: center;"></a-entity>
</a-entity>
<!-- These get drawn towards grabable objects, moving the whole hand and the attached elements-->
<a-entity id="left-magnet" data-left="grip" data-magnet="magnet-left" grab-magnet-target="startEvents:squeezestart,pose_fist;stopEvents:pose_flat_fuseShort,squeezeend;noMagnetEl:#left-no-magnet;"></a-entity>
<a-entity id="right-magnet" data-right="grip" data-magnet="magnet-right" grab-magnet-target="startEvents:squeezestart,pose_fist;stopEvents:pose_flat_fuseShort,squeezeend;noMagnetEl:#right-no-magnet;"></a-entity>
<!-- markers to let us know the real location of the hands, you probably want to make them visible="false" or just make them empty <a-entities> -->
<a-entity id="left-no-magnet" data-left="grip" data-no-magnet>
<a-entity html="html:#my-interface;cursor:#cursor" position="-0.142 -0.0166 -0.02928" rotation="-80 90 0" scale="0.7 0.7 0.7"></a-entity>
</a-entity>
<a-entity id="right-no-magnet" data-right="grip" data-no-magnet></a-entity>
<!-- Invisible objects at the tips of each finger for physics or intersections -->
<a-sphere data-right="index-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-right="middle-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-right="ring-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-right="pinky-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-right="thumb-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-left="index-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-left="middle-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-left="ring-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-left="pinky-finger-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
<a-sphere data-left="thumb-tip" radius="0.004" visible="false" physx-body="type: kinematic;"></a-sphere>
</a-entity>
</a-entity>
<a-entity id="my-ar-objects" position="-6 0 1">
<!-- "Dusty Piano" (https://skfb.ly/66EPx) by Vincent074 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). -->
<a-gltf-model id="piano" rotation="0 100 0" shadow="receive:false;cast:true;" src="https://cdn.glitch.global/d29f98b4-ddd1-4589-8b66-e2446690e697/piano.glb?v=1644414775118">
<a-plane rotation="-90 0 0" width="1.5" height="0.6" class="navmesh-hole" visible="false"></a-plane>
</a-gltf-model>
</a-entity>
<!-- This plane is only visible in AR and follows the given target to provide it with shadows.-->
<a-light id="dirlight" shadow-camera-automatic="[ar-shadow-helper],#table,#ladder" intensity="0.8" light="castShadow:true;type:directional" position="0 3 -6"></a-light>
<a-entity ar-shadow-helper="target:#my-ar-objects;light:#dirlight;" visible="false">
<a-plane rotation="-90 0 0" shadow="cast:false;receive:true;" position="0 0.01 0" material="shader:shadow; depthWrite:false; opacity:0.9;"></a-plane>
</a-entity>
<a-entity hide-on-enter-ar position="0 -0.2 0" environment="lighting:none;shadow:true;preset: osiris;"></a-entity>
<a-entity rotation="0 -50 0" position="0 0 0" hide-on-enter-ar>
<a-box
position="-5.148 -0.1 -0.355"
visible="false"
geometry="width:33.67;height:0.2;depth:19.06"
physx-body="type: static;"
physx-restitution="1.5">
</a-box>
<a-gltf-model id="pot" toggle-physics shadow="receive:false;" src="#stew-gltf" position="-2 1.2 0.8" physx-body-from-model="type:dynamic;mass:2;">
<a-entity id="stew-handle-1" data-magnet-range="0.2,0.1,360,180" data-pick-up="parent" class="magnet-left magnet-right" position="0 0.35 -0.35" rotation="0 90 0"></a-entity>
<a-entity id="stew-handle-2" data-magnet-range="0.2,0.1,360,180" data-pick-up="parent" class="magnet-left magnet-right" position="0 0.35 0.35" rotation="0 90 0"></a-entity>
</a-gltf-model>
<a-gltf-model id="table" shadow="receive:true;" src="#table-gltf" position="-2 0 0.8" rotation="0 51 0" scale="1.5 1.5 1.5" physx-body-from-model="type: static;">
<a-plane rotation="-90 0 0" width="1.2" height="0.6" class="navmesh-hole" visible="false"></a-plane>
</a-gltf-model>
<!-- Button test -->
<a-box position="-1.657 0.893 0.421" width="0.2" height="0.2" depth="0.2" color="grey"
animation__press="startEvents:press;property:components.material.material.color;type:color;to:green;dur:100;"
animation__release="startEvents:release;property:components.material.material.color;type:color;to:grey;dur:100;"
>
<a-entity position="0 0.12 0" linear-constraint="target:[data-no-magnet];axis:0 1 0;min:0;max:0.18;radius:0.1;useFixedValueIfOutOfRange:true;valueIfOutOfRange:0.18;downEventName:press;downEventThreshold:0;upEventName:release;upEventThreshold:0.18;">
<a-cylinder radius="0.09" height="0.2" position="0 -0.1 0" color="hotpink"></a-cylinder>
</a-entity>
</a-box>
<a-gltf-model id="ladder" ladder="grabbables:#ladder-left-hand,#ladder-right-hand;cameraRig:#cameraRig;" shadow src="#ladder-gltf" position="-4.98177 -0.01925 -2.97802" rotation="-4.9623 -62.929 0.6165" physx-body-from-model="type: static;">
<a-plane rotation="-90 0 0" width="1.2" height="0.6" class="navmesh-hole" visible="false"></a-plane>
<a-entity position="-0.25 0.07 0" linear-constraint="target:#left-no-magnet;axis:0 1 0;min:0;max:2.4;step:0.2;">
<a-entity id="ladder-left-hand" data-magnet-range="0.2,0.15,360,300" class="magnet-left" rotation="0 90 0" linear-constraint="target:#left-no-magnet;axis:1 0 0;max:0.5;"></a-entity>
</a-entity>
<a-entity position="-0.25 0.07 0" linear-constraint="target:#right-no-magnet;axis:0 1 0;max:2.5;step:0.2;">
<a-entity id="ladder-right-hand" data-magnet-range="0.2,0.15,360,300" class="magnet-right" rotation="0 -90 0" linear-constraint="target:#right-no-magnet;axis:1 0 0;max:0.5;"></a-entity>
</a-entity>
</a-gltf-model>
<a-gltf-model id="clock" shadow="receive:true;" src="#clock-gltf" position="-5 0 1.8" rotation="0 51 0" physx-body-from-model="type:dynamic;mass:15;"></a-gltf-model>
<a-gltf-model class="navmesh" src="#navmesh-glb" visible="false"></a-gltf-model>
<a-gltf-model src="#building-glb"
id="building"
lightmap="src:#bake;intensity: 1.5; filter:Window,Ceiling,floor;"
depthwrite="true"
window-replace="Glass"
no-tonemapping="Light"
shadow="cast:false;receive:true;"
></a-gltf-model>
</a-entity>
<a-sphere color="black" radius="0.005" id="cursor" material="shader:flat" visible="false"></a-sphere>
</a-scene>
<div id="dom-overlay">
<h1>
Hello World
</h1>
<div class="overlay-footer">
<section style="display: inline-block; background: lavenderblush; color: #333333; border-radius: 1em; padding: 1em; margin:0; accent-color: hotpink;" id="my-interface">
<h2>Settings</h2>
<fieldset style="border:none;">
<legend>Thumbstick Behaviour</legend>
<input onclick="toggleThumbstick(this)" type="radio" id="thumbstick-teleport" name="thumbstick" value="teleport" checked><label for="thumbstick-teleport"> Teleport</label>
<input onclick="toggleThumbstick(this)" type="radio" id="thumbstick-move" name="thumbstick" value="move"><label for="thumbstick-move"> Move</label>
</fieldset>
<button onclick="AFRAME.scenes[0].exitVR()" style="display: block;">Exit Immersive</button>
</section>
<!-- HTML form logic -->
<script>
let movementType = 'teleport';
function toggleThumbstick(detail) {
const rayPointers = ['[data-right="ray"]', '[data-left="ray"]'].map(s => document.querySelector(s));
const type = detail.value;
movementType = type;
if (type === 'move') {
cameraRig.setAttribute('movement-controls', 'enabled', true);
for (const p of rayPointers) p.removeAttribute('mixin');
}
if (type === 'teleport') {
cameraRig.setAttribute('movement-controls', 'enabled', false);
for (const p of rayPointers) p.setAttribute('mixin', 'blink');
}
}
// If the user is teleporting disable movement-controls in XR
const sceneEl = document.querySelector("a-scene");
sceneEl.addEventListener("enter-vr", function() {
if (movementType === 'teleport') {
cameraRig.setAttribute('movement-controls', 'enabled', false);
}
});
sceneEl.addEventListener("exit-vr", function() {
cameraRig.setAttribute('movement-controls', 'enabled', true);
});
</script>
<div id="dom-overlay-message">Enter AR or VR to start.</div>
</div>
</div>
<div class="glitchButton" style="position: absolute; top: 1em; right: 1em;"></div><script src="https://button.glitch.me/button.js"></script>
</body>
</html>