Преглед на файлове

Introduce the babylon.js plugin

customisations
alemart преди 11 месеца
родител
ревизия
111c8817ec
променени са 1 файла, в които са добавени 291 реда и са изтрити 0 реда
  1. 291
    0
      plugins/babylon-with-encantar.js

+ 291
- 0
plugins/babylon-with-encantar.js Целия файл

@@ -0,0 +1,291 @@
1
+/**
2
+ * babylon.js plugin for encantar.js
3
+ * @author Alexandre Martins <alemartf(at)gmail.com> (https://github.com/alemart/encantar-js)
4
+ * @license LGPL-3.0-or-later
5
+ */
6
+
7
+/* Usage of the indicated versions is encouraged */
8
+__THIS_PLUGIN_HAS_BEEN_TESTED_WITH__({
9
+    'encantar.js': { version: '0.3.0' },
10
+     'babylon.js': { version: '7.29.0' }
11
+});
12
+
13
+/**
14
+ * Base class for Augmented Reality experiences
15
+ */
16
+class ARDemo
17
+{
18
+    /**
19
+     * Start the AR session
20
+     * @abstract
21
+     * @returns {Promise<Session> | SpeedyPromise<Session>}
22
+     */
23
+    startSession()
24
+    {
25
+        throw new Error('Abstract method');
26
+    }
27
+
28
+    /**
29
+     * Initialization
30
+     * @abstract
31
+     * @param {ARSystem} ar
32
+     * @returns {void | Promise<void> | SpeedyPromise<void>}
33
+     */
34
+    init(ar)
35
+    {
36
+        throw new Error('Abstract method');
37
+    }
38
+
39
+    /**
40
+     * Animation loop
41
+     * @abstract
42
+     * @param {ARSystem} ar
43
+     * @returns {void}
44
+     */
45
+    update(ar)
46
+    {
47
+        throw new Error('Abstract method');
48
+    }
49
+
50
+    /**
51
+     * Release resources
52
+     * @param {ARSystem} ar
53
+     * @returns {void}
54
+     */
55
+    release(ar)
56
+    {
57
+        // optional implementation
58
+    }
59
+}
60
+
61
+/**
62
+ * Helper for creating Augmented Reality experiences
63
+ */
64
+class ARSystem
65
+{
66
+    /**
67
+     * AR Session
68
+     * @returns {Session}
69
+     */
70
+    get session()
71
+    {
72
+        return this._session;
73
+    }
74
+
75
+    /**
76
+     * Current frame: an object holding data to augment the physical scene.
77
+     * If the AR scene is not initialized, this will be null.
78
+     * @returns {Frame | null}
79
+     */
80
+    get frame()
81
+    {
82
+        return this._frame;
83
+    }
84
+
85
+    /**
86
+     * The root is a node that is automatically aligned to the physical scene.
87
+     * Objects of your virtual scene should be descendants of this node.
88
+     * @returns {BABYLON.TransformNode}
89
+     */
90
+    get root()
91
+    {
92
+        return this._root;
93
+    }
94
+
95
+    /**
96
+     * The babylon.js scene
97
+     * @returns {BABYLON.Scene}
98
+     */
99
+    get scene()
100
+    {
101
+        return this._scene;
102
+    }
103
+
104
+    /**
105
+     * A camera that is automatically adjusted for AR
106
+     * @returns {BABYLON.Camera}
107
+     */
108
+    get camera()
109
+    {
110
+        return this._camera;
111
+    }
112
+
113
+    /**
114
+     * The babylon.js engine
115
+     * @returns {BABYLON.Engine}
116
+     */
117
+    get engine()
118
+    {
119
+        return this._engine;
120
+    }
121
+
122
+    /**
123
+     * Constructor
124
+     */
125
+    constructor()
126
+    {
127
+        this._session = null;
128
+        this._frame = null;
129
+        this._origin = null;
130
+        this._root = null;
131
+        this._scene = null;
132
+        this._camera = null;
133
+        this._engine = null;
134
+    }
135
+}
136
+
137
+/**
138
+ * Enchant babylon.js with encantar.js!
139
+ * @param {ARDemo} demo
140
+ * @returns {Promise<ARSystem>}
141
+ */
142
+function encantar(demo)
143
+{
144
+    const ar = new ARSystem();
145
+    const flipZAxis = new BABYLON.Matrix().copyFromFloats(
146
+        1, 0, 0, 0,
147
+        0, 1, 0, 0,
148
+        0, 0,-1, 0,
149
+        0, 0, 0, 1
150
+    );
151
+
152
+    function animate(time, frame)
153
+    {
154
+        ar._frame = frame;
155
+        mix(frame);
156
+
157
+        demo.update(ar);
158
+
159
+        ar._scene.render(false);
160
+        ar._session.requestAnimationFrame(animate);
161
+    }
162
+
163
+    function mix(frame)
164
+    {
165
+        for(const result of frame.results) {
166
+            if(result.tracker.type == 'image-tracker') {
167
+                if(result.trackables.length > 0) {
168
+                    const trackable = result.trackables[0];
169
+                    const projectionMatrix = result.viewer.view.projectionMatrix;
170
+                    const viewMatrix = result.viewer.pose.viewMatrix;
171
+                    const modelMatrix = trackable.pose.transform.matrix;
172
+
173
+                    align(projectionMatrix, viewMatrix, modelMatrix);
174
+                    ar._origin.setEnabled(true);
175
+
176
+                    return;
177
+                }
178
+            }
179
+        }
180
+
181
+        ar._origin.setEnabled(false);
182
+    }
183
+
184
+    function align(projectionMatrix, viewMatrix, modelMatrix)
185
+    {
186
+        if(ar._scene.useRightHandedSystem)
187
+            ar._camera.freezeProjectionMatrix(convert(projectionMatrix));
188
+        else
189
+            ar._camera.freezeProjectionMatrix(convert(projectionMatrix).multiply(flipZAxis));
190
+
191
+        ar._camera.setViewMatrix(convert(viewMatrix));
192
+
193
+        convert(modelMatrix).decomposeToTransformNode(ar._origin);
194
+    }
195
+
196
+    function convert(matrix)
197
+    {
198
+        // encantar.js uses column vectors stored in column-major format,
199
+        // whereas babylon.js uses row vectors stored in row-major format
200
+        // (y = Ax vs y = xA). So, we return the transpose of the transpose.
201
+        return new BABYLON.Matrix().fromArray(matrix.read());
202
+    }
203
+
204
+    return Promise.resolve()
205
+    .then(() => {
206
+        return demo.startSession(); // Promise or SpeedyPromise
207
+    })
208
+    .then(session => {
209
+
210
+        ar._session = session;
211
+
212
+        ar._engine = new BABYLON.Engine(session.viewport.canvas, false, {
213
+            premultipliedAlpha: true
214
+        });
215
+        ar._engine.resize = function(forceSetSize = false) {
216
+            // make babylon.js respect the resolution of the viewport
217
+            const size = session.viewport.virtualSize;
218
+            this.setSize(size.width, size.height, forceSetSize);
219
+        };
220
+
221
+        ar._scene = new BABYLON.Scene(ar._engine);
222
+        ar._scene.useRightHandedSystem = true;
223
+        ar._scene.clearColor.set(0, 0, 0, 0);
224
+
225
+        ar._origin = new BABYLON.TransformNode('ar-origin', ar._scene);
226
+        ar._root = new BABYLON.TransformNode('ar-root', ar._scene);
227
+        ar._root.parent = ar._origin;
228
+        ar._origin.setEnabled(false);
229
+
230
+        ar._camera = new BABYLON.Camera('ar-camera', BABYLON.Vector3.Zero(), ar._scene);
231
+        ar._camera._tmpQuaternion = BABYLON.Quaternion.Identity();
232
+        ar._camera._customViewMatrix = BABYLON.Matrix.Identity();
233
+        ar._camera._getViewMatrix = function() { return this._customViewMatrix; };
234
+        ar._camera.setViewMatrix = function(matrix) {
235
+            this._customViewMatrix = matrix;
236
+            this.getViewMatrix(true);
237
+            this.getWorldMatrix().decompose(undefined, this._tmpQuaternion, this.position);
238
+            BABYLON.Axis.Y.rotateByQuaternionToRef(this._tmpQuaternion, this.upVector);
239
+            this._globalPosition.copyFrom(this.position);
240
+        };
241
+
242
+        session.addEventListener('end', event => {
243
+            ar._origin.setEnabled(false);
244
+            ar._frame = null;
245
+        });
246
+
247
+        session.viewport.addEventListener('resize', event => {
248
+            ar._engine.resize();
249
+        });
250
+
251
+        return Promise.resolve()
252
+        .then(() => {
253
+            return demo.init(ar);
254
+        })
255
+        .then(() => {
256
+            session.addEventListener('end', event => { demo.release(ar); });
257
+            session.requestAnimationFrame(animate);
258
+            return ar;
259
+        })
260
+        .catch(error => {
261
+            session.end();
262
+            throw error;
263
+        });
264
+
265
+    })
266
+    .catch(error => {
267
+        
268
+        console.error(error);
269
+        throw error;
270
+   
271
+    });
272
+}
273
+
274
+/**
275
+ * Version check
276
+ * @param {object} libs
277
+ */
278
+function __THIS_PLUGIN_HAS_BEEN_TESTED_WITH__(libs)
279
+{
280
+    window.addEventListener('load', () => {
281
+        try { AR, BABYLON;
282
+            const versionOf = { 'encantar.js': AR.version.replace(/-.*$/, ''), 'babylon.js': BABYLON.Engine.Version };
283
+            const check = (x,v,w) => v != w ? console.warn(`\n\n\nWARNING\n\nThis plugin 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;
284
+            for(const [lib, expected] of Object.entries(libs))
285
+                check(lib, expected.version, versionOf[lib]);
286
+        }
287
+        catch(e) {
288
+            alert(e.message);
289
+        }
290
+    });
291
+}

Loading…
Отказ
Запис