Kaynağa Gözat

Add glue codes: AFRAME, three.js

customisations
alemart 3 yıl önce
ebeveyn
işleme
97b1b09493
2 değiştirilmiş dosya ile 394 ekleme ve 0 silme
  1. 237
    0
      dist/aframe-with-martins.js
  2. 157
    0
      dist/three-with-martins.js

+ 237
- 0
dist/aframe-with-martins.js Dosyayı Görüntüle

@@ -0,0 +1,237 @@
1
+/**
2
+ * @file MARTINS.js & AFRAME glue code
3
+ * @version 1.0.1
4
+ * @author Alexandre Martins (https://github.com/alemart)
5
+ * @license AGPL-3.0-only or PolyForm-Perimeter-1.0.0
6
+ */
7
+
8
+/* Usage of the indicated versions is encouraged */
9
+__THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__({
10
+    'MARTINS.js': { version: '0.1.1' },
11
+        'AFRAME': { version: '1.3.0' }
12
+});
13
+
14
+/**
15
+ * Connect MARTINS.js to AFRAME
16
+ * @param {(canvas: HTMLCanvasElement) => Promise<Session> | SpeedyPromise<Session>} startARSession
17
+ */
18
+function linkMartinsToAFRAME(startARSession)
19
+{
20
+    AFRAME.registerSystem('ar-system', {
21
+        init()
22
+        {
23
+            this.state = {
24
+                isTracking: false,
25
+                frame: null,
26
+                referenceImage: null,
27
+                projectionMatrix: null,
28
+                viewMatrixInverse: null,
29
+                modelMatrix: null,
30
+            };
31
+            this.session = null;
32
+
33
+            return startARSession(this.el.canvas).then(session => {
34
+
35
+                this.session = session;
36
+
37
+                session.addEventListener('end', event => {
38
+                    this.state.isTracking = false;
39
+                });
40
+                session.viewport.addEventListener('resize', event => {
41
+                    this.updateRenderer(session.viewport.virtualSize);
42
+                });
43
+
44
+                if(session.viewport.canvas !== this.el.canvas) {
45
+                    session.end();
46
+                    throw new Error('Invalid AFRAME <canvas>');
47
+                }
48
+
49
+                const animate = (time, frame) => {
50
+                    this.updateState(frame);
51
+                    this.renderVirtualScene();
52
+                    session.requestAnimationFrame(animate);
53
+                };
54
+                session.requestAnimationFrame(animate);
55
+
56
+            }).catch(error => {
57
+
58
+                console.error(error);
59
+                alert(error.message);
60
+
61
+            });
62
+        },
63
+
64
+        updateState(frame)
65
+        {
66
+            const wasTracking = this.state.isTracking;
67
+
68
+            this.state.frame = frame;
69
+            this.state.isTracking = false;
70
+            this.state.referenceImage = null;
71
+
72
+            for(const result of frame.results) {
73
+                if(result.tracker.type == 'image-tracker') {
74
+                    if(result.trackables.length > 0) {
75
+                        const trackable = result.trackables[0];
76
+                        this.state.projectionMatrix = result.viewer.view.projectionMatrix;
77
+                        this.state.viewMatrixInverse = result.viewer.pose.transform.matrix;
78
+                        this.state.modelMatrix = trackable.pose.transform.matrix;
79
+                        this.state.referenceImage = trackable.referenceImage;
80
+                        this.state.isTracking = true;
81
+                    }
82
+                }
83
+            }
84
+
85
+            if(this.state.isTracking && !wasTracking)
86
+                this.updateRenderer(frame.session.viewport.virtualSize);
87
+        },
88
+
89
+        updateRenderer(size)
90
+        {
91
+            const renderer = this.el.renderer;
92
+            const resize = () => {
93
+                renderer.setPixelRatio(1.0);
94
+                renderer.setSize(size.width, size.height, false);
95
+            };
96
+
97
+            resize();
98
+            setTimeout(resize, 200); // internals of AFRAME (a-scene.js)
99
+        },
100
+
101
+        renderVirtualScene()
102
+        {
103
+            const scene = this.el;
104
+            if(!scene.camera || !scene.object3D)
105
+                return;
106
+
107
+            scene.delta = scene.clock.getDelta() * 1000;
108
+            scene.time = scene.clock.elapsedTime * 1000;
109
+            if(scene.isPlaying)
110
+                scene.tick(scene.time, scene.delta);
111
+
112
+            scene.object3D.background = null;
113
+            scene.renderer.render(scene.object3D, scene.camera);
114
+            scene.renderer.setAnimationLoop(null);
115
+        },
116
+    });
117
+
118
+    AFRAME.registerComponent('ar-root', {
119
+        schema: {
120
+            'image-target': { type: 'string', default: '' },
121
+        },
122
+
123
+        init()
124
+        {
125
+            this.arSystem = this.el.sceneEl.systems['ar-system'];
126
+
127
+            this.el.object3D.matrixAutoUpdate = false;
128
+            this.el.object3D.visible = false;
129
+        },
130
+
131
+        remove()
132
+        {
133
+            const session = this.arSystem.session;
134
+            session.end();
135
+        },
136
+
137
+        tick()
138
+        {
139
+            const ANY = '', target = this.data['image-target'];
140
+            const state = this.arSystem.state;
141
+
142
+            if(state.isTracking && (target === ANY || target === state.referenceImage.name)) {
143
+                this.alignVirtualScene(state.modelMatrix);
144
+                this.el.object3D.visible = true;
145
+            }
146
+            else
147
+                this.el.object3D.visible = false;
148
+        },
149
+
150
+        alignVirtualScene(modelMatrix)
151
+        {
152
+            const arRoot = this.el.object3D;
153
+
154
+            arRoot.matrix.fromArray(modelMatrix.read());
155
+            arRoot.updateMatrixWorld(true);
156
+        }
157
+    });
158
+
159
+    AFRAME.registerPrimitive('ar-root', AFRAME.utils.extendDeep({}, AFRAME.primitives.getMeshMixin(), {
160
+        defaultComponents: {
161
+            'ar-root': {}
162
+        },
163
+        mappings: {
164
+            'image-target': 'ar-root.image-target'
165
+        }
166
+    }));
167
+
168
+    AFRAME.registerComponent('ar-camera', {
169
+        init()
170
+        {
171
+            this.arSystem = this.el.sceneEl.systems['ar-system'];
172
+            this.arCamera = this.el.getObject3D('camera');
173
+
174
+            this.arCamera.matrixAutoUpdate = false;
175
+
176
+            this.el.setAttribute('camera', { active: true });
177
+            this.el.setAttribute('wasd-controls', { enabled: false });
178
+            this.el.setAttribute('look-controls', { enabled: false });
179
+            this.el.setAttribute('position', { x: 0, y: 0, z: 0 }); // AFRAME sets y = 1.6m for VR
180
+        },
181
+
182
+        tick()
183
+        {
184
+            const state = this.arSystem.state;
185
+
186
+            if(state.isTracking)
187
+                this.updateCamera(state.projectionMatrix, state.viewMatrixInverse);
188
+        },
189
+
190
+        updateCamera(projectionMatrix, viewMatrixInverse)
191
+        {
192
+            const arCamera = this.arCamera;
193
+
194
+            arCamera.projectionMatrix.fromArray(projectionMatrix.read());
195
+            arCamera.projectionMatrixInverse.copy(arCamera.projectionMatrix).invert();
196
+            arCamera.matrix.fromArray(viewMatrixInverse.read());
197
+            arCamera.updateMatrixWorld(true);
198
+        }
199
+    });
200
+
201
+    /*
202
+    // AFRAME won't catch this in setupInitialCamera()
203
+    AFRAME.registerPrimitive('ar-camera', AFRAME.utils.extendDeep({}, AFRAME.primitives.getMeshMixin(), {
204
+        defaultComponents: {
205
+            'ar-camera': {}
206
+        }
207
+    }));
208
+    */
209
+
210
+    AFRAME.registerComponent('ar-scene', {
211
+        init()
212
+        {
213
+            this.el.setAttribute('vr-mode-ui', { enabled: false });
214
+            this.el.setAttribute('embedded', true);
215
+            this.el.setAttribute('renderer', { alpha: true });
216
+        }
217
+    });
218
+};
219
+
220
+// Start automatically
221
+if(typeof startARSession === 'function')
222
+    linkMartinsToAFRAME(startARSession);
223
+else
224
+    alert('Missing startARSession');
225
+
226
+/**
227
+ * Version check
228
+ * @param {object} json
229
+ */
230
+function __THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__(json)
231
+{
232
+    try { Martins, AFRAME;
233
+    const versionOf = { 'MARTINS.js': Martins.version.replace(/-.*$/, ''), 'AFRAME': AFRAME.version };
234
+    const check = (x,v,w) => v !== w ? console.warn(`\n\n\nWARNING\n\nThis glue code has been tested with ${x} version ${v}. The version in use is ${w}. Usage of ${x} version ${v} is recommended instead.\n\n\n`) : void 0;
235
+    for(const [x, expected] of Object.entries(json)) check(x, expected.version, versionOf[x]);
236
+    } catch(e) { alert(e.message); }
237
+}

+ 157
- 0
dist/three-with-martins.js Dosyayı Görüntüle

@@ -0,0 +1,157 @@
1
+/**
2
+ * @file MARTINS.js & THREE.js glue code
3
+ * @version 1.0.1
4
+ * @author Alexandre Martins (https://github.com/alemart)
5
+ * @license AGPL-3.0-only or PolyForm-Perimeter-1.0.0
6
+ */
7
+
8
+/* Usage of the indicated versions is encouraged */
9
+__THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__({
10
+    'MARTINS.js': { version: '0.1.1' },
11
+      'THREE.js': { version: '138' }
12
+});
13
+
14
+/**
15
+ * Use this object to create your augmented scene
16
+ * @typedef {object} ARSystem
17
+ * @property {Session} session MARTINS.js Session
18
+ * @property {Frame} frame current Frame
19
+ * @property {ReferenceImage | null} referenceImage corresponds to the target being tracked (if any)
20
+ * @property {THREE.Scene} scene THREE.js Scene
21
+ * @property {THREE.Group} root a 3D object that is automatically aligned with the physical target
22
+ * @property {THREE.Camera} camera a camera adjusted for AR
23
+ * @property {THREE.WebGLRenderer} renderer THREE.js renderer
24
+ */
25
+
26
+/**
27
+ * Connect MARTINS.js to THREE.js
28
+ * @param {() => Promise<Session> | SpeedyPromise<Session>} startARSession
29
+ * @param {(ar: ARSystem) => void} [animateVirtualScene] animation callback
30
+ * @param {(ar: ARSystem) => void | Promise<void> | SpeedyPromise<Session>} [initializeVirtualScene] initialization callback
31
+ * @returns {Promise<ARSystem> | SpeedyPromise<ARSystem>}
32
+ */
33
+function linkMartinsToTHREE(startARSession, animateVirtualScene, initializeVirtualScene)
34
+{
35
+    const ar = /** @type {ARSystem} */ ({
36
+        session: null,
37
+        frame: null,
38
+        referenceImage: null,
39
+        scene: null,
40
+        root: null,
41
+        camera: null,
42
+        renderer: null,
43
+    });
44
+
45
+    function mix(frame)
46
+    {
47
+        ar.root.visible = false;
48
+        ar.referenceImage = null;
49
+
50
+        for(const result of frame.results) {
51
+            if(result.tracker.type == 'image-tracker') {
52
+                if(result.trackables.length > 0) {
53
+                    const trackable = result.trackables[0];
54
+                    const projectionMatrix = result.viewer.view.projectionMatrix;
55
+                    const viewMatrixInverse = result.viewer.pose.transform.matrix;
56
+                    const modelMatrix = trackable.pose.transform.matrix;
57
+
58
+                    ar.root.visible = true;
59
+                    ar.referenceImage = trackable.referenceImage;
60
+
61
+                    align(projectionMatrix, viewMatrixInverse, modelMatrix);
62
+                }
63
+            }
64
+        }
65
+    }
66
+
67
+    function align(projectionMatrix, viewMatrixInverse, modelMatrix)
68
+    {
69
+        ar.camera.projectionMatrix.fromArray(projectionMatrix.read());
70
+        ar.camera.projectionMatrixInverse.copy(ar.camera.projectionMatrix).invert();
71
+        ar.camera.matrix.fromArray(viewMatrixInverse.read());
72
+        ar.camera.updateMatrixWorld(true);
73
+        ar.root.matrix.fromArray(modelMatrix.read());
74
+        ar.root.updateMatrixWorld(true);
75
+    }
76
+
77
+    function animate(time, frame)
78
+    {
79
+        ar.frame = frame;
80
+        mix(ar.frame);
81
+
82
+        animateVirtualScene.call(undefined, ar);
83
+
84
+        ar.renderer.render(ar.scene, ar.camera);
85
+        ar.session.requestAnimationFrame(animate);
86
+    }
87
+
88
+
89
+
90
+
91
+    if(typeof animateVirtualScene !== 'function')
92
+        animateVirtualScene = (ar => void 0);
93
+
94
+    if(typeof initializeVirtualScene !== 'function')
95
+        initializeVirtualScene = (ar => void 0);
96
+
97
+    return startARSession().then(session => {
98
+
99
+        ar.session = session;
100
+
101
+        ar.scene = new THREE.Scene();
102
+        ar.camera = new THREE.PerspectiveCamera();
103
+        ar.camera.matrixAutoUpdate = false;
104
+
105
+        ar.root = new THREE.Group();
106
+        ar.root.matrixAutoUpdate = false;
107
+        ar.scene.add(ar.root);
108
+
109
+        ar.renderer = new THREE.WebGLRenderer({
110
+            canvas: ar.session.viewport.canvas,
111
+            alpha: true,
112
+        });
113
+
114
+        ar.session.addEventListener('end', event => {
115
+            ar.root.visible = false;
116
+            ar.frame = null;
117
+            ar.referenceImage = null;
118
+        });
119
+
120
+        ar.session.viewport.addEventListener('resize', event => {
121
+            const size = ar.session.viewport.virtualSize;
122
+            ar.renderer.setPixelRatio(1.0);
123
+            ar.renderer.setSize(size.width, size.height, false);
124
+        });
125
+
126
+        let init = initializeVirtualScene.call(undefined, ar);
127
+        if(!(typeof init === 'object' && 'then' in init))
128
+            init = Promise.resolve();
129
+
130
+        return init.then(() => {
131
+            ar.session.requestAnimationFrame(animate);
132
+            return ar;
133
+        });
134
+
135
+    }).catch(error => {
136
+        
137
+        console.error(error);
138
+        alert(error.message);
139
+        return ar;
140
+   
141
+    });
142
+}
143
+
144
+/**
145
+ * Version check
146
+ * @param {object} json
147
+ */
148
+function __THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__(json)
149
+{
150
+    window.addEventListener('load', () => {
151
+    try { Martins, __THREE__;
152
+    const versionOf = { 'MARTINS.js': Martins.version.replace(/-.*$/, ''), 'THREE.js': __THREE__ };
153
+    const check = (x,v,w) => v !== w ? console.warn(`\n\n\nWARNING\n\nThis glue code has been tested with ${x} version ${v}. The version in use is ${w}. Usage of ${x} version ${v} is recommended instead.\n\n\n`) : void 0;
154
+    for(const [x, expected] of Object.entries(json)) check(x, expected.version, versionOf[x]);
155
+    } catch(e) { alert(e.message); }
156
+    });
157
+}

Loading…
İptal
Kaydet