Skip to content

Commit

Permalink
Add hand-tracking-grab-controls component, docs and example. Any enti…
Browse files Browse the repository at this point in the history
…ty can now be grabbed and manipulated with hand tracking.
  • Loading branch information
dmarcos committed Nov 1, 2023
1 parent f036789 commit ecc2eb7
Show file tree
Hide file tree
Showing 26 changed files with 699 additions and 79 deletions.
39 changes: 39 additions & 0 deletions docs/components/hand-tracking-grab-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: hand-tracking-grab-controls
type: components
layout: docs
parent_section: components
source_code: src/components/hand-tracking-grab-controls.js
examples: []
---

Grab any entity with hand tracking using the pinch gesture.

## Example

```html
<a-entity id="leftHand" hand-tracking-grab-controls="hand: left;"></a-entity>
<a-entity id="rightHand" hand-tracking-grab-controls="hand: right;"></a-entity>
```

The `grabbable` component makes any entity hand grababble.

```html
<a-box grabbable></a-box>
```

For debugging purposes you can make the colliders visible as below:

```html
<a-scene obb-collider="showColliders: false"></a-scene>
```

## Properties

| Property | Description | Default Value |
|----------------|----------------------------------------------------------------------------------------|---------------|
| hand | The hand that will be tracked (i.e., right, left). | left |
| handColor | The color of the hand model. | white |
| hoverColor | Hand color when hand intersects a grabbable entity bounding box. | #538df1 |
| hoverEnabled | If the hand model changes color when intersecting a grabbable entity. | false |

41 changes: 41 additions & 0 deletions docs/components/obb-collider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: obb-collider
type: components
layout: docs
parent_section: components
source_code: src/components/obb-collider.js
examples: []
---

[obb]: https://threejs.org/docs/#examples/en/math/OBB

Collision system using Oriented Bounding Boxes based on [`THREE.OBB`][obb].

It checks collision accross all entities with the abb-collider component. It emits events when bounding boxes start and stop intersecting.

This comment has been minimized.

Copy link
@vincentfretin

vincentfretin Nov 2, 2023

Contributor

abb-collider -> obb-collider

This comment has been minimized.

Copy link
@dmarcos

dmarcos Nov 4, 2023

Author Member

thanks. done


## Example

```html
<a-entity obb-collider></a-entity>
```

Set `showColliders` to `true` on the scene to render colliders for debugging purposes:

```html
<a-scene obb-collider="showColliders: true"></a-scene>
```

## Properties

| Property | Description | Default Value |
|------------------|----------------------------------------------------------------------------------------|---------------|
| size | Force collider to a specific size | 0 0 0 |
| trackedObject3D | Variable path to the object3D used to calculate the collider. el.object3D by default | '' |

## Events

| Event Name | Description |
| ---------- | ------------------------------------------------------------------------------------------- |
| obbcollisionstarted | Emitted on the entities that start colliding. Event detail will contain `withEl` referencing the entity that has collided with. |
| obbcollisionended | Emitted on the entities that stop colliding. Event detail will contain `withEl` referencing the entity that was colliding with |

69 changes: 0 additions & 69 deletions docs/components/oculus-go-controls.md

This file was deleted.

2 changes: 2 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ <h2>Examples</h2>
<li><a href="showcase/curved-mockups/">Curved Mockups</a></li>
<li><a href="showcase/dynamic-lights/">Dynamic Lights</a></li>
<li><a href="showcase/hand-tracking/">Hand Tracking</a></li>
<li><a href="showcase/hand-tracking-grab-controls/">Hand Tracking Grab Controls</a></li>
<li><a href="showcase/ui/">User Interface</a></li>
<li><a href="showcase/link-traversal/">Link Traversal</a></li>
<li><a href="showcase/model-viewer/">Model Viewer</a></li>
Expand All @@ -153,6 +154,7 @@ <h2>Examples</h2>
<li><a href="boilerplate/panorama/">360&deg; Image</a></li>
<li><a href="boilerplate/360-video/">360&deg; Video</a></li>
<li><a href="boilerplate/3d-model/">3D Model (glTF)</a></li>
<li><a href="mixed-reality/anchor/">Anchor (Mixed Reality)</a></li>
</ul>

<h2>Examples from Documentation</h2>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ AFRAME.registerComponent('spatial-hero-image', {
width: {default: 1, min: 0},
height: {default: 1, min: 0},
focused: {default: true},
roundCorners: {default: true},
src: {type: 'map'}
},

init: function () {
var data = this.data;
var geometry = this.geometry = SPATIAL.utils.generatePlaneGeometryIndexed(data.width, data.height, 0.05, 22);
var borderRadius = data.roundCorners ? 0.05 : 0.01;
var geometry = this.geometry = SPATIAL.utils.generatePlaneGeometryIndexed(data.width, data.height, borderRadius, 22);
var material = this.material = new THREE.MeshBasicMaterial({color: new THREE.Color(data.color)});
this.el.sceneEl.systems.material.loadTexture(data.src, {src: data.src}, function textureLoaded (texture) {
material.map = texture;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ AFRAME.registerComponent('spatial-modal', {
color: {type: 'color', default: '#fff'},
width: {default: 1, min: 0},
height: {default: 1, min: 0},
roundCorners: {default: true},
src: {type: 'map'}
},

init: function () {
var data = this.data;
var geometry = this.geometry = SPATIAL.utils.generatePlaneGeometryIndexed(data.width, data.height, 0.05, 22);
var borderRadius = data.roundCorners ? 0.05 : 0.01;
var geometry = this.geometry = SPATIAL.utils.generatePlaneGeometryIndexed(data.width, data.height, borderRadius, 22);
var material = this.material = new THREE.MeshPhysicalMaterial({
roughness: 0.6,
transmission: 1,
Expand All @@ -27,5 +29,9 @@ AFRAME.registerComponent('spatial-modal', {

this.plane = new THREE.Mesh(geometry, material);
this.el.setObject3D('mesh', this.plane);
},

update: function () {
this.material.color.set(this.data.color);
}
});
File renamed without changes.
File renamed without changes.
18 changes: 18 additions & 0 deletions examples/showcase/hand-tracking-grab-controls/hover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* global AFRAME */
AFRAME.registerComponent('hover', {
init: function () {
this.onCollisionStarted = this.onCollisionStarted.bind(this);
this.onCollisionEnded = this.onCollisionEnded.bind(this);
this.modalEl = this.el.querySelector('[spatial-modal]');
this.el.addEventListener('obbcollisionstarted', this.onCollisionStarted);
this.el.addEventListener('obbcollisionended', this.onCollisionEnded);
},

onCollisionStarted: function () {
this.modalEl.setAttribute('spatial-modal', 'color', 'red');
},

onCollisionEnded: function () {
this.modalEl.setAttribute('spatial-modal', 'color', 'white');
}
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions examples/showcase/hand-tracking-grab-controls/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hand Tracking Grab Controls • A-Frame</title>
<meta name="description" content="Hand Tracking Grab Controls • A-Frame">
<script src="../../dist/aframe-master.js"></script>
<script src="../../js/spatial-ui/spatial-utils.js"></script>
<script src="../../js/spatial-ui/spatial-modal.js"></script>
<script src="../../js/spatial-ui/spatial-hero-image.js"></script>
<script src="../../js/info-message.js"></script>
<script src="https://unpkg.com/[email protected]/dist/aframe-environment-component.min.js"></script>
<script src="hover.js"></script>
</head>
<body>
<a-scene
environment
obb-collider="showColliders: false"
info-message="htmlSrc: #messageText">
<a-assets>
<a-asset-item id="messageText" src="message.html"></a-asset-item>
<img id="ubuntu" src="./images/ubuntu-desktop.png" />
</a-assets>
<a-entity camera position="0 1.5 0" look-controls wasd-controls></a-entity>
<a-entity position="0 1.6 -1" grabbable hover>
<a-entity spatial-modal="width: 1.81; height: 1.035; roundCorners: false">
<a-entity spatial-hero-image="src: #ubuntu; width: 1.775; height: 1; roundCorners: false" position="0 0 0.001"></a-entity>
</a-entity>
</a-entity>
<a-entity id="rightHand" hand-tracking-grab-controls="hand: right"></a-entity>
<a-entity id="leftHand" hand-tracking-grab-controls="hand: left"></a-entity>
</a-scene>
</body>
</html>
3 changes: 3 additions & 0 deletions examples/showcase/hand-tracking-grab-controls/message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>
Image from <a href="https://ubuntu.com/download/desktop">Ubuntu Desktop</a><br>
</p>
20 changes: 20 additions & 0 deletions examples/showcase/spatial-ui/grab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* global AFRAME */
AFRAME.registerComponent('grab', {
// init: function () {
// this.grabbed = null;

// },
// events: {
// gripdown: function (evt) {
// if (evt.currentTarget.components['raycaster'].intersections.length>0) {
// this.grabbed = evt.currentTarget.components['raycaster'].intersections[0].object.el;
// evt.currentTarget.object3D.attach(this.grabbed.object3D);
// }
// }, gripup: function (evt) {
// if (this.grabbed) {
// this.el.sceneEl.object3D.attach(this.grabbed.object3D);
// this.grabbed = null;
// }
// }
// }
});
16 changes: 8 additions & 8 deletions examples/showcase/spatial-ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
<meta charset="utf-8">
<title>Spatial UI</title>
<meta name="description" content="Spatial UI • A-Frame">
<script src="https://cdn.jsdelivr.net/gh/aframevr/aframe@309a1b73786ad79bfe64065f1decac741151524d/dist/aframe-master.min.js"></script>
<script src="../../../dist/aframe-master.js"></script>

<script src="ui-controller.js"></script>
<script src="spatial-utils.js"></script>
<script src="spatial-window.js"></script>
<script src="spatial-modal.js"></script>
<script src="spatial-modal-image.js"></script>
<script src="spatial-button.js"></script>
<script src="spatial-close-button.js"></script>
<script src="spatial-hero-image.js"></script>
<script src="../../js/spatial-ui/spatial-utils.js"></script>
<script src="../../js/spatial-ui/spatial-window.js"></script>
<script src="../../js/spatial-ui/spatial-modal.js"></script>
<script src="../../js/spatial-ui/spatial-modal-image.js"></script>
<script src="../../js/spatial-ui/spatial-button.js"></script>
<script src="../../js/spatial-ui/spatial-close-button.js"></script>
<script src="../../js/spatial-ui/spatial-hero-image.js"></script>
<script src="tile.js"></script>
</head>
<body>
Expand Down
13 changes: 13 additions & 0 deletions src/components/gltf-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ module.exports.Component = registerComponent('gltf-model', {
self.loader.load(src, function gltfLoaded (gltfModel) {
self.model = gltfModel.scene || gltfModel.scenes[0];
self.model.animations = gltfModel.animations;

self.centerModel();

This comment has been minimized.

Copy link
@mrxz

mrxz Nov 2, 2023

Contributor

@dmarcos This is a breaking change. It's not uncommon for models to intentionally have an offset center (e.g. trees/vegetation, avatars, etc).

I'd argue the proper approach would always be for the user to fix their assets. If that isn't an option, it can always be wrapped in an <a-entity> and manually offset. This code is probably better served as its own component that reacts to something like object3dset, so that it could work regardless of where the mesh comes from (and opt-in)

This comment has been minimized.

Copy link
@dmarcos

dmarcos Nov 4, 2023

Author Member

thanks. I'll look into it


el.setObject3D('mesh', self.model);
el.emit('model-loaded', {format: 'gltf', model: self.model});
}, undefined /* onProgress */, function gltfFailed (error) {
Expand All @@ -54,6 +57,16 @@ module.exports.Component = registerComponent('gltf-model', {
});
},

centerModel: function () {
var model = this.model;
var box = new THREE.Box3().setFromObject(model);
var center = box.getCenter(new THREE.Vector3());

model.position.x += (model.position.x - center.x);
model.position.y += (model.position.y - center.y);
model.position.z += (model.position.z - center.z);
},

remove: function () {
if (!this.model) { return; }
this.el.removeObject3D('mesh');
Expand Down
7 changes: 7 additions & 0 deletions src/components/grabbable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var registerComponent = require('../core/component').registerComponent;

registerComponent('grabbable', {
init: function () {
this.el.setAttribute('obb-collider', '');
}
});
16 changes: 16 additions & 0 deletions src/components/hand-tracking-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ module.exports.Component = registerComponent('hand-tracking-controls', {
this.el.sceneEl.addEventListener('exit-vr', this.updateReferenceSpace);
},

update: function () {
this.updateModelColor();
},

updateModelColor: function () {
var jointEls = this.jointEls;
var skinnedMesh = this.skinnedMesh;
if (skinnedMesh) {
this.skinnedMesh.material.color.set(this.data.modelColor);
}

for (var i = 0; i < jointEls.lenght; i++) {

This comment has been minimized.

Copy link
@vincentfretin

vincentfretin Nov 2, 2023

Contributor

lenght -> length

This comment has been minimized.

Copy link
@dmarcos

dmarcos Nov 4, 2023

Author Member

thanks. done

jointEls[i].setAttribute('material', 'color', this.data.modelColor);
}
},

updateReferenceSpace: function () {
var self = this;
var xrSession = this.el.sceneEl.xrSession;
Expand Down
Loading

0 comments on commit ecc2eb7

Please sign in to comment.