Skip to content

Commit a6593bd

Browse files
committed
Modify docs to use locar.js for location-based three
1 parent 6e4aa1d commit a6593bd

File tree

10 files changed

+507
-406
lines changed

10 files changed

+507
-406
lines changed

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ There are various tutorials available for developing with AR.js. These include:
354354

355355
- [Build your Location-Based Augmented Reality Web App](https://medium.com/chialab-open-source/build-your-location-based-augmented-reality-web-app-c2442e716564): covers location-based AR.js with A-Frame.
356356
- [Develop a Simple Points Of Interest App (A-Frame version)](location-based-aframe/index.md) (*Provided with these docs*): a further location-based A-Frame tutorial, written with AR.js 3.4 in mind.
357-
- [Develop a Simple Points of Interest App (three.js version)](location-based-three/index.md) (*Provided with these docs*): a pure three.js version of the above, also written for AR.js 3.4.
357+
- [Develop a Simple Points of Interest App (LocAR.js version)](locar.js/index.md) (*Provided with these docs*): develop a points of interest app with the new LocAR.js API.
358358

359359
## Troubleshooting, feature requests, community
360360

docs/locar.js/index.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# LocAR.js - Develop a simple Points of Interest app
2+
3+
[LocAR.js](https://github.com/AR-js-org/locar.js) is the new - still in early development - standalone location-based API for AR.js.
4+
5+
Here is a series of tutorials taking you through how to use LocAR.js, from the basics to a more advanced example: a simple but working Points of Interest app using a live web API.
6+
7+
It is expected that you have some understanding of the absolute basics of [three.js](https://threejs.org). You might want to read the introductory "Creating a Scene" section of the [three.js manual](https://three.js.org/docs/index.html#manual/en). (Note that there is currently a formatting issue on Firefox with the source code samples, so you should use another browser such as Chrome. There is a fix imminent on Firefox, however).
8+
9+
You should also have very basic knowledge of [Vite](https://vitejs.dev) and the concept of JavaScript build tools and bundling. Vite is a build and development tool which, as well as bundling your code for production, provides a development server allowing you to develop client-side web apps "live" so that when you make a change to your code or its dependencies, your code is reloaded and changes appear instantly. See the Vite docs for more.
10+
11+
**Do note that it is not recommended to use Firefox on a mobile device due to limitations of the device orientation API. Chrome on Android is recommended.**
12+
13+
## Installing and developing
14+
15+
Here is a sample `package.json` containing three.js and LocAR.js as dependencies, and Vite as a dev dependency.
16+
17+
```
18+
{
19+
"dependencies": {
20+
"three": "^0.169.0",
21+
"locar": "^0.0.2"
22+
},
23+
"devDependencies": {
24+
"vite": "^5.4.8"
25+
},
26+
"scripts": {
27+
"dev" : "vite dev",
28+
"build": "vite build"
29+
}
30+
}
31+
```
32+
As is standard in Vite, you should place your `index.html` inside your project's main directory and the JavaScript source, e.g. `main.js`, inside the `src` directory. You can then, as is normal with Vite, run in dev mode with `npm run dev`, which will start up a dev server on port 5173 and allow you to make live changes to your code which will be updated instantly.
33+
34+
You can also build a bundle with `npm run build`, which will create a production app (a JavaScript bundle plus a version of `index.html` linking to the bundle) in the `dist` directory.
35+
36+
### Contents
37+
38+
- [Part 1: Hello World](part1.md)
39+
- [Part 2: Using the GPS and Device Orientation](part2.md)
40+
- [Part 3: Connecting to a web API](part3.md)
41+

docs/locar.js/part1.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Location-based AR.js with LocAR.js
2+
3+
## Part 1 - Hello World!
4+
5+
6+
The first part of this tutorial will show you how to create a "hello world" application using LocAR.js. It is assumed you are aware of basic three.js concepts, such as the scene, renderer and camera as well as geometries, materials and meshes. This example will set your location to a "fake" GPS location and add a box a short distance away.
7+
8+
Let's start with the HTML, which is very simple:
9+
10+
```html
11+
<!DOCTYPE html>
12+
<html>
13+
<head>
14+
<title>LocAR.js - Hello World</title>
15+
<script type='module' src='src/main.js'></script>
16+
</head>
17+
<body>
18+
</body>
19+
</html>
20+
```
21+
22+
This example assumes that you have installed LocAR.js via `npm` and are using Vite in dev mode to run the application, as described on the [index page for the tutorial](index.md). We link in our JavaScript source as an ES6 module from `src/main.js`, so this is where you should save your code, as `main.js` inside the `src` directory. Here is the `main.js` code:
23+
24+
```javascript
25+
import * as THREE from 'three';
26+
import * as LocAR from 'locar';
27+
28+
const scene = new THREE.Scene();
29+
const camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.001, 100);
30+
const renderer = new THREE.WebGLRenderer();
31+
renderer.setSize(window.innerWidth, window.innerHeight);
32+
document.body.appendChild(renderer.domElement);
33+
34+
window.addEventListener("resize", e => {
35+
renderer.setSize(window.innerWidth, window.innerHeight);
36+
camera.aspect = window.innerWidth / window.innerHeight;
37+
camera.updateProjectionMatrix();
38+
});
39+
const box = new THREE.BoxGeometry(2,2,2);
40+
const cube = new THREE.Mesh(box, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
41+
42+
const locar = new LocAR.LocationBased(scene, camera);
43+
const cam = new LocAR.WebcamRenderer(renderer);
44+
45+
46+
locar.fakeGps(-0.72, 51.05);
47+
locar.add(cube, -0.72, 51.0501);
48+
49+
renderer.setAnimationLoop(animate);
50+
51+
52+
function animate() {
53+
cam.update();
54+
renderer.render(scene, camera);
55+
}
56+
57+
```
58+
59+
Much of this is using standard three.js setup code as described in the [manual](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene); if you do not understand basic three.js concepts such as the scene, camera, and renderer, as well as geometries and meshes, you should read the three.js manual first.
60+
61+
As normal, we create a `THREE.Scene`, a `THREE.PerspectiveCamera` and a `THREE.WebGLRenderer` using our canvas. We also handle resizing the window and handle window resize. We then create a box geometry and a mesh using that box geometry.
62+
63+
What comes next though is new, and specific to AR.js:
64+
65+
```javascript
66+
const locar = new LocAR.LocationBased(scene, camera);
67+
const cam = new LocAR.WebcamRenderer(renderer);
68+
```
69+
70+
We use two new objects, both part of the LocAR.js API. Firstly `LocAR.LocationBased` is the overall AR.js "manager" object and secondly `LocAR.WebcamRenderer` is responsible for rendering the live camera feed. We need to supply our scene and camera as arguments to `LocAR.LocationBased` and our renderer as an argument to `LocAR.WebcamRenderer`.
71+
72+
The `LocAR.WebcamRenderer` will, internally, create a `video` element to capture the webcam. Alternatively, if you have a `video` element already set up in your HTML, you can pass its CSS selector into the `WebcamRenderer` as an optional argument. For example:
73+
74+
```javascript
75+
const cam = new LocAR.WebcamRenderer(renderer, '#video1');
76+
```
77+
78+
Next we add our box mesh to LocAR. This next line is interesting:
79+
80+
```javascript
81+
locar.add(box, -0.72, 51.051);
82+
```
83+
84+
Rather than setting the box's `position` as we would normally do in standard three.js, we add it to a specific **real-world location** defined by longitude and latitude. The `add()` method of `LocAR.LocationBased` allows us to do that.
85+
86+
Having positioned our box in a specific real-world location, we now need to place **ourselves** (i.e. the camera) at a given real-world location We can do this with `LocAR.LocationBased`s `fakeGps()` method, which takes longitude and latitude as parameters:
87+
88+
```javascript
89+
arjs.fakeGps(-0.72, 51.05);
90+
```
91+
92+
This places us just to the south of the red box. By default, we face north, so the red box will appear in front of us.
93+
94+
The remaining code is the standard three.js code for defining a rendering function and setting it as the animation loop. However note this code within the rendering function:
95+
96+
```javascript
97+
cam.update();
98+
```
99+
100+
This API call will render the latest camera frame.
101+
102+
### Try it!
103+
104+
Try it on either a desktop machine or an Android device running Chrome. On a mobile device or desktop you should see the feed from the webcam, and a red box just in front of you. Note that the mobile device will not yet respond to changes in orientation: we will add that next time. For this reason you *must ensure the box is to your north* as the default view is to face north.
105+
106+
### Faking rotation on a desktop machine
107+
108+
If you do not have a suitable mobile device, you can simulate rotation with the mouse. The code below will do this (add to your main block of code, just before the rendering function):
109+
110+
```javascript
111+
const rotationStep = THREE.Math.degToRad(2);
112+
113+
let mousedown = false, lastX =0;
114+
115+
window.addEventListener("mousedown", e=> {
116+
mousedown = true;
117+
});
118+
119+
window.addEventListener("mouseup", e=> {
120+
mousedown = false;
121+
});
122+
123+
window.addEventListener("mousemove", e=> {
124+
if(!mousedown) return;
125+
if(e.clientX < lastX) {
126+
camera.rotation.y -= rotationStep;
127+
if(camera.rotation.y < 0) {
128+
camera.rotation.y += 2 * Math.PI;
129+
}
130+
} else if (e.clientX > lastX) {
131+
camera.rotation.y += rotationStep;
132+
if(camera.rotation.y > 2 * Math.PI) {
133+
camera.rotation.y -= 2 * Math.PI;
134+
}
135+
}
136+
lastX = e.clientX;
137+
});
138+
```
139+
140+
What does this do? Using mouse events, it detects the direction of movement of the mouse when it's pressed down, and in doing so, determines whether to rotate the camera clockwise or anticlockwise. It does this using the `clientX` property of the event object, which contains the mouse X position. This is compared to the previous value of `e.clientX` and from this, we can determine whether we moved the mouse to the left or to the right, and rotate accordingly.
141+
142+
We move the camera by the amount specified in `rotationStep` and ensure that the camera rotation is always within the range 0 to 2PI radians (i.e. 360 degrees).
143+

docs/locar.js/part2.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Location-based AR.js with LocAR.js
2+
3+
## Part 2 - Using the GPS and Device Orientation
4+
5+
Having looked at the basics of the LocAR.js API in the first tutorial, we will now look at how to use the real GPS location. Last time, if you remember, we used a "fake" location with the `LocAR.LocationBased`'s `fakeGps() `call.
6+
7+
We will also look at how we can use the device's orientation controls, so that the orientation sensors are tracked and objects will appear in their real-world position when the device is rotated. For example, an object directly north of the user will only appear when the device is facing north.
8+
9+
### GPS tracking
10+
11+
Here is a revised version of the previous example which obtains your real GPS location:
12+
13+
```javascript
14+
import * as THREE from 'three';
15+
import * as LocAR from 'locar';
16+
17+
const scene = new THREE.Scene();
18+
const camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.001, 100);
19+
const renderer = new THREE.WebGLRenderer();
20+
renderer.setSize(window.innerWidth, window.innerHeight);
21+
document.body.appendChild(renderer.domElement);
22+
23+
window.addEventListener("resize", e => {
24+
renderer.setSize(window.innerWidth, window.innerHeight);
25+
camera.aspect = window.innerWidth / window.innerHeight;
26+
camera.updateProjectionMatrix();
27+
});
28+
const box = new THREE.BoxGeometry(2,2,2);
29+
const cube = new THREE.Mesh(box, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
30+
31+
const locar = new LocAR.LocationBased(scene, camera);
32+
const cam = new LocAR.WebcamRenderer(renderer);
33+
34+
35+
locar.startGps();
36+
locar.add(cube, -0.72, 51.0501);
37+
38+
renderer.setAnimationLoop(animate);
39+
40+
41+
function animate() {
42+
cam.update();
43+
renderer.render(scene, camera);
44+
}
45+
```
46+
Note that we only needed to make one change, we replace the `fakeGps()` call with:
47+
```
48+
arjs.startGps();
49+
```
50+
Using the Geolocation API this will make the application start listening for GPS updates. *The nice thing is we do not need to do anything else. The `LocationBased` object automatically updates the camera x and z coordinates to reflect our current GPS location.* Specifically, the GPS latitude and longitude are converted to Spherical Mercator, the sign of `z` reversed (to match the OpenGL coordinate system), and the resulting coordinates used for the camera coordinates.
51+
52+
### Using the device orientation controls
53+
54+
Having looked at obtaining our real GPS position, we will now look at how we can use the orientation controls to ensure our AR scene matches the real world as we rotate the device around. This is, in principle, quite easy: we just need to create a `LocAR.DeviceOrientationControls` object and update it in our rendering function. This object is based on the original `DeviceOrientationControls` from three.js.
55+
56+
However, there is a slight problem. Unfortunately this will only work in Chrome on Android (it may also work in Chrome on iOS, this needs testing). This is due to the difficulty in obtaining absolute orientation (i.e. our orientation relative to north) using the device orientation API. This can be done on Chrome/Android using the `deviceorientationabsolute` event (and in fact, the `LocAR.DeviceOrientationControls` has been modified from the original to handle this event); it can also be done on Safari with `webkitCompassHeading` (but, due to the lack of an iDevice for testing, has not been implemented yet) but sadly it appears that support on Firefox is completely missing for now. See [this table of compatibility for absolute device orientation](https://developer.mozilla.org/en-US/docs/Web/API/Window/ondeviceorientationabsolute).
57+
58+
So it's recommended you use Chrome on Android for the moment. The example below shows the use of orientation tracking:
59+
60+
```javascript
61+
import * as THREE from 'three';
62+
import * as LocAR from 'locar';
63+
64+
const scene = new THREE.Scene();
65+
const camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.001, 100);
66+
const renderer = new THREE.WebGLRenderer();
67+
renderer.setSize(window.innerWidth, window.innerHeight);
68+
document.body.appendChild(renderer.domElement);
69+
70+
window.addEventListener("resize", e => {
71+
renderer.setSize(window.innerWidth, window.innerHeight);
72+
camera.aspect = window.innerWidth / window.innerHeight;
73+
camera.updateProjectionMatrix();
74+
});
75+
const box = new THREE.BoxGeometry(2,2,2);
76+
77+
const cube = new THREE.Mesh(box, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
78+
79+
const locar = new LocAR.LocationBased(scene, camera);
80+
const cam = new LocAR.WebcamRenderer(renderer);
81+
82+
// Create the device orientation tracker
83+
const deviceOrientationControls = new LocAR.DeviceOrientationControls(camera);
84+
85+
locar.startGps();
86+
locar.add(cube, -0.72, 51.0501);
87+
88+
renderer.setAnimationLoop(animate);
89+
90+
91+
function animate() {
92+
// Update the scene using the latest sensor readings
93+
deviceOrientationControls.update();
94+
95+
cam.update();
96+
renderer.render(scene, camera);
97+
}
98+
```
99+
100+
Note how we create a device orientation tracker with:
101+
```javascript
102+
const deviceOrientationControls = new LocAR.DeviceOrientationControls(camera);
103+
```
104+
105+
The device orientation tracker updates the camera, so we need to pass it in as an argument.
106+
107+
Also note how we update the device orientation tracker in our rendering function, so that new readings from the sensors are accounted for:
108+
109+
```javascript
110+
deviceOrientationControls.update();
111+
```
112+
113+
### Try it!
114+
115+
Try it out. As real GPS location and device orientation is used, you will need a mobile device. You should find that the red box appears in its real world position (ensure it's not too far from you, e.g. 0.001 degrees of longitude to the north) and, due to the use of orientation tracking, only appears in the field of view when you are facing its location.
116+
117+
### Adding four boxes to north, south, east and west
118+
119+
One issue on some devices with web AR is that the device's sensors (accelerometer, magnetometer) may be miscalibrated on a small number of devices. A good way of checking this is to write a simple app which displays four boxes to your north, south, east and west when a GPS location is first obtained. You can then check whether those boxes appear in their correct location. (In future we aim to produce a calibration tool to correct any miscalibration on your device).
120+
121+
Here is an enhanced version of the previous example, which will do this:
122+
123+
```javascript
124+
import * as THREE from 'three';
125+
import * as LocAR from 'locar';
126+
127+
const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.001, 1000);
128+
129+
const renderer = new THREE.WebGLRenderer();
130+
renderer.setSize(window.innerWidth, window.innerHeight);
131+
document.body.appendChild(renderer.domElement);
132+
133+
const scene = new THREE.Scene();
134+
135+
const locar = new LocAR.LocationBased(scene, camera);
136+
137+
window.addEventListener("resize", e => {
138+
renderer.setSize(window.innerWidth, window.innerHeight);
139+
camera.aspect = window.innerWidth / window.innerHeight;
140+
camera.updateProjectionMatrix();
141+
});
142+
143+
const cam = new LocAR.WebcamRenderer(renderer);
144+
145+
let firstLocation = true;
146+
147+
const deviceOrientationControls = new LocAR.DeviceOrientationControls(camera);
148+
149+
locar.on("gpsupdate", (pos, distMoved) => {
150+
if(firstLocation) {
151+
152+
const boxProps = [{
153+
latDis: 0.001,
154+
lonDis: 0,
155+
colour: 0xff0000
156+
}, {
157+
latDis: -0.001,
158+
lonDis: 0,
159+
colour: 0xffff00
160+
}, {
161+
latDis: 0,
162+
lonDis: -0.001,
163+
colour: 0x00ffff
164+
}, {
165+
latDis: 0,
166+
lonDis: 0.001,
167+
colour: 0x00ff00
168+
}];
169+
170+
const geom = new THREE.BoxGeometry(20,20,20);
171+
172+
for(const boxProp of boxProps) {
173+
const mesh = new THREE.Mesh(
174+
geom,
175+
new THREE.MeshBasicMaterial({color: boxProp.colour})
176+
);
177+
178+
locar.add(
179+
mesh,
180+
pos.coords.longitude + boxProp.lonDis,
181+
pos.coords.latitude + boxProp.latDis
182+
);
183+
}
184+
185+
firstLocation = false;
186+
}
187+
});
188+
189+
locar.startGps();
190+
191+
renderer.setAnimationLoop(animate);
192+
193+
function animate() {
194+
cam.update();
195+
deviceOrientationControls.update();
196+
renderer.render(scene, camera);
197+
}
198+
```
199+
Note how it works: when we get a location, we check whether this was the first GPS location obtained (to prevent the same boxes being added each time our GPS location changes). If it was, we add four boxes a short distance to the north (red), south (yellow), west (cyan) and east (green) of us.
200+
201+
Try it out, and if your sensors are calibrated correctly, you will see a red box to your north, a yellow box to your south, a cyan (light blue) box to your west and a green box to your east. These are relative to your *initial* position so as you move, the boxes' positions relative to you will change.

0 commit comments

Comments
 (0)