Skip to content

Commit 92fcd5a

Browse files
committed
Add light estimate support
1 parent 3d84a60 commit 92fcd5a

File tree

9 files changed

+207
-20
lines changed

9 files changed

+207
-20
lines changed

examples/light/index.html

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
2+
<html>
3+
<head>
4+
<title>Light example</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<style>
8+
body, html {
9+
padding: 0;
10+
margin: 0;
11+
overflow: hidden;
12+
position: fixed;
13+
width: 100%;
14+
height: 100vh;
15+
-webkit-user-select: none;
16+
user-select: none;
17+
}
18+
#target {
19+
width: 100%;
20+
height: 100%;
21+
position: absolute;
22+
}
23+
.common-message {
24+
position: absolute;
25+
top: 50%;
26+
left: 50%;
27+
transform: translate(-50%, -50%);
28+
font-size: 20px;
29+
}
30+
</style>
31+
<link rel="stylesheet" href="../common.css"/>
32+
<script src="../libs/three.min.js"></script>
33+
<script type="module" src="../../polyfill/XRPolyfill.js"></script>
34+
<script nomodule src="../../dist/webxr-polyfill.js"></script>
35+
<script src="../common.js"></script>
36+
</head>
37+
<body>
38+
<div id="target" />
39+
<div onclick="hideMe(this)" id="description">
40+
<h2>Light</h2>
41+
<h5>(click to dismiss)</h5>
42+
<p>Place a reticle on surfaces with light (only works with iOS viewer).</p>
43+
</div>
44+
<script>
45+
/*
46+
HitTestExample shows how to find surfaces or other features and place content relative to them.
47+
48+
In a production application, you would not create a separate anchor for every user action because
49+
your application would quickly slow down tracking so many anchors. Instead, find an anchor
50+
for groups of content that are positioned relative to some surface or other feature.
51+
*/
52+
class HitTestExample extends XRExampleBase {
53+
constructor(domElement){
54+
super(domElement, false)
55+
this._tapEventData = null // Will be filled in on touch start and used in updateScene
56+
57+
// A message at the bottom of the screen that shows whether a surface has been found
58+
this._messageEl = document.createElement('div')
59+
this.el.appendChild(this._messageEl)
60+
this._messageEl.style.position = 'absolute'
61+
this._messageEl.style.bottom = '10px'
62+
this._messageEl.style.left = '10px'
63+
this._messageEl.style.color = 'white'
64+
this._messageEl.style['font-size'] = '16px'
65+
66+
this._tapEventData = [ 0.5, 0.5 ]
67+
this.el.addEventListener('touchstart', this._onTouchStart.bind(this), false)
68+
}
69+
70+
// Called during construction to allow the app to populate this.scene
71+
initializeScene(){
72+
// Add a reticle at the scene
73+
this.reticle = new THREE.Mesh(
74+
new THREE.RingGeometry(0.04, 0.05, 36, 64),
75+
new THREE.MeshStandardMaterial({ color: '#ffcc00' })
76+
)
77+
this.reticle.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(-90)))
78+
this.reticle.visible = false
79+
this.scene.add(this.reticle)
80+
81+
// Add a few lights
82+
this.ambientLight = new THREE.AmbientLight('#f8f8f8', 1);
83+
this.scene.add(this.ambientLight);
84+
this.directionalLight = new THREE.DirectionalLight('#f8f8f8', 0.5);
85+
this.directionalLight.position.set(0, 10, 0);
86+
this.scene.add(this.directionalLight);
87+
}
88+
89+
// Called once per frame, before render, to give the app a chance to update this.scene
90+
updateScene(frame){
91+
const x = this._tapEventData[0]
92+
const y = this._tapEventData[1]
93+
94+
const hit = frame.hitTestNoAnchor(x, y);
95+
if(frame.hasLightEstimate){
96+
this.ambientLight.intensity = frame.lightEstimate;
97+
this.directionalLight.intensity = frame.lightEstimate * 0.5;
98+
}
99+
var model = new THREE.Matrix4();
100+
var tempPos = new THREE.Vector3();
101+
var tempQuat = new THREE.Quaternion();
102+
var tempScale = new THREE.Vector3();
103+
if (hit && hit.length > 0) {
104+
this.reticle.visible = true
105+
model.fromArray(hit[0].modelMatrix);
106+
model.decompose(tempPos, tempQuat, tempScale);
107+
this.reticle.position.copy(tempPos);
108+
this.reticle.quaternion.copy(tempQuat);
109+
}
110+
}
111+
112+
// Save screen taps as normalized coordinates for use in this.updateScene
113+
_onTouchStart(ev){
114+
if (!ev.touches || ev.touches.length === 0) {
115+
console.error('No touches on touch event', ev)
116+
return
117+
}
118+
//save screen coordinates normalized to -1..1 (0,0 is at center and 1,1 is at top right)
119+
this._tapEventData = [
120+
ev.touches[0].clientX / window.innerWidth,
121+
ev.touches[0].clientY / window.innerHeight
122+
]
123+
}
124+
}
125+
126+
127+
window.addEventListener('DOMContentLoaded', () => {
128+
setTimeout(() => {
129+
try {
130+
window.pageApp = new HitTestExample(document.getElementById('target'))
131+
} catch(e) {
132+
console.error('page error', e)
133+
}
134+
}, 1000)
135+
})
136+
</script>
137+
</body>
138+
</html>

examples/light/screenshot.jpeg

13.5 KB
Loading

index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ <h2>Examples</h2>
9696
<a class="source" href="https://github.com/mozilla/webxr-polyfill/blob/master/examples/reticle/index.html">source</a>
9797
<p>Place a reticle on surfaces.</p>
9898
</li>
99+
<li>
100+
<a href="examples/light/">Light</a>
101+
<a class="source" href="https://github.com/mozilla/webxr-polyfill/blob/master/examples/light/index.html">source</a>
102+
<p>Place a reticle on surfaces with light estimation.</p>
103+
</li>
99104
</ul>
100105
</li>
101106
</body>

package-lock.json

Lines changed: 23 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

polyfill/Reality.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,9 @@ export default class Reality extends EventHandlerBase {
9090
_hitTestNoAnchor(normalizedScreenX, normalizedScreenY, display){
9191
throw new Error('Exending classes should implement _hitTestNoAnchor')
9292
}
93+
94+
_getLightAmbientIntensity(){
95+
throw new Error('Exending classes should implement _getLightAmbientIntensity')
96+
}
9397
// attribute EventHandler onchange;
9498
}

polyfill/XRLightEstimate.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22
XRLightEstimate represents the attributes of environmental light as supplied by the device's sensors.
33
*/
44
export default class XRLightEstimate {
5+
constructor(){
6+
this._ambientLightIntensity = 1
7+
}
8+
9+
set ambientIntensity(value){
10+
// A value of 1000 represents "neutral" lighting. (https://developer.apple.com/documentation/arkit/arlightestimate/2878308-ambientintensity)
11+
this._ambientLightIntensity = value / 1000
12+
}
13+
514
get ambientIntensity(){
615
//readonly attribute double ambientIntensity;
7-
throw new Error('Not implemented')
16+
return this._ambientLightIntensity
817
}
918

1019
getAmbientColorTemperature(){

polyfill/XRPresentationFrame.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ export default class XRPresentationFrame {
2828

2929
get hasLightEstimate(){
3030
//readonly attribute boolean hasLightEstimate;
31-
return false
31+
return this._session.reality._getHasLightEstimate();
3232
}
3333

3434
get lightEstimate(){
3535
//readonly attribute XRLightEstimate? lightEstimate;
36-
return null
36+
return this._session.reality._getLightAmbientIntensity();
3737
}
3838

3939
/*

polyfill/platform/ARKitWrapper.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default class ARKitWrapper extends EventHandlerBase {
3939
this._isInitialized = false
4040
this._rawARData = null
4141

42+
this.lightIntensity = 1000;
4243
/**
4344
* The current projection matrix of the device.
4445
* @type {Float32Array}
@@ -680,6 +681,7 @@ export default class ARKitWrapper extends EventHandlerBase {
680681
detail: this._rawARData
681682
}))
682683

684+
this.lightIntensity = data.light_intensity;
683685
this.viewMatrix_ = data.camera_view;
684686
this.projectionMatrix_ = data.projection_camera;
685687

polyfill/reality/CameraReality.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import XRViewPose from '../XRViewPose.js'
44

55
import XRAnchorOffset from '../XRAnchorOffset.js'
66

7+
import XRLightEstimate from '../XRLightEstimate.js'
8+
79
import MatrixMath from '../fill/MatrixMath.js'
810
import Quaternion from '../fill/Quaternion.js'
911

@@ -38,6 +40,8 @@ export default class CameraReality extends Reality {
3840
this._vrDisplay = null
3941
this._vrFrameData = null
4042

43+
this._lightEstimate = new XRLightEstimate();
44+
4145
// Try to find a WebVR 1.1 display that supports Google's ARCore extensions
4246
if(typeof navigator.getVRDisplays === 'function'){
4347
navigator.getVRDisplays().then(displays => {
@@ -155,6 +159,7 @@ export default class CameraReality extends Reality {
155159
for(let anchorInfo of ev.detail.objects){
156160
this._updateAnchorFromARKitUpdate(anchorInfo.uuid, anchorInfo)
157161
}
162+
158163
}
159164
}
160165

@@ -319,4 +324,22 @@ export default class CameraReality extends Reality {
319324
return null;
320325
}
321326
}
327+
328+
_getHasLightEstimate(){
329+
if(this._arKitWrapper !== null){
330+
return true;
331+
}else{
332+
return false;
333+
}
334+
}
335+
336+
_getLightAmbientIntensity(){
337+
if(this._arKitWrapper !== null){
338+
this._lightEstimate.ambientIntensity = this._arKitWrapper.lightIntensity;
339+
return this._lightEstimate.ambientIntensity;
340+
}else{
341+
// No platform support for ligth estimation
342+
return null;
343+
}
344+
}
322345
}

0 commit comments

Comments
 (0)