-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for WebXR Mesh and WebXR Plane modules
- Loading branch information
Showing
10 changed files
with
477 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
title: real-world-meshing | ||
type: components | ||
layout: docs | ||
parent_section: components | ||
source_code: src/components/scene/real-world-meshing.js | ||
examples: [] | ||
--- | ||
|
||
Set this component on the scene element to render meshes corresponding to 3D surfaces detected in user's enviornment: this includes planes and meshes corresponding to floor, ceiling, walls and other objects. Each plane or meshes comes with a label indicating the type of surface or object. | ||
|
||
This component requires a browser with support for the [WebXR Mesh Detection Module]([object | ||
pooling](https://en.wikipedia.org/wiki/Object_pool_pattern) and the [WebXR Plane Detection Module](https://immersive-web.github.io/real-world-geometry/plane-detection.html). The system / headset used might require additional scene setup by the use like setting up floor, walls, ceiling or labeling furniture in the space. | ||
|
||
## Example | ||
|
||
```html | ||
<a-scene real-world-meshing></a-scene> | ||
``` | ||
|
||
## Properties | ||
|
||
| Property | Description | Default Value | ||
|---------------|---------------------------------------------------------------------------------------|--------------- | ||
| filterLabels | List of labels corresponding to the surfaces that will be rendered. Can constrain rendering to certain surfaces like desks, walls, tables... All surfaces will be rendered if left empty. | [] | | ||
| meshesEnabled | If meshes will be rendered as returned by the WebXR Mesh Detection Module. | true | | ||
| meshMixin | Mixin applied to the entities corresponding to the detected meshes. | '' | | ||
| planesEnabled | If planes will be rendered as returned by the WebXR Plane Detection Module. | true | | ||
| planeMixin | Mixin applied to the entities corresponding to the detected planes. | '' | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* global AFRAME, THREE */ | ||
AFRAME.registerComponent('center-model', { | ||
init: function () { | ||
var model; | ||
this.onModelLoaded = this.onModelLoaded.bind(this); | ||
model = this.el.components['gltf-model'] && this.el.components['gltf-model'].model; | ||
if (model) { | ||
this.centerModel(model); | ||
return; | ||
} | ||
document.addEventListener('model-loaded', this.onModelLoaded); | ||
}, | ||
|
||
onModelLoaded: function () { | ||
var model = this.el.components['gltf-model'].model; | ||
this.centerModel(model); | ||
}, | ||
|
||
centerModel: function (model) { | ||
var box; | ||
var center; | ||
this.el.removeObject3D('mesh'); | ||
box = new THREE.Box3().setFromObject(model); | ||
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); | ||
this.el.setObject3D('mesh', model); | ||
} | ||
}); |
93 changes: 93 additions & 0 deletions
93
examples/mixed-reality/real-world-meshing/coffee-spawner.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* global AFRAME */ | ||
AFRAME.registerComponent('coffee-spawner', { | ||
schema: { | ||
delay: {default: 250}, | ||
targetElementSelector: {default: ''} | ||
}, | ||
init: function () { | ||
var el = this.el; | ||
this.delaySpawn = this.delaySpawn.bind(this); | ||
this.cancelDelayedSpawn = this.cancelDelayedSpawn.bind(this); | ||
this.onCollisionEnded = this.onCollisionEnded.bind(this); | ||
this.onCollisionStarted = this.onCollisionStarted.bind(this); | ||
this.el.addEventListener('pinchstarted', this.delaySpawn); | ||
this.el.addEventListener('pinchended', this.cancelDelayedSpawn); | ||
el.addEventListener('obbcollisionstarted', this.onCollisionStarted); | ||
el.addEventListener('obbcollisionended', this.onCollisionEnded); | ||
this.enabled = true; | ||
}, | ||
|
||
delaySpawn: function (evt) { | ||
this.pinchEvt = evt; | ||
this.spawnDelay = this.data.delay; | ||
}, | ||
|
||
cancelDelayedSpawn: function (evt) { | ||
this.spawnDelay = undefined; | ||
}, | ||
|
||
tick: function (time, delta) { | ||
if (!this.spawnDelay) { return; } | ||
this.spawnDelay -= delta; | ||
if (this.spawnDelay <= 0) { | ||
this.spawn(this.pinchEvt); | ||
this.spawnDelay = undefined; | ||
} | ||
}, | ||
|
||
spawn: function (evt) { | ||
var auxEuler = this.auxEuler; | ||
var sceneEl = this.el.sceneEl; | ||
var saucerEl = document.createElement('a-entity'); | ||
var cupEl = document.createElement('a-entity'); | ||
var wristRotation = evt.detail.wristRotation; | ||
var animateScale = function (evt) { | ||
evt.target.setAttribute('animation', { | ||
property: 'scale', | ||
from: {x: 0, y: 0, z: 0}, | ||
to: {x: 0.0015, y: 0.0015, z: 0.0015}, | ||
dur: 200 | ||
}); | ||
}; | ||
|
||
if (!this.enabled) { return; } | ||
if (this.data.targetElementSelector && !this.targetEl) { return; } | ||
|
||
saucerEl.setAttribute('gltf-model', '#coffee'); | ||
saucerEl.setAttribute('grabbable', ''); | ||
saucerEl.setAttribute('hide-model-parts', 'parts: coffee, cup, handle'); | ||
saucerEl.setAttribute('scale', '0.0015 0.0015 0.0015'); | ||
saucerEl.setAttribute('position', evt.detail.position); | ||
saucerEl.addEventListener('loaded', animateScale); | ||
sceneEl.appendChild(saucerEl); | ||
|
||
cupEl.setAttribute('gltf-model', '#coffee'); | ||
cupEl.setAttribute('grabbable', ''); | ||
cupEl.setAttribute('hide-model-parts', 'parts: saucer'); | ||
cupEl.setAttribute('rotation', '0 90 0'); | ||
cupEl.setAttribute('scale', '0.0015 0.0015 0.0015'); | ||
cupEl.setAttribute('position', evt.detail.position); | ||
cupEl.addEventListener('loaded', animateScale); | ||
sceneEl.appendChild(cupEl); | ||
}, | ||
|
||
onCollisionStarted: function (evt) { | ||
var targetElementSelector = this.data.targetElementSelector; | ||
var targetEl = targetElementSelector && this.el.sceneEl.querySelector(targetElementSelector); | ||
if (targetEl === evt.detail.withEl) { | ||
this.targetEl = targetEl; | ||
return; | ||
} | ||
this.enabled = false; | ||
}, | ||
|
||
onCollisionEnded: function (evt) { | ||
var targetElementSelector = this.data.targetElementSelector; | ||
var targetEl = targetElementSelector && this.el.sceneEl.querySelector(targetElementSelector); | ||
if (targetEl === evt.detail.withEl) { | ||
this.targetEl = undefined; | ||
return; | ||
} | ||
this.enabled = true; | ||
} | ||
}); |
43 changes: 43 additions & 0 deletions
43
examples/mixed-reality/real-world-meshing/hide-model-parts.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* global AFRAME, THREE */ | ||
AFRAME.registerComponent('hide-model-parts', { | ||
schema: { | ||
parts: {type: 'array'} | ||
}, | ||
|
||
update: function () { | ||
this.hideParts = this.hideParts.bind(this); | ||
this.el.addEventListener('model-loaded', this.hideParts); | ||
}, | ||
|
||
hideParts: function () { | ||
var part; | ||
var parts = this.data.parts; | ||
var model = this.el.getObject3D('mesh'); | ||
for (var i = 0; i < parts.length; i++) { | ||
part = model.getObjectByName(parts[i]); | ||
part.parent.remove(part); | ||
} | ||
}, | ||
|
||
/** | ||
* Search for the part name and look for a mesh. | ||
*/ | ||
selectFromModel: function (model) { | ||
var mesh; | ||
var part; | ||
part = model.getObjectByName(this.data.part); | ||
if (!part) { | ||
console.error('[gltf-part] `' + this.data.part + '` not found in model.'); | ||
return; | ||
} | ||
|
||
mesh = part.getObjectByProperty('type', 'Mesh').clone(true); | ||
|
||
if (this.data.buffer) { | ||
mesh.geometry = mesh.geometry.toNonIndexed(); | ||
return mesh; | ||
} | ||
mesh.geometry = new THREE.Geometry().fromBufferGeometry(mesh.geometry); | ||
return mesh; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>Real World Meshing (Mixed Reality) • A-Frame</title> | ||
<meta name="description" content="Real World Meshing (Mixed Reality) • A-Frame"> | ||
<script src="../../dist/aframe-master.js"></script> | ||
<script src="../../js/info-message.js"></script> | ||
<script src="../../js/center-model.js"></script> | ||
<script src="hide-model-parts.js"></script> | ||
<script src="coffee-spawner.js"></script> | ||
</head> | ||
<body> | ||
<a-scene | ||
real-world-meshing="filter: table; meshesEnabled: false; planeMixin: xrplane" | ||
obb-collider="showColliders: false" | ||
renderer="colorManagement: true;" | ||
xr-mode-ui="XRMode: ar" | ||
info-message="htmlSrc: #messageText"> | ||
<a-assets> | ||
<!-- Model | ||
* title: Coffee | ||
* source: https://sketchfab.com/3d-models/coffee-963a9e5d288c42509886c5efd4fedd3c | ||
* author: Rosnandie Yikie (https://sketchfab.com/rosnandie.yikie) --> | ||
<a-asset-item id="coffee" | ||
src="https://cdn.aframe.io/examples/mixed-reality/real-world-meshing/models/ coffee.glb" | ||
response-type="arraybuffer" crossorigin="anonymous"></a-asset-item> | ||
<a-asset-item id="messageText" src="message.html"></a-asset-item> | ||
<a-mixin | ||
id="xrplane" | ||
obb-collider | ||
material="color: pink" | ||
visible="false"> | ||
</a-mixin> | ||
<img id="acoffeeshop" src="https://cdn.aframe.io/examples/mixed-reality/real-world-meshing/images/acoffeeshop.png" /> | ||
</a-assets> | ||
<a-plane | ||
material="src: #acoffeeshop; shader: flat" | ||
height="10" width="17.7" position="0 1.5 -5.5" hide-on-enter-ar> | ||
</a-plane> | ||
<a-entity id="rightHand" | ||
hand-tracking-grab-controls="hand: right" | ||
coffee-spawner="targetElementSelector: [data-world-mesh=table]"></a-entity> | ||
<a-entity id="leftHand" | ||
hand-tracking-grab-controls="hand: left" | ||
coffee-spawner="targetElementSelector: [data-world-mesh=table]"></a-entity> | ||
</a-scene> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<p> | ||
This demo requires a browser supporting the <a href="https://immersive-web.github.io/real-world-meshing/">WebXR Mesh Detection Module</a> and <a href="https://immersive-web.github.io/real-world-geometry/plane-detection.html">WebXR Plane Detection Module</a> | ||
</p> | ||
|
||
<p> | ||
In AR mode the user can pinch over a suface labeled as table to spawn coffee cups. This requires the user to setup the environment: set boundaries, mesh and label at least one surface as table. | ||
</p> | ||
|
||
<p> | ||
Frame model by <a href="https://sketchfab.com/3d-models/coffee-963a9e5d288c42509886c5efd4fedd3c">Rosnandie Yikie</a> | ||
</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.