lunchboxjs 0.2.1016 → 0.2.1017-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lunchboxjs.js +307 -498
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +304 -492
- package/package.json +81 -79
|
@@ -2,34 +2,31 @@ import { isRef, isVNode, toRaw, defineComponent, createVNode, resolveComponent,
|
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
|
|
4
4
|
function find(target) {
|
|
5
|
-
target = isRef(target) ? target.value : target;
|
|
6
|
-
|
|
5
|
+
target = isRef(target) ? target.value : target;
|
|
6
|
+
// handle standard lunchbox node
|
|
7
7
|
if (isLunchboxStandardNode(target)) {
|
|
8
8
|
return target?.instance;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
}
|
|
10
|
+
// handle component
|
|
12
11
|
if (isLunchboxComponent(target)) {
|
|
13
12
|
return target?.$el?.instance;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
}
|
|
14
|
+
// handle vnode
|
|
17
15
|
if (isVNode(target)) {
|
|
18
16
|
return target.el?.instance;
|
|
19
17
|
}
|
|
20
|
-
|
|
21
18
|
return null;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
const get = (obj, path, defValue) => {
|
|
25
22
|
// If path is not defined or it has false value
|
|
26
|
-
if (!path) return undefined;
|
|
23
|
+
if (!path) return undefined;
|
|
24
|
+
// Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
|
|
27
25
|
// Regex explained: https://regexr.com/58j0k
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g);
|
|
27
|
+
// Find value
|
|
28
|
+
const result = pathArray?.reduce((prevObj, key) => prevObj && prevObj[key], obj);
|
|
29
|
+
// If found value is undefined return default value; otherwise return the value
|
|
33
30
|
return result === undefined ? defValue : result;
|
|
34
31
|
};
|
|
35
32
|
|
|
@@ -42,18 +39,15 @@ const buildIsNumber = () => {
|
|
|
42
39
|
* Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
43
40
|
* Available under MIT license <https://lodash.com/license>
|
|
44
41
|
*/
|
|
45
|
-
|
|
46
42
|
/** `Object#toString` result references. */
|
|
47
43
|
const numberTag = '[object Number]';
|
|
48
44
|
/** Used for built-in method references. */
|
|
49
|
-
|
|
50
45
|
const objectProto = Object.prototype;
|
|
51
46
|
/**
|
|
52
47
|
* Used to resolve the
|
|
53
48
|
* [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
|
|
54
49
|
* of values.
|
|
55
50
|
*/
|
|
56
|
-
|
|
57
51
|
const objectToString = objectProto.toString;
|
|
58
52
|
/**
|
|
59
53
|
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
|
@@ -79,7 +73,6 @@ const buildIsNumber = () => {
|
|
|
79
73
|
* _.isObjectLike(null);
|
|
80
74
|
* // => false
|
|
81
75
|
*/
|
|
82
|
-
|
|
83
76
|
function isObjectLike(value) {
|
|
84
77
|
return !!value && typeof value == 'object';
|
|
85
78
|
}
|
|
@@ -109,15 +102,11 @@ const buildIsNumber = () => {
|
|
|
109
102
|
* _.isNumber('3');
|
|
110
103
|
* // => false
|
|
111
104
|
*/
|
|
112
|
-
|
|
113
|
-
|
|
114
105
|
const output = function isNumber(value) {
|
|
115
106
|
return typeof value == 'number' || isObjectLike(value) && objectToString.call(value) == numberTag;
|
|
116
107
|
};
|
|
117
|
-
|
|
118
108
|
return output;
|
|
119
109
|
};
|
|
120
|
-
|
|
121
110
|
const isNumber = buildIsNumber();
|
|
122
111
|
|
|
123
112
|
const set = (obj, path, value) => {
|
|
@@ -130,10 +119,11 @@ const set = (obj, path, value) => {
|
|
|
130
119
|
}, obj);
|
|
131
120
|
};
|
|
132
121
|
|
|
122
|
+
// MAKE SURE THESE MATCH VALUES IN types.EventKey
|
|
133
123
|
/** Type check on whether target is a Lunchbox.EventKey */
|
|
134
|
-
|
|
135
124
|
const isEventKey = target => {
|
|
136
|
-
return ['onClick', 'onContextMenu', 'onDoubleClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove',
|
|
125
|
+
return ['onClick', 'onContextMenu', 'onDoubleClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove',
|
|
126
|
+
// 'onPointerMissed',
|
|
137
127
|
// 'onUpdate',
|
|
138
128
|
'onWheel'].includes(target);
|
|
139
129
|
};
|
|
@@ -152,61 +142,61 @@ const isLunchboxRootNode = node => {
|
|
|
152
142
|
};
|
|
153
143
|
|
|
154
144
|
/** Create a new Lunchbox comment node. */
|
|
155
|
-
|
|
156
145
|
function createCommentNode(options = {}) {
|
|
157
146
|
const defaults = {
|
|
158
147
|
text: options.text ?? ''
|
|
159
148
|
};
|
|
160
|
-
return new MiniDom.RendererCommentNode({
|
|
149
|
+
return new MiniDom.RendererCommentNode({
|
|
150
|
+
...defaults,
|
|
161
151
|
...options,
|
|
162
152
|
metaType: 'commentMeta'
|
|
163
153
|
});
|
|
164
154
|
}
|
|
165
155
|
/** Create a new DOM node. */
|
|
166
|
-
|
|
167
156
|
function createDomNode(options = {}) {
|
|
168
157
|
const domElement = document.createElement(options.type ?? '');
|
|
169
158
|
const defaults = {
|
|
170
159
|
domElement
|
|
171
160
|
};
|
|
172
|
-
const node = new MiniDom.RendererDomNode({
|
|
161
|
+
const node = new MiniDom.RendererDomNode({
|
|
162
|
+
...defaults,
|
|
173
163
|
...options,
|
|
174
164
|
metaType: 'domMeta'
|
|
175
165
|
});
|
|
176
166
|
return node;
|
|
177
167
|
}
|
|
178
168
|
/** Create a new Lunchbox text node. */
|
|
179
|
-
|
|
180
169
|
function createTextNode(options = {}) {
|
|
181
170
|
const defaults = {
|
|
182
171
|
text: options.text ?? ''
|
|
183
172
|
};
|
|
184
|
-
return new MiniDom.RendererTextNode({
|
|
173
|
+
return new MiniDom.RendererTextNode({
|
|
174
|
+
...options,
|
|
185
175
|
...defaults,
|
|
186
176
|
metaType: 'textMeta'
|
|
187
177
|
});
|
|
188
178
|
}
|
|
189
179
|
/** Create a new Lunchbox standard node. */
|
|
190
|
-
|
|
191
180
|
function createNode(options = {}, props = {}) {
|
|
192
181
|
const defaults = {
|
|
193
182
|
attached: options.attached ?? [],
|
|
194
183
|
attachedArray: options.attachedArray ?? {},
|
|
195
184
|
instance: options.instance ?? null
|
|
196
185
|
};
|
|
197
|
-
const node = new MiniDom.RendererStandardNode({
|
|
186
|
+
const node = new MiniDom.RendererStandardNode({
|
|
187
|
+
...options,
|
|
198
188
|
...defaults,
|
|
199
189
|
metaType: 'standardMeta'
|
|
200
190
|
});
|
|
201
|
-
|
|
202
191
|
if (node.type && !isLunchboxRootNode(node) && !node.instance) {
|
|
203
|
-
node.instance = instantiateThreeObject({
|
|
204
|
-
|
|
192
|
+
node.instance = instantiateThreeObject({
|
|
193
|
+
...node,
|
|
194
|
+
props: {
|
|
195
|
+
...node.props,
|
|
205
196
|
...props
|
|
206
197
|
}
|
|
207
198
|
});
|
|
208
199
|
}
|
|
209
|
-
|
|
210
200
|
return node;
|
|
211
201
|
}
|
|
212
202
|
|
|
@@ -221,14 +211,12 @@ function addEventListener({
|
|
|
221
211
|
if (!node.eventListeners[key]) {
|
|
222
212
|
node.eventListeners[key] = [];
|
|
223
213
|
}
|
|
224
|
-
|
|
225
214
|
if (!node.eventListenerRemoveFunctions[key]) {
|
|
226
215
|
node.eventListenerRemoveFunctions[key] = [];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
216
|
+
}
|
|
217
|
+
// add event listener
|
|
218
|
+
node.eventListeners[key].push(value);
|
|
219
|
+
// if we need it, let's get/create the main raycaster
|
|
232
220
|
if (interactionsRequiringRaycaster.includes(key)) {
|
|
233
221
|
if (node.instance && !interactables.value.includes(node)) {
|
|
234
222
|
// add to interactables
|
|
@@ -236,27 +224,25 @@ function addEventListener({
|
|
|
236
224
|
node.eventListenerRemoveFunctions[key].push(() => {
|
|
237
225
|
// remove from interactables
|
|
238
226
|
const idx = interactables.value.indexOf(node);
|
|
239
|
-
|
|
240
227
|
if (idx !== -1) {
|
|
241
228
|
interactables.value.splice(idx, 1);
|
|
242
229
|
}
|
|
243
230
|
});
|
|
244
231
|
}
|
|
245
232
|
}
|
|
246
|
-
|
|
247
233
|
return node;
|
|
248
234
|
}
|
|
249
|
-
const interactionsRequiringRaycaster = ['onClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove'
|
|
235
|
+
const interactionsRequiringRaycaster = ['onClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove'
|
|
236
|
+
// 'onPointerMissed',
|
|
250
237
|
];
|
|
251
238
|
|
|
252
239
|
const resizeCanvas = (camera, renderer, scene, width, height) => {
|
|
253
240
|
// ignore if no element
|
|
254
241
|
if (!renderer?.domElement || !scene || !camera) return;
|
|
255
242
|
width = width ?? window.innerWidth;
|
|
256
|
-
height = height ?? window.innerHeight;
|
|
257
|
-
|
|
243
|
+
height = height ?? window.innerHeight;
|
|
244
|
+
// update camera
|
|
258
245
|
const aspect = width / height;
|
|
259
|
-
|
|
260
246
|
if (camera.type?.toLowerCase() === 'perspectivecamera') {
|
|
261
247
|
const perspectiveCamera = camera;
|
|
262
248
|
perspectiveCamera.aspect = aspect;
|
|
@@ -270,11 +256,10 @@ const resizeCanvas = (camera, renderer, scene, width, height) => {
|
|
|
270
256
|
orthoCamera.right = 10;
|
|
271
257
|
orthoCamera.left = -10;
|
|
272
258
|
orthoCamera.updateProjectionMatrix();
|
|
273
|
-
} else ;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
259
|
+
} else ;
|
|
260
|
+
// update canvas
|
|
261
|
+
renderer.setSize(width, height);
|
|
262
|
+
// render immediately so there's no flicker
|
|
278
263
|
if (scene && camera) {
|
|
279
264
|
renderer.render(toRaw(scene), toRaw(camera));
|
|
280
265
|
}
|
|
@@ -289,42 +274,37 @@ const getInnerDimensions = node => {
|
|
|
289
274
|
height
|
|
290
275
|
};
|
|
291
276
|
};
|
|
292
|
-
|
|
293
277
|
const prepCanvas = (container, camera, renderer, scene, sizePolicy) => {
|
|
294
278
|
const containerElement = container.value?.domElement;
|
|
295
|
-
if (!containerElement) throw new Error('missing container');
|
|
296
|
-
|
|
279
|
+
if (!containerElement) throw new Error('missing container');
|
|
280
|
+
// save and size element
|
|
297
281
|
const resizeCanvasByPolicy = () => {
|
|
298
282
|
if (sizePolicy === 'container') {
|
|
299
283
|
const dims = getInnerDimensions(containerElement);
|
|
300
284
|
resizeCanvas(camera, renderer, scene, dims.width, dims.height);
|
|
301
285
|
} else resizeCanvas(camera, renderer, scene);
|
|
302
286
|
};
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
287
|
+
resizeCanvasByPolicy();
|
|
288
|
+
// attach listeners
|
|
306
289
|
let observer = new ResizeObserver(() => {
|
|
307
290
|
resizeCanvasByPolicy();
|
|
308
|
-
});
|
|
309
|
-
|
|
291
|
+
});
|
|
292
|
+
// window.addEventListener('resize', resizeCanvas)
|
|
310
293
|
if (containerElement) {
|
|
311
294
|
observer.observe(containerElement);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
295
|
+
}
|
|
296
|
+
// cleanup
|
|
315
297
|
return {
|
|
316
298
|
dispose() {
|
|
317
299
|
if (containerElement) {
|
|
318
300
|
observer.unobserve(containerElement);
|
|
319
301
|
}
|
|
320
302
|
}
|
|
321
|
-
|
|
322
303
|
};
|
|
323
304
|
};
|
|
324
305
|
|
|
325
306
|
const LunchboxScene = defineComponent({
|
|
326
307
|
name: 'LunchboxScene',
|
|
327
|
-
|
|
328
308
|
setup(props, {
|
|
329
309
|
slots
|
|
330
310
|
}) {
|
|
@@ -332,12 +312,10 @@ const LunchboxScene = defineComponent({
|
|
|
332
312
|
default: () => [slots.default?.()]
|
|
333
313
|
});
|
|
334
314
|
}
|
|
335
|
-
|
|
336
315
|
});
|
|
337
316
|
|
|
338
317
|
const LunchboxEventHandlers = defineComponent({
|
|
339
318
|
name: 'LunchboxEventHandlers',
|
|
340
|
-
|
|
341
319
|
setup() {
|
|
342
320
|
const interactables = useLunchboxInteractables();
|
|
343
321
|
const globals = useGlobals();
|
|
@@ -348,7 +326,6 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
348
326
|
const inputActive = ref(false);
|
|
349
327
|
let currentIntersections = [];
|
|
350
328
|
const raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, 0, -1));
|
|
351
|
-
|
|
352
329
|
const fireEventsFromIntersections = ({
|
|
353
330
|
element,
|
|
354
331
|
eventKeys,
|
|
@@ -364,52 +341,44 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
364
341
|
});
|
|
365
342
|
}
|
|
366
343
|
});
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
|
|
344
|
+
};
|
|
345
|
+
// add mouse listener to renderer DOM element when the element is ready
|
|
370
346
|
onRendererReady(v => {
|
|
371
|
-
if (!v?.domElement) return;
|
|
372
|
-
|
|
347
|
+
if (!v?.domElement) return;
|
|
348
|
+
// we have a DOM element, so let's add mouse listeners
|
|
373
349
|
const {
|
|
374
350
|
domElement
|
|
375
351
|
} = v;
|
|
376
|
-
|
|
377
352
|
const mouseMoveListener = evt => {
|
|
378
353
|
const screenWidth = (domElement.width ?? 1) / globals.dpr;
|
|
379
354
|
const screenHeight = (domElement.height ?? 1) / globals.dpr;
|
|
380
355
|
mousePos.value.x = evt.offsetX / screenWidth * 2 - 1;
|
|
381
356
|
mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1;
|
|
382
357
|
};
|
|
383
|
-
|
|
384
358
|
const mouseDownListener = () => inputActive.value = true;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
359
|
+
const mouseUpListener = () => inputActive.value = false;
|
|
360
|
+
// add mouse events
|
|
389
361
|
domElement.addEventListener('pointermove', mouseMoveListener);
|
|
390
362
|
domElement.addEventListener('pointerdown', mouseDownListener);
|
|
391
363
|
domElement.addEventListener('pointerup', mouseUpListener);
|
|
392
364
|
});
|
|
393
365
|
const camera = useCamera();
|
|
394
|
-
|
|
395
366
|
const update = () => {
|
|
396
367
|
const c = camera.value;
|
|
397
|
-
if (!c) return;
|
|
398
|
-
|
|
368
|
+
if (!c) return;
|
|
369
|
+
// console.log(camera.value)
|
|
399
370
|
raycaster.setFromCamera(mousePos.value, c);
|
|
400
371
|
const intersections = raycaster.intersectObjects(interactables?.value.map(v => v.instance) ?? []);
|
|
401
372
|
let leaveValues = [],
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
leaveValues = currentIntersections.map(v => v.intersection);
|
|
406
|
-
|
|
373
|
+
entering = [],
|
|
374
|
+
staying = [];
|
|
375
|
+
// intersection arrays
|
|
376
|
+
leaveValues = currentIntersections.map(v => v.intersection);
|
|
377
|
+
// element arrays
|
|
407
378
|
intersections?.forEach(intersection => {
|
|
408
379
|
const currentIdx = currentIntersections.findIndex(v => v.intersection.object === intersection.object);
|
|
409
|
-
|
|
410
380
|
if (currentIdx === -1) {
|
|
411
381
|
const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
|
|
412
|
-
|
|
413
382
|
if (found) {
|
|
414
383
|
entering.push({
|
|
415
384
|
element: found,
|
|
@@ -418,18 +387,15 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
418
387
|
}
|
|
419
388
|
} else {
|
|
420
389
|
const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
|
|
421
|
-
|
|
422
390
|
if (found) {
|
|
423
391
|
staying.push({
|
|
424
392
|
element: found,
|
|
425
393
|
intersection
|
|
426
394
|
});
|
|
427
395
|
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
|
|
396
|
+
}
|
|
397
|
+
// this is a current intersection, so it won't be in our `leave` array
|
|
431
398
|
const leaveIdx = leaveValues.findIndex(v => v.object.uuid === intersection.object.uuid);
|
|
432
|
-
|
|
433
399
|
if (leaveIdx !== -1) {
|
|
434
400
|
leaveValues.splice(leaveIdx, 1);
|
|
435
401
|
}
|
|
@@ -439,8 +405,8 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
439
405
|
element: interactables?.value.find(interactable => interactable.instance?.uuid === intersection.object.uuid),
|
|
440
406
|
intersection
|
|
441
407
|
};
|
|
442
|
-
});
|
|
443
|
-
|
|
408
|
+
});
|
|
409
|
+
// new interactions
|
|
444
410
|
entering.forEach(({
|
|
445
411
|
element,
|
|
446
412
|
intersection
|
|
@@ -450,8 +416,8 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
450
416
|
eventKeys: ['onPointerEnter'],
|
|
451
417
|
intersection
|
|
452
418
|
});
|
|
453
|
-
});
|
|
454
|
-
|
|
419
|
+
});
|
|
420
|
+
// unchanged interactions
|
|
455
421
|
staying.forEach(({
|
|
456
422
|
element,
|
|
457
423
|
intersection
|
|
@@ -462,8 +428,8 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
462
428
|
eventKeys,
|
|
463
429
|
intersection
|
|
464
430
|
});
|
|
465
|
-
});
|
|
466
|
-
|
|
431
|
+
});
|
|
432
|
+
// exited interactions
|
|
467
433
|
leaving.forEach(({
|
|
468
434
|
element,
|
|
469
435
|
intersection
|
|
@@ -476,28 +442,24 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
476
442
|
});
|
|
477
443
|
});
|
|
478
444
|
currentIntersections = [].concat(entering, staying);
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
|
|
445
|
+
};
|
|
446
|
+
// update function
|
|
482
447
|
onBeforeRender(update);
|
|
483
|
-
|
|
484
448
|
const teardown = () => offBeforeRender(update);
|
|
485
|
-
|
|
486
449
|
onBeforeUnmount(teardown);
|
|
487
450
|
const clickEventKeys = ['onClick', 'onPointerDown', 'onPointerUp'];
|
|
488
451
|
watch(inputActive, isDown => {
|
|
489
452
|
// run raycaster on click (necessary when `update` is not automatically called,
|
|
490
453
|
// for example in `updateSource` functions)
|
|
491
|
-
update();
|
|
454
|
+
update();
|
|
455
|
+
// meshes with multiple intersections receive multiple callbacks by default -
|
|
492
456
|
// let's make it so they only receive one callback of each type per frame.
|
|
493
457
|
// (ie usually when you click on a mesh, you expect only one click event to fire, even
|
|
494
458
|
// if there are technically multiple intersections with that mesh)
|
|
495
|
-
|
|
496
459
|
const uuidsInteractedWithThisFrame = [];
|
|
497
460
|
currentIntersections.forEach(v => {
|
|
498
461
|
clickEventKeys.forEach(key => {
|
|
499
462
|
const id = v.element.uuid + key;
|
|
500
|
-
|
|
501
463
|
if (isDown && (key === 'onClick' || key === 'onPointerDown')) {
|
|
502
464
|
if (!uuidsInteractedWithThisFrame.includes(id)) {
|
|
503
465
|
v.element.eventListeners[key]?.forEach(cb => cb({
|
|
@@ -515,16 +477,14 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
515
477
|
}
|
|
516
478
|
});
|
|
517
479
|
});
|
|
518
|
-
});
|
|
480
|
+
});
|
|
481
|
+
// return arbitrary object to ensure instantiation
|
|
519
482
|
// TODO: why can't we return a <raycaster/> here?
|
|
520
|
-
|
|
521
483
|
return () => createVNode(resolveComponent("object3D"), null, null);
|
|
522
484
|
}
|
|
523
|
-
|
|
524
485
|
});
|
|
525
486
|
|
|
526
487
|
/** fixed & fill styling for container */
|
|
527
|
-
|
|
528
488
|
const fillStyle = position => {
|
|
529
489
|
return {
|
|
530
490
|
position,
|
|
@@ -537,7 +497,6 @@ const fillStyle = position => {
|
|
|
537
497
|
display: 'block'
|
|
538
498
|
};
|
|
539
499
|
};
|
|
540
|
-
|
|
541
500
|
const LunchboxWrapper = defineComponent({
|
|
542
501
|
name: 'Lunchbox',
|
|
543
502
|
props: {
|
|
@@ -559,7 +518,6 @@ const LunchboxWrapper = defineComponent({
|
|
|
559
518
|
zoom: Number,
|
|
560
519
|
updateSource: Object
|
|
561
520
|
},
|
|
562
|
-
|
|
563
521
|
setup(props, context) {
|
|
564
522
|
const canvas = ref();
|
|
565
523
|
let dpr = props.dpr ?? -1;
|
|
@@ -571,101 +529,87 @@ const LunchboxWrapper = defineComponent({
|
|
|
571
529
|
const updateGlobals = useUpdateGlobals();
|
|
572
530
|
const app = useApp();
|
|
573
531
|
const consolidatedCameraProperties = reactive({});
|
|
574
|
-
const startCallbacks = useStartCallbacks();
|
|
575
|
-
|
|
532
|
+
const startCallbacks = useStartCallbacks();
|
|
533
|
+
// https://threejs.org/docs/index.html#manual/en/introduction/Color-management
|
|
576
534
|
if (props.r3f && THREE?.ColorManagement) {
|
|
577
535
|
THREE.ColorManagement.legacyMode = false;
|
|
578
536
|
}
|
|
579
|
-
|
|
580
|
-
|
|
537
|
+
const interactables = useLunchboxInteractables();
|
|
538
|
+
// MOUNT
|
|
581
539
|
// ====================
|
|
582
|
-
|
|
583
540
|
onMounted(async () => {
|
|
584
541
|
// canvas needs to exist (or user needs to handle it on their own)
|
|
585
|
-
if (!canvas.value && !context.slots?.renderer?.()?.length) throw new Error('missing canvas');
|
|
586
|
-
|
|
542
|
+
if (!canvas.value && !context.slots?.renderer?.()?.length) throw new Error('missing canvas');
|
|
543
|
+
// no camera provided, so let's create one
|
|
587
544
|
if (!context.slots?.camera?.()?.length) {
|
|
588
545
|
if (props.cameraPosition) {
|
|
589
546
|
consolidatedCameraProperties.position = props.cameraPosition;
|
|
590
547
|
}
|
|
591
|
-
|
|
592
548
|
if (props.cameraLook || props.cameraLookAt) {
|
|
593
549
|
consolidatedCameraProperties.lookAt = props.cameraLook || props.cameraLookAt;
|
|
594
550
|
}
|
|
595
|
-
|
|
596
551
|
if (props.zoom !== undefined) {
|
|
597
552
|
consolidatedCameraProperties.zoom = props.zoom;
|
|
598
553
|
}
|
|
599
|
-
}
|
|
554
|
+
}
|
|
555
|
+
// SCENE
|
|
600
556
|
// ====================
|
|
601
557
|
// set background color
|
|
602
|
-
|
|
603
|
-
|
|
604
558
|
if (scene.value?.$el?.instance && props.background) {
|
|
605
559
|
scene.value.$el.instance.background = new THREE.Color(props.background);
|
|
606
|
-
}
|
|
560
|
+
}
|
|
561
|
+
// MISC PROPERTIES
|
|
607
562
|
// ====================
|
|
608
|
-
|
|
609
|
-
|
|
610
563
|
if (dpr === -1) {
|
|
611
564
|
dpr = window.devicePixelRatio;
|
|
612
565
|
}
|
|
613
|
-
|
|
614
566
|
updateGlobals?.({
|
|
615
567
|
dpr
|
|
616
568
|
});
|
|
617
|
-
|
|
618
|
-
|
|
569
|
+
while (!renderer.value?.$el?.instance &&
|
|
570
|
+
// TODO: remove `as any`
|
|
619
571
|
!renderer.value?.component?.ctx.$el?.instance) {
|
|
620
572
|
await new Promise(r => requestAnimationFrame(r));
|
|
621
573
|
}
|
|
622
|
-
|
|
623
|
-
|
|
574
|
+
while (!scene.value?.$el?.instance &&
|
|
575
|
+
// TODO: remove `as any`
|
|
624
576
|
!scene.value?.component?.ctx.$el?.instance) {
|
|
625
577
|
await new Promise(r => requestAnimationFrame(r));
|
|
626
578
|
}
|
|
627
|
-
|
|
628
579
|
const normalizedRenderer = renderer.value?.$el?.instance ?? renderer.value?.component?.ctx.$el?.instance;
|
|
629
580
|
normalizedRenderer.setPixelRatio(globals.dpr);
|
|
630
581
|
const normalizedScene = scene.value?.$el?.instance ?? scene.value?.component?.ctx.$el?.instance;
|
|
631
|
-
const normalizedCamera = camera.value?.$el?.instance ?? camera.value?.component?.ctx.$el?.instance;
|
|
582
|
+
const normalizedCamera = camera.value?.$el?.instance ?? camera.value?.component?.ctx.$el?.instance;
|
|
583
|
+
// TODO: update DPR on monitor switch
|
|
632
584
|
// prep canvas (sizing, observe, unmount, etc)
|
|
633
585
|
// (only run if no custom renderer)
|
|
634
|
-
|
|
635
586
|
if (!context.slots?.renderer?.()?.length) {
|
|
636
587
|
// TODO: use dispose
|
|
637
588
|
prepCanvas(container, normalizedCamera, normalizedRenderer, normalizedScene, props.sizePolicy);
|
|
638
|
-
|
|
639
589
|
if (props.r3f) {
|
|
640
590
|
normalizedRenderer.outputEncoding = THREE.sRGBEncoding;
|
|
641
591
|
normalizedRenderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
|
|
592
|
+
}
|
|
593
|
+
// update render sugar
|
|
645
594
|
const sugar = {
|
|
646
595
|
shadow: props.shadow
|
|
647
596
|
};
|
|
648
|
-
|
|
649
597
|
if (sugar?.shadow) {
|
|
650
598
|
normalizedRenderer.shadowMap.enabled = true;
|
|
651
|
-
|
|
652
599
|
if (typeof sugar.shadow === 'object') {
|
|
653
600
|
normalizedRenderer.shadowMap.type = sugar.shadow.type;
|
|
654
601
|
}
|
|
655
602
|
}
|
|
656
|
-
}
|
|
603
|
+
}
|
|
604
|
+
// START
|
|
657
605
|
// ====================
|
|
658
|
-
|
|
659
|
-
|
|
660
606
|
if (!app) {
|
|
661
607
|
throw new Error('error creating app');
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
|
|
608
|
+
}
|
|
609
|
+
// save renderer, scene, camera
|
|
665
610
|
app.config.globalProperties.lunchbox.camera = normalizedCamera;
|
|
666
611
|
app.config.globalProperties.lunchbox.renderer = normalizedRenderer;
|
|
667
612
|
app.config.globalProperties.lunchbox.scene = normalizedScene;
|
|
668
|
-
|
|
669
613
|
for (let startCallback of startCallbacks ?? []) {
|
|
670
614
|
startCallback({
|
|
671
615
|
app,
|
|
@@ -673,10 +617,9 @@ const LunchboxWrapper = defineComponent({
|
|
|
673
617
|
renderer: normalizedRenderer,
|
|
674
618
|
scene: normalizedScene
|
|
675
619
|
});
|
|
676
|
-
}
|
|
620
|
+
}
|
|
621
|
+
// KICK UPDATE
|
|
677
622
|
// ====================
|
|
678
|
-
|
|
679
|
-
|
|
680
623
|
update({
|
|
681
624
|
app,
|
|
682
625
|
camera: normalizedCamera,
|
|
@@ -684,36 +627,35 @@ const LunchboxWrapper = defineComponent({
|
|
|
684
627
|
scene: normalizedScene,
|
|
685
628
|
updateSource: props.updateSource
|
|
686
629
|
});
|
|
687
|
-
});
|
|
630
|
+
});
|
|
631
|
+
// UNMOUNT
|
|
688
632
|
// ====================
|
|
689
|
-
|
|
690
633
|
onBeforeUnmount(() => {
|
|
691
634
|
cancelUpdate();
|
|
692
635
|
cancelUpdateSource();
|
|
693
|
-
});
|
|
636
|
+
});
|
|
637
|
+
// RENDER FUNCTION
|
|
694
638
|
// ====================
|
|
695
|
-
|
|
696
639
|
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
697
|
-
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
640
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
641
|
+
// REACTIVE CUSTOM CAMERAS
|
|
698
642
|
// ====================
|
|
699
643
|
// find first camera with `type.name` property
|
|
700
644
|
// (which indicates a Lunch.Node)
|
|
701
|
-
|
|
702
645
|
const activeCamera = computed(() => {
|
|
703
646
|
const output = context.slots?.camera?.().find(c => c.type?.name);
|
|
704
|
-
|
|
705
647
|
if (output) {
|
|
706
648
|
return output;
|
|
707
649
|
}
|
|
708
|
-
|
|
709
650
|
return output;
|
|
710
|
-
});
|
|
711
|
-
|
|
651
|
+
});
|
|
652
|
+
// TODO: make custom cameras reactive
|
|
712
653
|
watch(activeCamera, async (newVal, oldVal) => {
|
|
713
654
|
// console.log('got camera', newVal)
|
|
714
655
|
if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
|
|
715
656
|
// TODO: remove cast
|
|
716
|
-
camera.value = newVal;
|
|
657
|
+
camera.value = newVal;
|
|
658
|
+
// TODO: why isn't this updating app camera?
|
|
717
659
|
// const el = await waitFor(() => newVal.el)
|
|
718
660
|
// console.log(el)
|
|
719
661
|
// camera.value = el
|
|
@@ -722,10 +664,11 @@ const LunchboxWrapper = defineComponent({
|
|
|
722
664
|
}
|
|
723
665
|
}, {
|
|
724
666
|
immediate: true
|
|
725
|
-
});
|
|
667
|
+
});
|
|
668
|
+
// RENDER FUNCTION
|
|
726
669
|
// ====================
|
|
727
|
-
|
|
728
|
-
|
|
670
|
+
return () => createVNode(Fragment, null, [context.slots?.renderer?.()?.length ?
|
|
671
|
+
// TODO: remove `as any` cast
|
|
729
672
|
renderer.value = context.slots?.renderer?.()[0] : // ...otherwise, add canvas...
|
|
730
673
|
createVNode(Fragment, null, [createVNode("div", {
|
|
731
674
|
"class": "lunchbox-container",
|
|
@@ -746,14 +689,16 @@ const LunchboxWrapper = defineComponent({
|
|
|
746
689
|
powerPreference: !!props.r3f ? 'high-performance' : 'default',
|
|
747
690
|
...(props.rendererArguments ?? {})
|
|
748
691
|
}]
|
|
749
|
-
}), null)]), context.slots?.scene?.()?.length ?
|
|
692
|
+
}), null)]), context.slots?.scene?.()?.length ?
|
|
693
|
+
// TODO: remove `as any` cast
|
|
750
694
|
scene.value = context.slots?.scene?.()[0] : // ...otherwise, add default scene
|
|
751
695
|
// TODO: why does this need to be a separate component? <scene> throws an error
|
|
752
696
|
createVNode(LunchboxScene, {
|
|
753
697
|
"ref": scene
|
|
754
698
|
}, {
|
|
755
699
|
default: () => [context.slots?.default?.()]
|
|
756
|
-
}), context.slots?.camera?.()?.length ?
|
|
700
|
+
}), context.slots?.camera?.()?.length ?
|
|
701
|
+
// TODO: remove `any` cast
|
|
757
702
|
camera.value : props.ortho || props.orthographic ? createVNode(resolveComponent("orthographicCamera"), mergeProps({
|
|
758
703
|
"ref": camera,
|
|
759
704
|
"args": props.cameraArgs ?? []
|
|
@@ -762,22 +707,32 @@ const LunchboxWrapper = defineComponent({
|
|
|
762
707
|
"args": props.cameraArgs ?? [props.r3f ? 75 : 45, 0.5625, 1, 1000]
|
|
763
708
|
}, consolidatedCameraProperties), null), interactables?.value.length && createVNode(LunchboxEventHandlers, null, null)]);
|
|
764
709
|
}
|
|
765
|
-
|
|
766
710
|
});
|
|
767
711
|
|
|
768
712
|
// list of all components to register out of the box
|
|
769
|
-
const autoGeneratedComponents = [
|
|
770
|
-
|
|
771
|
-
'
|
|
713
|
+
const autoGeneratedComponents = [
|
|
714
|
+
// ThreeJS basics
|
|
715
|
+
'mesh', 'instancedMesh', 'scene', 'sprite', 'object3D',
|
|
716
|
+
// geometry
|
|
717
|
+
'instancedBufferGeometry', 'bufferGeometry', 'boxBufferGeometry', 'circleBufferGeometry', 'coneBufferGeometry', 'cylinderBufferGeometry', 'dodecahedronBufferGeometry', 'extrudeBufferGeometry', 'icosahedronBufferGeometry', 'latheBufferGeometry', 'octahedronBufferGeometry', 'parametricBufferGeometry', 'planeBufferGeometry', 'polyhedronBufferGeometry', 'ringBufferGeometry', 'shapeBufferGeometry', 'sphereBufferGeometry', 'tetrahedronBufferGeometry', 'textBufferGeometry', 'torusBufferGeometry', 'torusKnotBufferGeometry', 'tubeBufferGeometry', 'wireframeGeometry', 'parametricGeometry', 'tetrahedronGeometry', 'octahedronGeometry', 'icosahedronGeometry', 'dodecahedronGeometry', 'polyhedronGeometry', 'tubeGeometry', 'torusKnotGeometry', 'torusGeometry',
|
|
718
|
+
// textgeometry has been moved to /examples/jsm/geometries/TextGeometry
|
|
772
719
|
// 'textGeometry',
|
|
773
|
-
'sphereGeometry', 'ringGeometry', 'planeGeometry', 'latheGeometry', 'shapeGeometry', 'extrudeGeometry', 'edgesGeometry', 'coneGeometry', 'cylinderGeometry', 'circleGeometry', 'boxGeometry',
|
|
774
|
-
|
|
775
|
-
'
|
|
776
|
-
|
|
777
|
-
'
|
|
778
|
-
|
|
779
|
-
'
|
|
780
|
-
|
|
720
|
+
'sphereGeometry', 'ringGeometry', 'planeGeometry', 'latheGeometry', 'shapeGeometry', 'extrudeGeometry', 'edgesGeometry', 'coneGeometry', 'cylinderGeometry', 'circleGeometry', 'boxGeometry',
|
|
721
|
+
// materials
|
|
722
|
+
'material', 'shadowMaterial', 'spriteMaterial', 'rawShaderMaterial', 'shaderMaterial', 'pointsMaterial', 'meshPhysicalMaterial', 'meshStandardMaterial', 'meshPhongMaterial', 'meshToonMaterial', 'meshNormalMaterial', 'meshLambertMaterial', 'meshDepthMaterial', 'meshDistanceMaterial', 'meshBasicMaterial', 'meshMatcapMaterial', 'lineDashedMaterial', 'lineBasicMaterial',
|
|
723
|
+
// lights
|
|
724
|
+
'light', 'spotLightShadow', 'spotLight', 'pointLight', 'rectAreaLight', 'hemisphereLight', 'directionalLightShadow', 'directionalLight', 'ambientLight', 'lightShadow', 'ambientLightProbe', 'hemisphereLightProbe', 'lightProbe',
|
|
725
|
+
// textures
|
|
726
|
+
'texture', 'videoTexture', 'dataTexture', 'dataTexture3D', 'compressedTexture', 'cubeTexture', 'canvasTexture', 'depthTexture',
|
|
727
|
+
// Texture loaders
|
|
728
|
+
'textureLoader',
|
|
729
|
+
// misc
|
|
730
|
+
'group', 'catmullRomCurve3', 'points', 'raycaster',
|
|
731
|
+
// helpers
|
|
732
|
+
'cameraHelper',
|
|
733
|
+
// cameras
|
|
734
|
+
'camera', 'perspectiveCamera', 'orthographicCamera', 'cubeCamera', 'arrayCamera',
|
|
735
|
+
// renderers
|
|
781
736
|
'webGLRenderer'
|
|
782
737
|
/*
|
|
783
738
|
// List copied from r3f:
|
|
@@ -823,42 +778,36 @@ color: ColorProps
|
|
|
823
778
|
fog: FogProps
|
|
824
779
|
fogExp2: FogExp2Props
|
|
825
780
|
shape: ShapeProps
|
|
826
|
-
*/
|
|
827
|
-
];
|
|
828
|
-
|
|
829
|
-
const catalogue = {}; // component creation utility
|
|
781
|
+
*/];
|
|
830
782
|
|
|
783
|
+
const catalogue = {};
|
|
784
|
+
// component creation utility
|
|
831
785
|
const createComponent$1 = tag => defineComponent({
|
|
832
786
|
inheritAttrs: false,
|
|
833
787
|
name: tag,
|
|
834
|
-
|
|
835
788
|
setup(props, context) {
|
|
836
789
|
return () => {
|
|
837
790
|
return h(tag, context.attrs, context.slots?.default?.() || []);
|
|
838
791
|
};
|
|
839
792
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
793
|
+
});
|
|
794
|
+
// turn components into registered map
|
|
844
795
|
const processed = autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
|
|
845
796
|
acc[curr.name] = curr;
|
|
846
797
|
return acc;
|
|
847
798
|
}, {});
|
|
848
|
-
const components = {
|
|
799
|
+
const components = {
|
|
800
|
+
...processed,
|
|
849
801
|
Lunchbox: LunchboxWrapper
|
|
850
802
|
};
|
|
851
803
|
|
|
852
804
|
const createComponent = tag => defineComponent({
|
|
853
805
|
inheritAttrs: false,
|
|
854
806
|
name: tag,
|
|
855
|
-
|
|
856
807
|
render() {
|
|
857
808
|
return h(tag, this.$attrs, this.$slots?.default?.() || []);
|
|
858
809
|
}
|
|
859
|
-
|
|
860
810
|
});
|
|
861
|
-
|
|
862
811
|
const extend = ({
|
|
863
812
|
app,
|
|
864
813
|
...targets
|
|
@@ -877,14 +826,12 @@ function processProp({
|
|
|
877
826
|
// return $attachedArray value if needed
|
|
878
827
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
879
828
|
return node.attachedArray[prop.replace('$attachedArray.', '')];
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
|
|
829
|
+
}
|
|
830
|
+
// return $attached value if needed
|
|
883
831
|
if (typeof prop === 'string' && prop.startsWith('$attached')) {
|
|
884
832
|
return node.attached[prop.replace('$attached.', '')];
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
|
|
833
|
+
}
|
|
834
|
+
// otherwise, return plain value
|
|
888
835
|
return prop;
|
|
889
836
|
}
|
|
890
837
|
function processPropAsArray({
|
|
@@ -900,17 +847,17 @@ function processPropAsArray({
|
|
|
900
847
|
}
|
|
901
848
|
|
|
902
849
|
function instantiateThreeObject(node) {
|
|
903
|
-
if (!node.type) return null;
|
|
904
|
-
|
|
850
|
+
if (!node.type) return null;
|
|
851
|
+
// what class will we be instantiating?
|
|
905
852
|
const uppercaseType = node.type[0].toUpperCase() + node.type.slice(1);
|
|
906
853
|
const translatedType = uppercaseType.replace(/Lunchbox$/, '');
|
|
907
854
|
const targetClass = catalogue[node.type] || THREE[uppercaseType] || catalogue[translatedType] || THREE[translatedType];
|
|
908
|
-
if (!targetClass) throw `${uppercaseType} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;
|
|
909
|
-
|
|
910
|
-
const args = node.props.args ?? [];
|
|
855
|
+
if (!targetClass) throw `${uppercaseType} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;
|
|
856
|
+
// what args have we been provided?
|
|
857
|
+
const args = node.props.args ?? [];
|
|
858
|
+
// replace $attached values with their instances
|
|
911
859
|
// we need to guarantee everything comes back as an array so we can spread $attachedArrays,
|
|
912
860
|
// so we'll use processPropAsArray
|
|
913
|
-
|
|
914
861
|
const argsWrappedInArrays = args.map(arg => {
|
|
915
862
|
return processPropAsArray({
|
|
916
863
|
node,
|
|
@@ -936,12 +883,10 @@ function rng() {
|
|
|
936
883
|
// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
|
|
937
884
|
// find the complete implementation of crypto (msCrypto) on IE11.
|
|
938
885
|
getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);
|
|
939
|
-
|
|
940
886
|
if (!getRandomValues) {
|
|
941
887
|
throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
|
|
942
888
|
}
|
|
943
889
|
}
|
|
944
|
-
|
|
945
890
|
return getRandomValues(rnds8);
|
|
946
891
|
}
|
|
947
892
|
|
|
@@ -957,15 +902,13 @@ function validate(uuid) {
|
|
|
957
902
|
*/
|
|
958
903
|
|
|
959
904
|
var byteToHex = [];
|
|
960
|
-
|
|
961
905
|
for (var i = 0; i < 256; ++i) {
|
|
962
906
|
byteToHex.push((i + 0x100).toString(16).substr(1));
|
|
963
907
|
}
|
|
964
|
-
|
|
965
908
|
function stringify(arr) {
|
|
966
|
-
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
909
|
+
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
910
|
+
// Note: Be careful editing this code! It's been tuned for performance
|
|
967
911
|
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
|
|
968
|
-
|
|
969
912
|
var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one
|
|
970
913
|
// of the following:
|
|
971
914
|
// - One or more input array values don't map to a hex octet (leading to
|
|
@@ -975,7 +918,6 @@ function stringify(arr) {
|
|
|
975
918
|
if (!validate(uuid)) {
|
|
976
919
|
throw TypeError('Stringified UUID is invalid');
|
|
977
920
|
}
|
|
978
|
-
|
|
979
921
|
return uuid;
|
|
980
922
|
}
|
|
981
923
|
|
|
@@ -988,119 +930,97 @@ function v4(options, buf, offset) {
|
|
|
988
930
|
|
|
989
931
|
if (buf) {
|
|
990
932
|
offset = offset || 0;
|
|
991
|
-
|
|
992
933
|
for (var i = 0; i < 16; ++i) {
|
|
993
934
|
buf[offset + i] = rnds[i];
|
|
994
935
|
}
|
|
995
|
-
|
|
996
936
|
return buf;
|
|
997
937
|
}
|
|
998
|
-
|
|
999
938
|
return stringify(rnds);
|
|
1000
939
|
}
|
|
1001
940
|
|
|
941
|
+
// MiniDom recreates DOM node properties and methods.
|
|
1002
942
|
// Since Vue 3 is a DOM-first framework, many of its nodeOps depend on
|
|
1003
943
|
// properties and methods the DOM naturally contains. MiniDom recreates
|
|
1004
944
|
// those properties (as well as a few from the tree-model npm package)
|
|
1005
945
|
// to make a DOM-like but otherwise agnostic hierarchy structure.
|
|
1006
|
-
|
|
1007
946
|
var MiniDom;
|
|
1008
|
-
|
|
1009
947
|
(function (MiniDom) {
|
|
1010
948
|
class BaseNode {
|
|
1011
949
|
constructor(options = {}, parent) {
|
|
1012
950
|
this.parentNode = options?.parentNode ?? parent ?? null;
|
|
1013
951
|
this.minidomType = 'MinidomBaseNode';
|
|
1014
|
-
this.uuid = options?.uuid ?? v4();
|
|
952
|
+
this.uuid = options?.uuid ?? v4();
|
|
953
|
+
// allNodes.push(this)
|
|
1015
954
|
}
|
|
1016
955
|
|
|
1017
|
-
uuid;
|
|
956
|
+
uuid;
|
|
957
|
+
// DOM FEATURES
|
|
1018
958
|
// ====================
|
|
1019
|
-
|
|
1020
959
|
parentNode;
|
|
1021
|
-
|
|
1022
960
|
get nextSibling() {
|
|
1023
961
|
if (!this.parentNode) return null;
|
|
1024
|
-
const idx = this.parentNode.children.findIndex(n => n.uuid === this.uuid);
|
|
1025
|
-
|
|
962
|
+
const idx = this.parentNode.children.findIndex(n => n.uuid === this.uuid);
|
|
963
|
+
// return next sibling if we're present and not the last child of the parent
|
|
1026
964
|
if (idx !== -1 && idx < this.parentNode.children.length - 1) {
|
|
1027
965
|
return this.parentNode.children[idx + 1];
|
|
1028
966
|
}
|
|
1029
|
-
|
|
1030
967
|
return null;
|
|
1031
968
|
}
|
|
1032
|
-
|
|
1033
969
|
insertBefore(child, anchor) {
|
|
1034
970
|
child.removeAsChildFromAnyParents();
|
|
1035
971
|
child.parentNode = this;
|
|
1036
972
|
const anchorIdx = this.children.findIndex(n => n.uuid === anchor?.uuid);
|
|
1037
|
-
|
|
1038
973
|
if (anchorIdx !== -1) {
|
|
1039
974
|
this.children.splice(anchorIdx, 0, child);
|
|
1040
975
|
} else {
|
|
1041
976
|
this.children.push(child);
|
|
1042
977
|
}
|
|
1043
978
|
}
|
|
1044
|
-
|
|
1045
979
|
removeChild(child) {
|
|
1046
980
|
const idx = this.children.findIndex(n => n?.uuid === child?.uuid);
|
|
1047
|
-
|
|
1048
981
|
if (idx !== -1) {
|
|
1049
982
|
this.children.splice(idx, 1);
|
|
1050
983
|
}
|
|
1051
|
-
}
|
|
984
|
+
}
|
|
985
|
+
// TREE FEATURES
|
|
1052
986
|
// ====================
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
987
|
children = [];
|
|
1056
|
-
|
|
1057
988
|
addChild(child) {
|
|
1058
989
|
if (child) {
|
|
1059
990
|
// remove child from any other parents
|
|
1060
|
-
child.removeAsChildFromAnyParents();
|
|
1061
|
-
|
|
991
|
+
child.removeAsChildFromAnyParents();
|
|
992
|
+
// add to this node
|
|
1062
993
|
child.parentNode = this;
|
|
1063
994
|
this.insertBefore(child, null);
|
|
1064
995
|
}
|
|
1065
|
-
|
|
1066
996
|
return this;
|
|
1067
997
|
}
|
|
1068
998
|
/** Get the array of Nodes representing the path from the root to this Node (inclusive). */
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
999
|
getPath() {
|
|
1072
1000
|
const output = [];
|
|
1073
1001
|
let current = this;
|
|
1074
|
-
|
|
1075
1002
|
while (current) {
|
|
1076
1003
|
output.unshift(current);
|
|
1077
1004
|
current = current.parentNode;
|
|
1078
1005
|
}
|
|
1079
|
-
|
|
1080
1006
|
return output;
|
|
1081
1007
|
}
|
|
1082
1008
|
/** Drop this node. Removes parent's knowledge of this node
|
|
1083
1009
|
* and resets this node's internal parent. */
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
1010
|
drop() {
|
|
1087
1011
|
// remove as child
|
|
1088
|
-
this.removeAsChildFromAnyParents();
|
|
1089
|
-
|
|
1012
|
+
this.removeAsChildFromAnyParents();
|
|
1013
|
+
// remove parent
|
|
1090
1014
|
this.parentNode = null;
|
|
1091
1015
|
}
|
|
1092
1016
|
/** Walk over the entire subtree. Return falsey value in callback to end early. */
|
|
1093
1017
|
// TODO: depth-first vs breadth-first
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
1018
|
walk(callback) {
|
|
1097
1019
|
const queue = [this, ...this.children];
|
|
1098
1020
|
const traversed = [];
|
|
1099
1021
|
let canContinue = true;
|
|
1100
|
-
|
|
1101
1022
|
while (queue.length && canContinue) {
|
|
1102
1023
|
const current = queue.shift();
|
|
1103
|
-
|
|
1104
1024
|
if (current) {
|
|
1105
1025
|
if (traversed.includes(current)) continue;
|
|
1106
1026
|
traversed.push(current);
|
|
@@ -1110,20 +1030,15 @@ var MiniDom;
|
|
|
1110
1030
|
canContinue = false;
|
|
1111
1031
|
}
|
|
1112
1032
|
}
|
|
1113
|
-
}
|
|
1033
|
+
}
|
|
1034
|
+
// INTERNAL FEATURES
|
|
1114
1035
|
// ====================
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
1036
|
minidomType;
|
|
1118
|
-
|
|
1119
1037
|
removeAsChildFromAnyParents() {
|
|
1120
1038
|
this.parentNode?.removeChild(this);
|
|
1121
1039
|
}
|
|
1122
|
-
|
|
1123
1040
|
}
|
|
1124
|
-
|
|
1125
1041
|
MiniDom.BaseNode = BaseNode;
|
|
1126
|
-
|
|
1127
1042
|
class RendererBaseNode extends MiniDom.BaseNode {
|
|
1128
1043
|
constructor(options = {}, parent) {
|
|
1129
1044
|
super(options, parent);
|
|
@@ -1135,73 +1050,57 @@ var MiniDom;
|
|
|
1135
1050
|
this.props = options.props ?? [];
|
|
1136
1051
|
this.type = options.type ?? '';
|
|
1137
1052
|
}
|
|
1138
|
-
|
|
1139
1053
|
eventListeners;
|
|
1140
1054
|
eventListenerRemoveFunctions;
|
|
1141
1055
|
name;
|
|
1142
1056
|
metaType;
|
|
1143
1057
|
props;
|
|
1144
1058
|
type;
|
|
1145
|
-
|
|
1146
1059
|
drop() {
|
|
1147
|
-
super.drop();
|
|
1148
|
-
|
|
1060
|
+
super.drop();
|
|
1061
|
+
// handle remove functions
|
|
1149
1062
|
Object.keys(this.eventListenerRemoveFunctions).forEach(key => {
|
|
1150
1063
|
this.eventListenerRemoveFunctions[key].forEach(func => func());
|
|
1151
1064
|
});
|
|
1152
1065
|
}
|
|
1153
|
-
|
|
1154
1066
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1067
|
+
MiniDom.RendererBaseNode = RendererBaseNode;
|
|
1068
|
+
// ====================
|
|
1157
1069
|
// SPECIFIC RENDERER NODES BELOW
|
|
1158
1070
|
// ====================
|
|
1159
|
-
|
|
1160
1071
|
class RendererRootNode extends MiniDom.RendererBaseNode {
|
|
1161
1072
|
constructor(options = {}, parent) {
|
|
1162
1073
|
super(options, parent);
|
|
1163
1074
|
this.domElement = options.domElement ?? document.createElement('div');
|
|
1164
1075
|
}
|
|
1165
|
-
|
|
1166
1076
|
domElement;
|
|
1167
1077
|
isLunchboxRootNode = true;
|
|
1168
1078
|
}
|
|
1169
|
-
|
|
1170
1079
|
MiniDom.RendererRootNode = RendererRootNode;
|
|
1171
|
-
|
|
1172
1080
|
class RendererCommentNode extends MiniDom.RendererBaseNode {
|
|
1173
1081
|
constructor(options = {}, parent) {
|
|
1174
1082
|
super(options, parent);
|
|
1175
1083
|
this.text = options.text ?? '';
|
|
1176
1084
|
}
|
|
1177
|
-
|
|
1178
1085
|
text;
|
|
1179
1086
|
}
|
|
1180
|
-
|
|
1181
1087
|
MiniDom.RendererCommentNode = RendererCommentNode;
|
|
1182
|
-
|
|
1183
1088
|
class RendererDomNode extends MiniDom.RendererBaseNode {
|
|
1184
1089
|
constructor(options = {}, parent) {
|
|
1185
1090
|
super(options, parent);
|
|
1186
1091
|
this.domElement = options.domElement ?? document.createElement('div');
|
|
1187
1092
|
}
|
|
1188
|
-
|
|
1189
1093
|
domElement;
|
|
1190
1094
|
}
|
|
1191
|
-
|
|
1192
1095
|
MiniDom.RendererDomNode = RendererDomNode;
|
|
1193
|
-
|
|
1194
1096
|
class RendererTextNode extends MiniDom.RendererBaseNode {
|
|
1195
1097
|
constructor(options = {}, parent) {
|
|
1196
1098
|
super(options, parent);
|
|
1197
1099
|
this.text = options.text ?? '';
|
|
1198
1100
|
}
|
|
1199
|
-
|
|
1200
1101
|
text;
|
|
1201
1102
|
}
|
|
1202
|
-
|
|
1203
1103
|
MiniDom.RendererTextNode = RendererTextNode;
|
|
1204
|
-
|
|
1205
1104
|
class RendererStandardNode extends MiniDom.RendererBaseNode {
|
|
1206
1105
|
constructor(options = {}, parent) {
|
|
1207
1106
|
super(options, parent);
|
|
@@ -1209,15 +1108,12 @@ var MiniDom;
|
|
|
1209
1108
|
this.attachedArray = options.attachedArray ?? {};
|
|
1210
1109
|
this.instance = options.instance ?? null;
|
|
1211
1110
|
}
|
|
1212
|
-
|
|
1213
1111
|
attached;
|
|
1214
1112
|
attachedArray;
|
|
1215
1113
|
instance;
|
|
1216
1114
|
}
|
|
1217
|
-
|
|
1218
1115
|
MiniDom.RendererStandardNode = RendererStandardNode;
|
|
1219
1116
|
})(MiniDom || (MiniDom = {}));
|
|
1220
|
-
|
|
1221
1117
|
function isMinidomNode(item) {
|
|
1222
1118
|
return item?.minidomType === 'RendererNode';
|
|
1223
1119
|
}
|
|
@@ -1246,7 +1142,6 @@ const requestUpdate = opts => {
|
|
|
1246
1142
|
if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
|
|
1247
1143
|
cancelAnimationFrame(opts.app.config.globalProperties.lunchbox.frameId);
|
|
1248
1144
|
}
|
|
1249
|
-
|
|
1250
1145
|
opts.app.config.globalProperties.lunchbox.frameId = requestAnimationFrame(() => update({
|
|
1251
1146
|
app: opts.app,
|
|
1252
1147
|
renderer: opts.renderer,
|
|
@@ -1255,7 +1150,6 @@ const requestUpdate = opts => {
|
|
|
1255
1150
|
updateSource: opts.updateSource
|
|
1256
1151
|
}));
|
|
1257
1152
|
};
|
|
1258
|
-
|
|
1259
1153
|
const update = opts => {
|
|
1260
1154
|
if (opts.updateSource) {
|
|
1261
1155
|
if (!opts.app.config.globalProperties.lunchbox.watchStopHandle) {
|
|
@@ -1269,37 +1163,35 @@ const update = opts => {
|
|
|
1269
1163
|
} else {
|
|
1270
1164
|
// request next frame on a continuous loop
|
|
1271
1165
|
requestUpdate(opts);
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1166
|
+
}
|
|
1167
|
+
// prep options
|
|
1275
1168
|
const {
|
|
1276
1169
|
app,
|
|
1277
1170
|
renderer,
|
|
1278
1171
|
scene
|
|
1279
|
-
} = opts;
|
|
1280
|
-
|
|
1172
|
+
} = opts;
|
|
1173
|
+
// BEFORE RENDER
|
|
1281
1174
|
app.config.globalProperties.lunchbox.beforeRender.forEach(cb => {
|
|
1282
1175
|
cb?.(opts);
|
|
1283
|
-
});
|
|
1284
|
-
|
|
1176
|
+
});
|
|
1177
|
+
// RENDER
|
|
1285
1178
|
if (renderer && scene && opts.app.config.globalProperties.lunchbox.camera) {
|
|
1286
1179
|
if (app.customRender) {
|
|
1287
1180
|
app.customRender(opts);
|
|
1288
1181
|
} else {
|
|
1289
|
-
renderer.render(toRaw(scene), opts.app.config.globalProperties.lunchbox.camera
|
|
1182
|
+
renderer.render(toRaw(scene), opts.app.config.globalProperties.lunchbox.camera
|
|
1183
|
+
// toRaw(camera)
|
|
1290
1184
|
);
|
|
1291
1185
|
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1186
|
+
}
|
|
1187
|
+
// AFTER RENDER
|
|
1295
1188
|
app.config.globalProperties.lunchbox.afterRender.forEach(cb => {
|
|
1296
1189
|
cb?.(opts);
|
|
1297
1190
|
});
|
|
1298
|
-
};
|
|
1191
|
+
};
|
|
1192
|
+
// before render
|
|
1299
1193
|
// ====================
|
|
1300
|
-
|
|
1301
1194
|
/** Obtain callback methods for `onBeforeRender` and `offBeforeRender`. Usually used internally by Lunchbox. */
|
|
1302
|
-
|
|
1303
1195
|
const useBeforeRender = () => {
|
|
1304
1196
|
return {
|
|
1305
1197
|
onBeforeRender: inject(onBeforeRenderKey),
|
|
@@ -1311,21 +1203,18 @@ const useBeforeRender = () => {
|
|
|
1311
1203
|
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1312
1204
|
* before a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1313
1205
|
*/
|
|
1314
|
-
|
|
1315
1206
|
const onBeforeRender = (cb, index = Infinity) => {
|
|
1316
1207
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1317
1208
|
};
|
|
1318
1209
|
/** Remove a function from the `beforeRender` callback list. Useful for tearing down functions added
|
|
1319
1210
|
* by `onBeforeRender`.
|
|
1320
1211
|
*/
|
|
1321
|
-
|
|
1322
1212
|
const offBeforeRender = cb => {
|
|
1323
1213
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1324
|
-
};
|
|
1214
|
+
};
|
|
1215
|
+
// after render
|
|
1325
1216
|
// ====================
|
|
1326
|
-
|
|
1327
1217
|
/** Obtain callback methods for `onAfterRender` and `offAfterRender`. Usually used internally by Lunchbox. */
|
|
1328
|
-
|
|
1329
1218
|
const useAfterRender = () => {
|
|
1330
1219
|
return {
|
|
1331
1220
|
onAfterRender: inject(onBeforeRenderKey),
|
|
@@ -1337,21 +1226,18 @@ const useAfterRender = () => {
|
|
|
1337
1226
|
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1338
1227
|
* after a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1339
1228
|
*/
|
|
1340
|
-
|
|
1341
1229
|
const onAfterRender = (cb, index = Infinity) => {
|
|
1342
1230
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1343
1231
|
};
|
|
1344
1232
|
/** Remove a function from the `afterRender` callback list. Useful for tearing down functions added
|
|
1345
1233
|
* by `onAfterRender`.
|
|
1346
1234
|
*/
|
|
1347
|
-
|
|
1348
1235
|
const offAfterRender = cb => {
|
|
1349
1236
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1350
1237
|
};
|
|
1351
1238
|
/** Obtain a function used to cancel the current update frame. Use `cancelUpdate` if you wish
|
|
1352
1239
|
* to immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1353
1240
|
*/
|
|
1354
|
-
|
|
1355
1241
|
const useCancelUpdate = () => {
|
|
1356
1242
|
const frameId = inject(frameIdKey);
|
|
1357
1243
|
return () => {
|
|
@@ -1359,26 +1245,22 @@ const useCancelUpdate = () => {
|
|
|
1359
1245
|
};
|
|
1360
1246
|
};
|
|
1361
1247
|
/** Cancel the current update frame. Usually used internally by Lunchbox. */
|
|
1362
|
-
|
|
1363
1248
|
const cancelUpdate = () => {
|
|
1364
1249
|
useCancelUpdate()?.();
|
|
1365
1250
|
};
|
|
1366
1251
|
/** Obtain a function used to cancel an update source. Use `cancelUpdateSource` if you wish to
|
|
1367
1252
|
* immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1368
1253
|
*/
|
|
1369
|
-
|
|
1370
1254
|
const useCancelUpdateSource = () => {
|
|
1371
1255
|
const cancel = inject(watchStopHandleKey);
|
|
1372
1256
|
return () => cancel?.();
|
|
1373
1257
|
};
|
|
1374
1258
|
/** Cancel an update source. Usually used internally by Lunchbox. */
|
|
1375
|
-
|
|
1376
1259
|
const cancelUpdateSource = () => {
|
|
1377
1260
|
useCancelUpdateSource()?.();
|
|
1378
1261
|
};
|
|
1379
1262
|
|
|
1380
1263
|
/** Update a single prop on a given node. */
|
|
1381
|
-
|
|
1382
1264
|
function updateObjectProp({
|
|
1383
1265
|
node,
|
|
1384
1266
|
key,
|
|
@@ -1394,37 +1276,33 @@ function updateObjectProp({
|
|
|
1394
1276
|
interactables,
|
|
1395
1277
|
value
|
|
1396
1278
|
});
|
|
1397
|
-
}
|
|
1279
|
+
}
|
|
1280
|
+
// update THREE property
|
|
1398
1281
|
// get final key
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
1282
|
const camelKey = key.replace(/-/g, '.');
|
|
1402
|
-
const finalKey = propertyShortcuts[camelKey] || camelKey;
|
|
1403
|
-
|
|
1404
|
-
if (internalLunchboxVueKeys.includes(key) || internalLunchboxVueKeys.includes(finalKey)) return node;
|
|
1405
|
-
|
|
1406
|
-
if (!isLunchboxStandardNode(node)) return node;
|
|
1407
|
-
|
|
1283
|
+
const finalKey = propertyShortcuts[camelKey] || camelKey;
|
|
1284
|
+
// handle and return early if prop is specific to Vue/Lunchbox
|
|
1285
|
+
if (internalLunchboxVueKeys.includes(key) || internalLunchboxVueKeys.includes(finalKey)) return node;
|
|
1286
|
+
// everything else should be Three-specific, so let's cancel if this isn't a standard node
|
|
1287
|
+
if (!isLunchboxStandardNode(node)) return node;
|
|
1288
|
+
// parse $attached values
|
|
1408
1289
|
if (typeof value === 'string' && value.startsWith('$attached')) {
|
|
1409
1290
|
const attachedName = value.replace('$attached.', '');
|
|
1410
1291
|
value = get(node.attached, attachedName, null);
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1292
|
+
}
|
|
1293
|
+
// save instance
|
|
1294
|
+
const target = node.instance;
|
|
1295
|
+
// cancel if no target
|
|
1296
|
+
if (!target) return node;
|
|
1297
|
+
// burrow down until we get property to change
|
|
1418
1298
|
let liveProperty;
|
|
1419
|
-
|
|
1420
1299
|
for (let i = 0; i < nestedPropertiesToCheck.length && !liveProperty; i++) {
|
|
1421
1300
|
const nestedProperty = nestedPropertiesToCheck[i];
|
|
1422
1301
|
const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
|
|
1423
1302
|
liveProperty = liveProperty = get(target, fullPath);
|
|
1424
|
-
}
|
|
1303
|
+
}
|
|
1304
|
+
// change property
|
|
1425
1305
|
// first, save as array in case we need to spread it
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
1306
|
if (liveProperty && isNumber(value) && liveProperty?.setScalar) {
|
|
1429
1307
|
// if value is a number and the property has a `setScalar` method, use that
|
|
1430
1308
|
liveProperty.setScalar(value);
|
|
@@ -1439,18 +1317,17 @@ function updateObjectProp({
|
|
|
1439
1317
|
} else {
|
|
1440
1318
|
if (!Array.isArray(value)) {
|
|
1441
1319
|
throw new Error('Arguments on a declarative method must be wrapped in an array.\nWorks:\n<example :methodCall="[256]" />\nDoesn\'t work:\n<example :methodCall="256" />');
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1320
|
+
}
|
|
1321
|
+
// if property is a function, let's try calling it
|
|
1445
1322
|
liveProperty.bind(node.instance)(...value);
|
|
1446
|
-
}
|
|
1323
|
+
}
|
|
1324
|
+
// pass the result to the parent
|
|
1447
1325
|
// const parent = node.parentNode
|
|
1448
1326
|
// if (parent) {
|
|
1449
1327
|
// const parentAsLunchboxNode = parent as Lunchbox.Node
|
|
1450
1328
|
// parentAsLunchboxNode.attached[finalKey] = result
|
|
1451
1329
|
// ; (parentAsLunchboxNode.instance as any)[finalKey] = result
|
|
1452
1330
|
// }
|
|
1453
|
-
|
|
1454
1331
|
} else if (get(target, finalKey, undefined) !== undefined) {
|
|
1455
1332
|
// blank strings evaluate to `true`
|
|
1456
1333
|
// <mesh castShadow receiveShadow /> will work the same as
|
|
@@ -1460,25 +1337,20 @@ function updateObjectProp({
|
|
|
1460
1337
|
// if you see this error in production, you might need to add `finalKey`
|
|
1461
1338
|
// to `internalLunchboxVueKeys` below
|
|
1462
1339
|
console.log(`No property ${finalKey} found on ${target}`);
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1340
|
+
}
|
|
1341
|
+
// mark that we need to update if needed
|
|
1466
1342
|
const targetTypeRaw = target?.texture?.type || target?.type;
|
|
1467
|
-
|
|
1468
1343
|
if (typeof targetTypeRaw === 'string') {
|
|
1469
1344
|
const targetType = targetTypeRaw.toLowerCase();
|
|
1470
|
-
|
|
1471
1345
|
switch (true) {
|
|
1472
1346
|
case targetType.includes('material'):
|
|
1473
1347
|
target.needsUpdate = true;
|
|
1474
1348
|
break;
|
|
1475
|
-
|
|
1476
1349
|
case targetType.includes('camera') && target.updateProjectionMatrix:
|
|
1477
1350
|
target.updateProjectionMatrix();
|
|
1478
1351
|
break;
|
|
1479
1352
|
}
|
|
1480
1353
|
}
|
|
1481
|
-
|
|
1482
1354
|
return node;
|
|
1483
1355
|
}
|
|
1484
1356
|
const propertyShortcuts = {
|
|
@@ -1488,8 +1360,8 @@ const propertyShortcuts = {
|
|
|
1488
1360
|
};
|
|
1489
1361
|
const nestedPropertiesToCheck = ['', 'parameters'];
|
|
1490
1362
|
/** props that Lunchbox intercepts and prevents passing to created instances */
|
|
1491
|
-
|
|
1492
|
-
|
|
1363
|
+
const internalLunchboxVueKeys = ['args', 'attach', 'attachArray', 'is.default', 'isDefault', 'key', 'onAdded',
|
|
1364
|
+
// 'onReady',
|
|
1493
1365
|
'ref', 'src'];
|
|
1494
1366
|
|
|
1495
1367
|
const autoAttach = ['geometry', 'material'];
|
|
@@ -1497,55 +1369,48 @@ const createElement = (type, isSVG, isCustomizedBuiltin, vnodeProps) => {
|
|
|
1497
1369
|
const options = {
|
|
1498
1370
|
type,
|
|
1499
1371
|
props: vnodeProps
|
|
1500
|
-
};
|
|
1501
|
-
|
|
1372
|
+
};
|
|
1373
|
+
// handle dom node
|
|
1502
1374
|
const isDomNode = isLunchboxDomComponent(options);
|
|
1503
|
-
|
|
1504
1375
|
if (isDomNode) {
|
|
1505
1376
|
const node = createDomNode(options);
|
|
1506
1377
|
return node;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1378
|
+
}
|
|
1379
|
+
// handle standard node
|
|
1380
|
+
const node = createNode(options);
|
|
1381
|
+
// autoattach
|
|
1512
1382
|
autoAttach.forEach(key => {
|
|
1513
1383
|
if (type.toLowerCase().endsWith(key)) {
|
|
1514
1384
|
node.props.attach = key;
|
|
1515
1385
|
}
|
|
1516
|
-
});
|
|
1517
|
-
|
|
1386
|
+
});
|
|
1387
|
+
// TODO: array autoattach
|
|
1518
1388
|
return node;
|
|
1519
1389
|
};
|
|
1520
1390
|
|
|
1521
1391
|
const insert = (child, parent, anchor) => {
|
|
1522
1392
|
if (!parent) {
|
|
1523
1393
|
throw new Error('missing parent');
|
|
1524
|
-
}
|
|
1394
|
+
}
|
|
1395
|
+
// add to parent tree node if we have one
|
|
1525
1396
|
// let effectiveParent = parent ?? ensureRootNode()
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
parent.insertBefore(child, anchor); // handle comment & text nodes
|
|
1529
|
-
|
|
1397
|
+
parent.insertBefore(child, anchor);
|
|
1398
|
+
// handle comment & text nodes
|
|
1530
1399
|
if (child.metaType === 'commentMeta' || child.metaType === 'textMeta') {
|
|
1531
1400
|
return;
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1401
|
+
}
|
|
1402
|
+
// handle dom element
|
|
1535
1403
|
if (isLunchboxDomComponent(child)) {
|
|
1536
1404
|
if (isLunchboxDomComponent(parent) || isLunchboxRootNode(parent)) {
|
|
1537
1405
|
parent.domElement.appendChild(child.domElement);
|
|
1538
1406
|
}
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1407
|
+
}
|
|
1408
|
+
// handle standard nodes
|
|
1542
1409
|
if (isLunchboxStandardNode(child)) {
|
|
1543
1410
|
// let effectiveParent = parent
|
|
1544
1411
|
let effectiveParentNodeType = parent.metaType;
|
|
1545
|
-
|
|
1546
1412
|
if (effectiveParentNodeType === 'textMeta' || effectiveParentNodeType === 'commentMeta') {
|
|
1547
1413
|
const path = parent.getPath();
|
|
1548
|
-
|
|
1549
1414
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
1550
1415
|
if (path[i].metaType !== 'textMeta' && path[i].metaType !== 'commentMeta') {
|
|
1551
1416
|
parent = path[i];
|
|
@@ -1553,26 +1418,23 @@ const insert = (child, parent, anchor) => {
|
|
|
1553
1418
|
}
|
|
1554
1419
|
}
|
|
1555
1420
|
}
|
|
1556
|
-
|
|
1557
1421
|
if (isLunchboxStandardNode(child) && child.instance?.isObject3D && isLunchboxStandardNode(parent) && parent.instance?.isObject3D) {
|
|
1558
1422
|
parent.instance?.add?.(child.instance);
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1423
|
+
}
|
|
1424
|
+
// add attached props
|
|
1562
1425
|
if (child?.props?.attach && isLunchboxStandardNode(parent) && parent?.instance) {
|
|
1563
1426
|
// if this element is a loader and the `src` attribute is being used,
|
|
1564
1427
|
// let's assume we want to create the loader and run `load`
|
|
1565
|
-
const isUsingLoaderSugar = child.type?.toLowerCase().endsWith('loader') && child.props.src && (child.props.attach || child.props.attachArray);
|
|
1566
|
-
|
|
1428
|
+
const isUsingLoaderSugar = child.type?.toLowerCase().endsWith('loader') && child.props.src && (child.props.attach || child.props.attachArray);
|
|
1429
|
+
// run special loader behavior
|
|
1567
1430
|
if (isUsingLoaderSugar) {
|
|
1568
1431
|
runLoader(child, parent);
|
|
1569
1432
|
} else {
|
|
1570
1433
|
// update attached normally
|
|
1571
1434
|
attachToParentInstance(child, parent, child.props.attach);
|
|
1572
1435
|
}
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1436
|
+
}
|
|
1437
|
+
// fire onAdded event
|
|
1576
1438
|
if (child.props?.onAdded) {
|
|
1577
1439
|
child.props.onAdded({
|
|
1578
1440
|
instance: child.instance
|
|
@@ -1580,15 +1442,13 @@ const insert = (child, parent, anchor) => {
|
|
|
1580
1442
|
}
|
|
1581
1443
|
}
|
|
1582
1444
|
};
|
|
1583
|
-
|
|
1584
1445
|
function runLoader(child, parent) {
|
|
1585
|
-
const loader = child.instance;
|
|
1586
|
-
|
|
1446
|
+
const loader = child.instance;
|
|
1447
|
+
// ensure parent has attached spaces ready
|
|
1587
1448
|
parent.attached = parent.attached || {};
|
|
1588
|
-
parent.attachedArray = parent.attachedArray || {};
|
|
1589
|
-
|
|
1449
|
+
parent.attachedArray = parent.attachedArray || {};
|
|
1450
|
+
// this should never be true, but just in case
|
|
1590
1451
|
if (!child.props.attach) return;
|
|
1591
|
-
|
|
1592
1452
|
if (child.type?.toLowerCase() === 'textureloader') {
|
|
1593
1453
|
// if this is a texture loader, immediately pass
|
|
1594
1454
|
// load function to parent attachment
|
|
@@ -1604,11 +1464,9 @@ function runLoader(child, parent) {
|
|
|
1604
1464
|
});
|
|
1605
1465
|
}
|
|
1606
1466
|
}
|
|
1607
|
-
|
|
1608
1467
|
function attachToParentInstance(child, parent, key, value) {
|
|
1609
1468
|
const finalValueToAttach = value ?? child.instance;
|
|
1610
1469
|
const parentInstanceAsAny = parent.instance;
|
|
1611
|
-
|
|
1612
1470
|
if (child.props.attach === key) {
|
|
1613
1471
|
parent.attached = {
|
|
1614
1472
|
[key]: finalValueToAttach,
|
|
@@ -1616,40 +1474,38 @@ function attachToParentInstance(child, parent, key, value) {
|
|
|
1616
1474
|
};
|
|
1617
1475
|
parentInstanceAsAny[key] = value ?? child.instance;
|
|
1618
1476
|
}
|
|
1619
|
-
|
|
1620
1477
|
if (child.props.attachArray === key) {
|
|
1621
1478
|
if (!parent.attachedArray[child.props.attachArray]) {
|
|
1622
1479
|
parent.attachedArray[child.props.attachArray] = [];
|
|
1623
1480
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1481
|
+
parent.attachedArray[child.props.attachArray].push(finalValueToAttach);
|
|
1482
|
+
// TODO: implement auto-attaching array
|
|
1627
1483
|
parentInstanceAsAny[key] = [parentInstanceAsAny[key]];
|
|
1628
1484
|
}
|
|
1629
1485
|
}
|
|
1630
1486
|
|
|
1631
1487
|
const remove = node => {
|
|
1632
|
-
if (!node) return;
|
|
1633
|
-
|
|
1488
|
+
if (!node) return;
|
|
1489
|
+
// prep subtree
|
|
1634
1490
|
const subtree = [];
|
|
1635
1491
|
node.walk(descendant => {
|
|
1636
1492
|
subtree.push(descendant);
|
|
1637
1493
|
return true;
|
|
1638
|
-
});
|
|
1639
|
-
|
|
1494
|
+
});
|
|
1495
|
+
// clean up subtree
|
|
1640
1496
|
subtree.forEach(n => {
|
|
1641
1497
|
if (isLunchboxStandardNode(n)) {
|
|
1642
1498
|
// try to remove three object
|
|
1643
|
-
n.instance?.removeFromParent?.();
|
|
1644
|
-
|
|
1645
|
-
const dispose =
|
|
1499
|
+
n.instance?.removeFromParent?.();
|
|
1500
|
+
// try to dispose three object
|
|
1501
|
+
const dispose =
|
|
1502
|
+
// calling `dispose` on a scene triggers an error,
|
|
1646
1503
|
// so let's ignore if this node is a scene
|
|
1647
1504
|
n.type !== 'scene' && n.instance?.dispose;
|
|
1648
1505
|
if (dispose) dispose.bind(n.instance)();
|
|
1649
1506
|
n.instance = null;
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1507
|
+
}
|
|
1508
|
+
// drop tree node
|
|
1653
1509
|
n.drop();
|
|
1654
1510
|
});
|
|
1655
1511
|
};
|
|
@@ -1657,45 +1513,38 @@ const remove = node => {
|
|
|
1657
1513
|
/*
|
|
1658
1514
|
Elements are `create`d from the outside in, then `insert`ed from the inside out.
|
|
1659
1515
|
*/
|
|
1660
|
-
|
|
1661
1516
|
const createNodeOps = () => {
|
|
1662
1517
|
// APP-LEVEL GLOBALS
|
|
1663
1518
|
// ====================
|
|
1664
1519
|
// These need to exist at the app level in a place where the node ops can access them.
|
|
1665
1520
|
// It'd be better to set these via `app.provide` at app creation, but the node ops need access
|
|
1666
1521
|
// to these values before the app is instantiated, so this is the next-best place for them to exist.
|
|
1667
|
-
const interactables = ref([]);
|
|
1522
|
+
const interactables = ref([]);
|
|
1523
|
+
// NODE OPS
|
|
1668
1524
|
// ====================
|
|
1669
|
-
|
|
1670
1525
|
const nodeOps = {
|
|
1671
1526
|
createElement,
|
|
1672
|
-
|
|
1673
1527
|
createText(text) {
|
|
1674
1528
|
return createTextNode({
|
|
1675
1529
|
text
|
|
1676
1530
|
});
|
|
1677
1531
|
},
|
|
1678
|
-
|
|
1679
1532
|
createComment(text) {
|
|
1680
1533
|
return createCommentNode({
|
|
1681
1534
|
text
|
|
1682
1535
|
});
|
|
1683
1536
|
},
|
|
1684
|
-
|
|
1685
1537
|
insert,
|
|
1686
|
-
|
|
1687
1538
|
nextSibling(node) {
|
|
1688
1539
|
const result = node.nextSibling;
|
|
1689
1540
|
if (!result) return null;
|
|
1690
1541
|
return result;
|
|
1691
1542
|
},
|
|
1692
|
-
|
|
1693
1543
|
parentNode(node) {
|
|
1694
1544
|
const result = node.parentNode;
|
|
1695
1545
|
if (!result) return null;
|
|
1696
1546
|
return result;
|
|
1697
1547
|
},
|
|
1698
|
-
|
|
1699
1548
|
patchProp(node, key, prevValue, nextValue) {
|
|
1700
1549
|
if (isLunchboxDomComponent(node)) {
|
|
1701
1550
|
// handle DOM node
|
|
@@ -1707,16 +1556,13 @@ const createNodeOps = () => {
|
|
|
1707
1556
|
} else {
|
|
1708
1557
|
node.domElement.setAttribute(key, nextValue);
|
|
1709
1558
|
}
|
|
1710
|
-
|
|
1711
1559
|
return;
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1560
|
+
}
|
|
1561
|
+
// ignore if root node, or Lunchbox internal prop
|
|
1715
1562
|
if (isLunchboxRootNode(node) || key.startsWith('$')) {
|
|
1716
1563
|
return;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1564
|
+
}
|
|
1565
|
+
// otherwise, update prop
|
|
1720
1566
|
updateObjectProp({
|
|
1721
1567
|
node: node,
|
|
1722
1568
|
key,
|
|
@@ -1724,15 +1570,13 @@ const createNodeOps = () => {
|
|
|
1724
1570
|
value: nextValue
|
|
1725
1571
|
});
|
|
1726
1572
|
},
|
|
1727
|
-
|
|
1728
1573
|
remove,
|
|
1729
|
-
|
|
1730
|
-
|
|
1574
|
+
setElementText() {
|
|
1575
|
+
// noop
|
|
1731
1576
|
},
|
|
1732
|
-
|
|
1733
|
-
|
|
1577
|
+
setText() {
|
|
1578
|
+
// noop
|
|
1734
1579
|
}
|
|
1735
|
-
|
|
1736
1580
|
};
|
|
1737
1581
|
return {
|
|
1738
1582
|
nodeOps,
|
|
@@ -1741,18 +1585,14 @@ const createNodeOps = () => {
|
|
|
1741
1585
|
};
|
|
1742
1586
|
|
|
1743
1587
|
/** The current camera as a computed value. */
|
|
1744
|
-
|
|
1745
1588
|
const useCamera = () => inject(appCameraKey);
|
|
1746
1589
|
/** Run a function using the current camera when it's present. */
|
|
1747
|
-
|
|
1748
1590
|
const onCameraReady = cb => {
|
|
1749
1591
|
const existing = useCamera();
|
|
1750
|
-
|
|
1751
1592
|
if (existing.value) {
|
|
1752
1593
|
cb(existing.value);
|
|
1753
1594
|
return;
|
|
1754
1595
|
}
|
|
1755
|
-
|
|
1756
1596
|
let stopWatch = null;
|
|
1757
1597
|
stopWatch = watch(useCamera(), newVal => {
|
|
1758
1598
|
if (newVal) {
|
|
@@ -1762,18 +1602,14 @@ const onCameraReady = cb => {
|
|
|
1762
1602
|
});
|
|
1763
1603
|
};
|
|
1764
1604
|
/** The current renderer as a computed value. */
|
|
1765
|
-
|
|
1766
1605
|
const useRenderer = () => inject(appRenderersKey);
|
|
1767
1606
|
/** Run a function using the current renderer when it's present. */
|
|
1768
|
-
|
|
1769
1607
|
const onRendererReady = cb => {
|
|
1770
1608
|
const existing = useRenderer();
|
|
1771
|
-
|
|
1772
1609
|
if (existing.value) {
|
|
1773
1610
|
cb(existing.value);
|
|
1774
1611
|
return;
|
|
1775
1612
|
}
|
|
1776
|
-
|
|
1777
1613
|
let stopWatch = null;
|
|
1778
1614
|
stopWatch = watch(useRenderer(), newVal => {
|
|
1779
1615
|
if (newVal) {
|
|
@@ -1785,18 +1621,14 @@ const onRendererReady = cb => {
|
|
|
1785
1621
|
});
|
|
1786
1622
|
};
|
|
1787
1623
|
/** The current scene as a computed value. */
|
|
1788
|
-
|
|
1789
1624
|
const useScene = () => inject(appSceneKey);
|
|
1790
1625
|
/** Run a function using the current scene when it's present. */
|
|
1791
|
-
|
|
1792
1626
|
const onSceneReady = cb => {
|
|
1793
1627
|
const existing = useScene();
|
|
1794
|
-
|
|
1795
1628
|
if (existing.value) {
|
|
1796
1629
|
cb(existing.value);
|
|
1797
1630
|
return;
|
|
1798
1631
|
}
|
|
1799
|
-
|
|
1800
1632
|
let stopWatch = null;
|
|
1801
1633
|
stopWatch = watch(useScene(), newVal => {
|
|
1802
1634
|
if (newVal) {
|
|
@@ -1806,16 +1638,15 @@ const onSceneReady = cb => {
|
|
|
1806
1638
|
}, {
|
|
1807
1639
|
immediate: true
|
|
1808
1640
|
});
|
|
1809
|
-
};
|
|
1641
|
+
};
|
|
1642
|
+
// CUSTOM RENDER SUPPORT
|
|
1810
1643
|
// ====================
|
|
1811
|
-
|
|
1812
1644
|
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
1813
1645
|
* Changing this requires the user to manually render their scene.
|
|
1814
1646
|
*
|
|
1815
1647
|
* Invokes immediately - use `useCustomRender().setCustomRender`
|
|
1816
1648
|
* if you need to call somewhere outside of `setup`.
|
|
1817
1649
|
*/
|
|
1818
|
-
|
|
1819
1650
|
const setCustomRender = render => {
|
|
1820
1651
|
useCustomRender()?.setCustomRender?.(render);
|
|
1821
1652
|
};
|
|
@@ -1824,24 +1655,20 @@ const setCustomRender = render => {
|
|
|
1824
1655
|
* Invokes immediately - use `useCustomRender().clearCustomRender`
|
|
1825
1656
|
* if you need to call somewhere outside of `setup`.
|
|
1826
1657
|
*/
|
|
1827
|
-
|
|
1828
1658
|
const clearCustomRender = () => {
|
|
1829
1659
|
useCustomRender()?.clearCustomRender?.();
|
|
1830
1660
|
};
|
|
1831
1661
|
/** Provides `setCustomRender` and `clearCustomRender` functions to be called in a non-`setup` context. */
|
|
1832
|
-
|
|
1833
1662
|
const useCustomRender = () => {
|
|
1834
1663
|
return {
|
|
1835
1664
|
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
1836
1665
|
* Changing this requires the user to manually render their scene. */
|
|
1837
1666
|
setCustomRender: inject(setCustomRenderKey),
|
|
1838
|
-
|
|
1839
1667
|
/** Clear the active app's custom render function. */
|
|
1840
1668
|
clearCustomRender: inject(clearCustomRenderKey)
|
|
1841
1669
|
};
|
|
1842
1670
|
};
|
|
1843
1671
|
/** Use app-level globals. */
|
|
1844
|
-
|
|
1845
1672
|
const useGlobals = () => inject(globalsInjectionKey);
|
|
1846
1673
|
/** Construct a function to update your app-level globals.
|
|
1847
1674
|
*
|
|
@@ -1853,29 +1680,23 @@ const useGlobals = () => inject(globalsInjectionKey);
|
|
|
1853
1680
|
* updateGlobals({ dpr: 2 })
|
|
1854
1681
|
* ```
|
|
1855
1682
|
*/
|
|
1856
|
-
|
|
1857
1683
|
const useUpdateGlobals = () => inject(updateGlobalsInjectionKey);
|
|
1858
1684
|
/** Update app-level globals.
|
|
1859
1685
|
*
|
|
1860
1686
|
* Invokes immediately - use `useUpdateGlobals`
|
|
1861
1687
|
* if you need to call somewhere outside of `setup`.
|
|
1862
1688
|
*/
|
|
1863
|
-
|
|
1864
1689
|
const updateGlobals = newValue => {
|
|
1865
1690
|
useUpdateGlobals()?.(newValue);
|
|
1866
1691
|
};
|
|
1867
1692
|
/** Use the current Lunchbox app. Usually used internally by Lunchbox. */
|
|
1868
|
-
|
|
1869
1693
|
const useApp = () => inject(appKey);
|
|
1870
1694
|
/** Obtain a list of the start callback functions. Usually used internally by Lunchbox. */
|
|
1871
|
-
|
|
1872
1695
|
const useStartCallbacks = () => inject(startCallbackKey);
|
|
1873
1696
|
/** Run a given callback once when the Lunchbox app starts. Include an index to
|
|
1874
1697
|
* splice the callback at that index in the callback queue. */
|
|
1875
|
-
|
|
1876
1698
|
const onStart = (cb, index = Infinity) => {
|
|
1877
1699
|
const callbacks = useStartCallbacks();
|
|
1878
|
-
|
|
1879
1700
|
if (index === Infinity) {
|
|
1880
1701
|
callbacks?.push(cb);
|
|
1881
1702
|
} else {
|
|
@@ -1883,41 +1704,39 @@ const onStart = (cb, index = Infinity) => {
|
|
|
1883
1704
|
}
|
|
1884
1705
|
};
|
|
1885
1706
|
/** Obtain a list of interactable objects (registered via onClick, onHover, etc events). Usually used internally by Lunchbox. */
|
|
1886
|
-
|
|
1887
1707
|
const useLunchboxInteractables = () => inject(lunchboxInteractables);
|
|
1888
1708
|
/** Build a computed instance-getter from a specified ref. Defaults to a `toRaw`'d result. */
|
|
1889
|
-
|
|
1890
1709
|
const getInstance = (target, raw = true) => computed(() => {
|
|
1891
1710
|
const output = target.value?.$el?.instance ?? target.value?.instance ?? null;
|
|
1892
1711
|
if (output && raw) return toRaw(output);
|
|
1893
1712
|
return output;
|
|
1894
|
-
});
|
|
1713
|
+
});
|
|
1714
|
+
// CREATE APP
|
|
1895
1715
|
// ====================
|
|
1896
|
-
|
|
1897
1716
|
const createApp = root => {
|
|
1898
1717
|
const {
|
|
1899
1718
|
nodeOps,
|
|
1900
1719
|
interactables
|
|
1901
1720
|
} = createNodeOps();
|
|
1902
|
-
const app = createRenderer(nodeOps).createApp(root);
|
|
1721
|
+
const app = createRenderer(nodeOps).createApp(root);
|
|
1722
|
+
// provide Lunchbox interaction handlers flag (modified when user references events via
|
|
1903
1723
|
// @click, etc)
|
|
1904
|
-
|
|
1905
|
-
|
|
1724
|
+
app.provide(lunchboxInteractables, interactables);
|
|
1725
|
+
// register all components
|
|
1906
1726
|
// ====================
|
|
1907
|
-
|
|
1908
1727
|
Object.keys(components).forEach(key => {
|
|
1909
1728
|
app?.component(key, components[key]);
|
|
1910
|
-
});
|
|
1729
|
+
});
|
|
1730
|
+
// provide custom renderer functions
|
|
1911
1731
|
// ====================
|
|
1912
|
-
|
|
1913
1732
|
app.provide(setCustomRenderKey, render => {
|
|
1914
1733
|
app.setCustomRender(render);
|
|
1915
1734
|
});
|
|
1916
1735
|
app.provide(clearCustomRenderKey, () => {
|
|
1917
1736
|
app.clearCustomRender();
|
|
1918
|
-
});
|
|
1737
|
+
});
|
|
1738
|
+
// before render
|
|
1919
1739
|
// ====================
|
|
1920
|
-
|
|
1921
1740
|
const beforeRender = [];
|
|
1922
1741
|
app.provide(beforeRenderKey, beforeRender);
|
|
1923
1742
|
app.provide(onBeforeRenderKey, (cb, index = Infinity) => {
|
|
@@ -1932,14 +1751,13 @@ const createApp = root => {
|
|
|
1932
1751
|
beforeRender.splice(cb, 1);
|
|
1933
1752
|
} else {
|
|
1934
1753
|
const idx = beforeRender.findIndex(v => v == cb);
|
|
1935
|
-
|
|
1936
1754
|
if (idx !== -1) {
|
|
1937
1755
|
beforeRender.splice(idx, 1);
|
|
1938
1756
|
}
|
|
1939
1757
|
}
|
|
1940
|
-
});
|
|
1758
|
+
});
|
|
1759
|
+
// after render
|
|
1941
1760
|
// ====================
|
|
1942
|
-
|
|
1943
1761
|
const afterRender = [];
|
|
1944
1762
|
app.provide(afterRenderKey, afterRender);
|
|
1945
1763
|
app.provide(onAfterRenderKey, (cb, index = Infinity) => {
|
|
@@ -1954,14 +1772,13 @@ const createApp = root => {
|
|
|
1954
1772
|
afterRender.splice(cb, 1);
|
|
1955
1773
|
} else {
|
|
1956
1774
|
const idx = afterRender.findIndex(v => v == cb);
|
|
1957
|
-
|
|
1958
1775
|
if (idx !== -1) {
|
|
1959
1776
|
afterRender.splice(idx, 1);
|
|
1960
1777
|
}
|
|
1961
1778
|
}
|
|
1962
|
-
});
|
|
1779
|
+
});
|
|
1780
|
+
// save app-level components
|
|
1963
1781
|
// ====================
|
|
1964
|
-
|
|
1965
1782
|
app.config.globalProperties.lunchbox = reactive({
|
|
1966
1783
|
afterRender,
|
|
1967
1784
|
beforeRender,
|
|
@@ -1970,35 +1787,34 @@ const createApp = root => {
|
|
|
1970
1787
|
frameId: -1,
|
|
1971
1788
|
renderer: null,
|
|
1972
1789
|
scene: null,
|
|
1973
|
-
watchStopHandle: null
|
|
1974
|
-
|
|
1975
|
-
});
|
|
1790
|
+
watchStopHandle: null
|
|
1791
|
+
// TODO: inputActive, mousePos
|
|
1792
|
+
});
|
|
1793
|
+
// provide app-level globals & globals update method
|
|
1976
1794
|
// ====================
|
|
1977
|
-
|
|
1978
1795
|
app.provide(globalsInjectionKey, app.config.globalProperties.lunchbox);
|
|
1979
1796
|
app.provide(updateGlobalsInjectionKey, newGlobals => {
|
|
1980
1797
|
Object.keys(newGlobals).forEach(key => {
|
|
1981
|
-
const typedKey = key;
|
|
1982
|
-
|
|
1798
|
+
const typedKey = key;
|
|
1799
|
+
// TODO: fix
|
|
1983
1800
|
app.config.globalProperties.lunchbox[typedKey] = newGlobals[typedKey];
|
|
1984
1801
|
});
|
|
1985
|
-
});
|
|
1802
|
+
});
|
|
1803
|
+
// frame ID (used for update functions)
|
|
1986
1804
|
// ====================
|
|
1987
|
-
|
|
1988
|
-
|
|
1805
|
+
app.provide(frameIdKey, app.config.globalProperties.lunchbox.frameId);
|
|
1806
|
+
// watch stop handler (used for conditional update loop)
|
|
1989
1807
|
// ====================
|
|
1990
|
-
|
|
1991
|
-
|
|
1808
|
+
app.provide(watchStopHandleKey, app.config.globalProperties.lunchbox.watchStopHandle);
|
|
1809
|
+
// update mount function to match Lunchbox.Node
|
|
1992
1810
|
// ====================
|
|
1993
|
-
|
|
1994
1811
|
const {
|
|
1995
1812
|
mount
|
|
1996
1813
|
} = app;
|
|
1997
|
-
|
|
1998
1814
|
app.mount = (root, ...args) => {
|
|
1999
1815
|
// find DOM element to use as app root
|
|
2000
|
-
const domElement = typeof root === 'string' ? document.querySelector(root) : root;
|
|
2001
|
-
|
|
1816
|
+
const domElement = typeof root === 'string' ? document.querySelector(root) : root;
|
|
1817
|
+
// create or find root node
|
|
2002
1818
|
const rootNode = new MiniDom.RendererRootNode({
|
|
2003
1819
|
domElement,
|
|
2004
1820
|
isLunchboxRootNode: true,
|
|
@@ -2011,44 +1827,40 @@ const createApp = root => {
|
|
|
2011
1827
|
app.provide(appRootNodeKey, rootNode);
|
|
2012
1828
|
const mounted = mount(rootNode, ...args);
|
|
2013
1829
|
return mounted;
|
|
2014
|
-
};
|
|
1830
|
+
};
|
|
1831
|
+
// embed .extend function
|
|
2015
1832
|
// ====================
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
1833
|
app.extend = targets => {
|
|
2019
1834
|
extend({
|
|
2020
1835
|
app: app,
|
|
2021
1836
|
...targets
|
|
2022
1837
|
});
|
|
2023
1838
|
return app;
|
|
2024
|
-
};
|
|
1839
|
+
};
|
|
1840
|
+
// start callback functions
|
|
2025
1841
|
// ====================
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
1842
|
const startCallbacks = [];
|
|
2029
|
-
app.provide(startCallbackKey, startCallbacks);
|
|
1843
|
+
app.provide(startCallbackKey, startCallbacks);
|
|
1844
|
+
// prep for custom render support
|
|
2030
1845
|
// ====================
|
|
2031
|
-
|
|
2032
1846
|
app.setCustomRender = newRender => {
|
|
2033
1847
|
if (app) {
|
|
2034
1848
|
app.customRender = newRender;
|
|
2035
1849
|
}
|
|
2036
|
-
};
|
|
2037
|
-
|
|
2038
|
-
|
|
1850
|
+
};
|
|
1851
|
+
// add custom render removal
|
|
2039
1852
|
app.clearCustomRender = () => {
|
|
2040
1853
|
if (app) {
|
|
2041
1854
|
app.customRender = null;
|
|
2042
1855
|
}
|
|
2043
|
-
};
|
|
1856
|
+
};
|
|
1857
|
+
// provide app
|
|
2044
1858
|
// ====================
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
1859
|
app.provide(appKey, app);
|
|
2048
1860
|
app.provide(appRenderersKey, computed(() => app.config.globalProperties.lunchbox.renderer));
|
|
2049
1861
|
app.provide(appSceneKey, computed(() => app.config.globalProperties.lunchbox.scene));
|
|
2050
|
-
app.provide(appCameraKey, computed(() => app.config.globalProperties.lunchbox.camera));
|
|
2051
|
-
|
|
1862
|
+
app.provide(appCameraKey, computed(() => app.config.globalProperties.lunchbox.camera));
|
|
1863
|
+
// done
|
|
2052
1864
|
return app;
|
|
2053
1865
|
};
|
|
2054
1866
|
|