Skip to content

Commit f82b698

Browse files
committed
Start linking in AR.js
1 parent 56e2ef0 commit f82b698

24 files changed

+4951
-21
lines changed

README.md

+7-9
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,21 @@ So far, just a proof-of-concept using what I believe is the minimal code for usi
66

77
## Instructions
88

9-
Copy the `demomod` branch of my fork of the AlvaAR code [here](https://github.com/nickw1/AlvaAR) into the project. Specifically, copy the `public` directory from the AlvaAR examples into a subdirectory called `alva` within the `public` directory in this repository.
9+
This now includes a **modified** version of code from the AlvaAR examples, specifically the contents of the `assets` directory which contains library-style code.
1010

11+
Everything in the `alva` directory is taken from AlvaAR.
12+
13+
I have however removed the large `image.gif` and `video.mp4` files as these are not needed.
1114

12-
My fork has made one or two adaptations to the utility classes provided with the examples, to make them more reusable in other projects.
1315

1416
## Versions
1517

1618
This repository is currently very experimental and subject to change; I have experiments with both three.js and A-Frame. Currently, my aim is to try and re-use as much of the AlvaAR example code as possible.
1719

18-
### three.js
19-
20-
Just run a server directly inside the `public` directory and request `three.html`.
21-
22-
### A-Frame
20+
### Build
2321

24-
Build the A-Frame version with
22+
Build with
2523

2624
`npm run build`
2725

28-
Then run a webserver in the `public` directory and request the index page in your browser.
26+
Then run a webserver in the `public` directory and request the index page you want (`three.html` or `aframe.html`) in your browser.

aframe/alva-scene.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// https://github.com/alanross/AlvaAR/blob/main/examples/public/camera.html
55

66

7-
import { AlvaAR } from '../public/alva/assets/alva_ar.js';
8-
import { AlvaARConnectorTHREE } from '../public/alva/assets/alva_ar_three.js';
7+
import { AlvaAR } from '../alva/assets/alva_ar.js';
8+
import { AlvaARConnectorTHREE } from '../alva/assets/alva_ar_three.js';
99

1010
AFRAME.registerComponent("alva-scene", {
1111

alva/assets/alva_ar.js

+254
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

alva/assets/alva_ar_three.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Example usage:
3+
* import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js';
4+
* import { AlvaAR } from 'alva_ar.js';
5+
* import { AlvaAR } from 'alva_ar_three.js';
6+
*
7+
* const alva = await AlvaAR.Initialize( ... );
8+
* const applyPose = AlvaARConnectorTHREE.Initialize( THREE )
9+
* const renderer = new THREE.WebGLRenderer( ... );
10+
* const camera = new THREE.PerspectiveCamera( ... );
11+
* const scene = new THREE.Scene();
12+
* ...
13+
*
14+
* function loop()
15+
* {
16+
* const imageData = ctx.getImageData( ... );
17+
* const pose = alva.findCameraPose( imageData );
18+
*
19+
* if( pose ) applyPose( pose, camera.quaternion, camera.position );
20+
*
21+
* renderer.render( this.scene, this.camera );
22+
* }
23+
*/
24+
25+
class AlvaARConnectorTHREE
26+
{
27+
static Initialize( THREE )
28+
{
29+
return ( pose, rotationQuaternion, translationVector ) =>
30+
{
31+
const m = new THREE.Matrix4().fromArray( pose );
32+
const r = new THREE.Quaternion().setFromRotationMatrix( m );
33+
const t = new THREE.Vector3( pose[12], pose[13], pose[14] );
34+
35+
( rotationQuaternion !== null ) && rotationQuaternion.set( -r.x, r.y, r.z, r.w );
36+
( translationVector !== null ) && translationVector.set( t.x, -t.y, -t.z );
37+
}
38+
}
39+
}
40+
41+
export { AlvaARConnectorTHREE };

alva/assets/image.jpg

107 KB
Loading

alva/assets/imu.js

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import { deg2rad, getScreenOrientation, isIOS } from "./utils.js";
2+
3+
class Quaternion
4+
{
5+
static fromAxisAngle( axisX = 0, axisY = 0, axisZ = 0, angle = 0 )
6+
{
7+
const angle2 = angle / 2;
8+
const s = Math.sin( angle2 );
9+
10+
return {
11+
x: axisX * s,
12+
y: axisY * s,
13+
z: axisZ * s,
14+
w: Math.cos( angle2 )
15+
};
16+
}
17+
18+
static fromEuler( x = 0, y = 0, z = 0, order = 'XYZ' )
19+
{
20+
const cos = Math.cos;
21+
const sin = Math.sin;
22+
23+
const c1 = cos( x / 2 );
24+
const c2 = cos( y / 2 );
25+
const c3 = cos( z / 2 );
26+
27+
const s1 = sin( x / 2 );
28+
const s2 = sin( y / 2 );
29+
const s3 = sin( z / 2 );
30+
31+
const q = { x: 0, y: 0, z: 0, w: 1 };
32+
33+
switch( order )
34+
{
35+
case 'XYZ':
36+
q.x = s1 * c2 * c3 + c1 * s2 * s3;
37+
q.y = c1 * s2 * c3 - s1 * c2 * s3;
38+
q.z = c1 * c2 * s3 + s1 * s2 * c3;
39+
q.w = c1 * c2 * c3 - s1 * s2 * s3;
40+
break;
41+
42+
case 'YXZ':
43+
q.x = s1 * c2 * c3 + c1 * s2 * s3;
44+
q.y = c1 * s2 * c3 - s1 * c2 * s3;
45+
q.z = c1 * c2 * s3 - s1 * s2 * c3;
46+
q.w = c1 * c2 * c3 + s1 * s2 * s3;
47+
break;
48+
49+
case 'ZXY':
50+
q.x = s1 * c2 * c3 - c1 * s2 * s3;
51+
q.y = c1 * s2 * c3 + s1 * c2 * s3;
52+
q.z = c1 * c2 * s3 + s1 * s2 * c3;
53+
q.w = c1 * c2 * c3 - s1 * s2 * s3;
54+
break;
55+
56+
case 'ZYX':
57+
q.x = s1 * c2 * c3 - c1 * s2 * s3;
58+
q.y = c1 * s2 * c3 + s1 * c2 * s3;
59+
q.z = c1 * c2 * s3 - s1 * s2 * c3;
60+
q.w = c1 * c2 * c3 + s1 * s2 * s3;
61+
break;
62+
63+
case 'YZX':
64+
q.x = s1 * c2 * c3 + c1 * s2 * s3;
65+
q.y = c1 * s2 * c3 + s1 * c2 * s3;
66+
q.z = c1 * c2 * s3 - s1 * s2 * c3;
67+
q.w = c1 * c2 * c3 - s1 * s2 * s3;
68+
break;
69+
70+
case 'XZY':
71+
q.x = s1 * c2 * c3 - c1 * s2 * s3;
72+
q.y = c1 * s2 * c3 - s1 * c2 * s3;
73+
q.z = c1 * c2 * s3 + s1 * s2 * c3;
74+
q.w = c1 * c2 * c3 + s1 * s2 * s3;
75+
break;
76+
77+
default:
78+
console.warn( 'CreateFromEuler() encountered an unknown order: ' + order );
79+
}
80+
81+
return q;
82+
}
83+
84+
static multiply( a, b )
85+
{
86+
const qax = a.x, qay = a.y, qaz = a.z, qaw = a.w;
87+
const qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
88+
89+
return {
90+
x: qax * qbw + qaw * qbx + qay * qbz - qaz * qby,
91+
y: qay * qbw + qaw * qby + qaz * qbx - qax * qbz,
92+
z: qaz * qbw + qaw * qbz + qax * qby - qay * qbx,
93+
w: qaw * qbw - qax * qbx - qay * qby - qaz * qbz,
94+
}
95+
}
96+
97+
static dot( a, b )
98+
{
99+
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
100+
}
101+
}
102+
103+
class IMU
104+
{
105+
static Initialize()
106+
{
107+
return new Promise( ( resolve, reject ) =>
108+
{
109+
const finalize = () =>
110+
{
111+
if( window.isSecureContext === false )
112+
{
113+
reject( "DeviceOrientation is only available in secure contexts (https)." );
114+
return;
115+
}
116+
117+
if( window.DeviceOrientationEvent === undefined )
118+
{
119+
reject( "DeviceOrientation not supported." );
120+
return;
121+
}
122+
123+
if( window.DeviceMotionEvent === undefined )
124+
{
125+
reject( "DeviceMotion not supported." );
126+
return;
127+
}
128+
129+
resolve( new IMU() );
130+
}
131+
132+
if( window.DeviceMotionEvent !== undefined && typeof window.DeviceMotionEvent.requestPermission === 'function' )
133+
{
134+
window.DeviceMotionEvent.requestPermission().then( state =>
135+
{
136+
if( state === "granted" )
137+
{
138+
finalize();
139+
}
140+
else
141+
{
142+
reject( "Permission denied by user." );
143+
}
144+
}, error =>
145+
{
146+
reject( error.toString() );
147+
} );
148+
}
149+
else if( window.ondevicemotion !== undefined )
150+
{
151+
finalize();
152+
}
153+
else
154+
{
155+
reject( "DeviceMotion is not supported." );
156+
}
157+
} );
158+
}
159+
160+
constructor()
161+
{
162+
this.EPS = 0.000001;
163+
164+
this.screenOrientation = null;
165+
this.screenOrientationAngle = 0;
166+
167+
this.motion = [];
168+
169+
this.orientation = { x: 1, y: 0, z: 0, w: 1 };
170+
this.worldTransform = isIOS()
171+
? Quaternion.fromAxisAngle( 1, 0, 0, -Math.PI / 2 ) // -90 degrees on x-axis
172+
: Quaternion.fromAxisAngle( 0, 1, 0, Math.PI / 2 ); // 90 degrees on y-axis
173+
174+
const handleDeviceOrientation = ( event ) =>
175+
{
176+
// axis orientation assumes device is placed on ground, screen upward
177+
const x = event.beta * deg2rad; // X-axis (β) vertical tilt
178+
const y = event.gamma * deg2rad; // Y-axis (γ) horizontal tilt
179+
const z = event.alpha * deg2rad; // Z-axis (α) compass direction
180+
181+
const orientation = Quaternion.multiply( this.worldTransform, Quaternion.fromEuler( x, y, z, 'ZXY' ) );
182+
183+
if( 8 * (1 - Quaternion.dot( this.orientation, orientation )) > this.EPS )
184+
{
185+
this.orientation = orientation;
186+
}
187+
}
188+
189+
const handleDeviceMotion = ( event ) =>
190+
{
191+
const gx = event.rotationRate.beta * deg2rad; // X-axis (β) deg to rad: rad/s
192+
const gy = event.rotationRate.gamma * deg2rad; // Y-axis (γ) deg to rad: rad/s
193+
const gz = event.rotationRate.alpha * deg2rad; // Z-axis (α) deg to rad: rad/s
194+
195+
const ax = event.acceleration.x; // (m/s^2)
196+
const ay = event.acceleration.y; // (m/s^2)
197+
const az = event.acceleration.z; // (m/s^2)
198+
199+
const timestamp = Date.now();
200+
201+
this.motion.push( { timestamp, gx, gy, gz, ax, ay, az } );
202+
}
203+
204+
const handleScreenOrientation = ( event ) =>
205+
{
206+
this.screenOrientation = getScreenOrientation();
207+
208+
if( this.screenOrientation === 'landscape_left' )
209+
{
210+
this.screenOrientationAngle = 90;
211+
}
212+
else if( this.screenOrientation === 'landscape_right' )
213+
{
214+
this.screenOrientationAngle = 270;
215+
}
216+
else
217+
{
218+
this.screenOrientationAngle = 0;
219+
}
220+
}
221+
222+
window.addEventListener( 'devicemotion', handleDeviceMotion.bind( this ), false );
223+
window.addEventListener( 'deviceorientation', handleDeviceOrientation.bind( this ), false );
224+
window.addEventListener( 'orientationchange', handleScreenOrientation.bind( this ), false );
225+
}
226+
227+
clear()
228+
{
229+
this.motion.length = 0;
230+
}
231+
}
232+
233+
export { IMU }

alva/assets/qr.png

3.82 KB
Loading

0 commit comments

Comments
 (0)