lunchboxjs 0.1.4001 → 0.1.4005
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/README.md +3 -0
- package/dist/lunchboxjs.js +407 -226
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +405 -227
- package/extras/Gltf.vue +2 -2
- package/extras/OrbitControlsWrapper.vue +9 -10
- package/package.json +24 -3
- package/src/components/LunchboxWrapper/LunchboxWrapper.ts +125 -33
- package/src/components/LunchboxWrapper/resizeCanvas.ts +17 -7
- package/src/components/autoGeneratedComponents.ts +175 -0
- package/src/components/index.ts +5 -188
- package/src/core/createNode.ts +19 -9
- package/src/core/ensure.ts +166 -130
- package/src/core/instantiateThreeObject/index.ts +6 -4
- package/src/core/interaction/index.ts +4 -2
- package/src/core/interaction/setupAutoRaycaster.ts +6 -6
- package/src/core/minidom.ts +85 -27
- package/src/core/update.ts +36 -26
- package/src/index.ts +62 -22
- package/src/nodeOps/createElement.ts +3 -6
- package/src/nodeOps/insert.ts +6 -4
- package/src/nodeOps/remove.ts +19 -5
- package/src/types.ts +10 -0
package/dist/lunchboxjs.js
CHANGED
|
@@ -28,29 +28,39 @@
|
|
|
28
28
|
const allNodes = [];
|
|
29
29
|
|
|
30
30
|
const resizeCanvas = (width, height) => {
|
|
31
|
-
const renderer = ensureRenderer
|
|
32
|
-
const scene =
|
|
31
|
+
const renderer = ensureRenderer.value?.instance;
|
|
32
|
+
const scene = ensuredScene.value.instance;
|
|
33
|
+
const camera = ensuredCamera.value;
|
|
33
34
|
// ignore if no element
|
|
34
|
-
if (!renderer?.domElement || !scene)
|
|
35
|
+
if (!renderer?.domElement || !scene || !camera)
|
|
35
36
|
return;
|
|
36
37
|
width = width ?? window.innerWidth;
|
|
37
38
|
height = height ?? window.innerHeight;
|
|
38
39
|
// update camera
|
|
39
40
|
const aspect = width / height;
|
|
40
|
-
const camera = ensureCamera();
|
|
41
41
|
if (camera.type?.toLowerCase() === 'perspectivecamera') {
|
|
42
42
|
const perspectiveCamera = camera.instance;
|
|
43
43
|
perspectiveCamera.aspect = aspect;
|
|
44
44
|
perspectiveCamera.updateProjectionMatrix();
|
|
45
45
|
}
|
|
46
|
+
else if (camera.type?.toLowerCase() === 'orthographiccamera') {
|
|
47
|
+
// console.log('TODO: ortho camera update')
|
|
48
|
+
const orthoCamera = camera.instance;
|
|
49
|
+
const heightInTermsOfWidth = height / width;
|
|
50
|
+
orthoCamera.top = heightInTermsOfWidth * 10;
|
|
51
|
+
orthoCamera.bottom = -heightInTermsOfWidth * 10;
|
|
52
|
+
orthoCamera.right = 10;
|
|
53
|
+
orthoCamera.left = -10;
|
|
54
|
+
orthoCamera.updateProjectionMatrix();
|
|
55
|
+
}
|
|
46
56
|
else {
|
|
47
|
-
console.log('TODO: ortho camera
|
|
57
|
+
console.log('TODO: non-ortho or perspective camera');
|
|
48
58
|
}
|
|
49
59
|
// update canvas
|
|
50
60
|
renderer.setSize(width, height);
|
|
51
61
|
// render immediately so there's no flicker
|
|
52
62
|
if (scene && camera.instance) {
|
|
53
|
-
renderer.render(scene, camera.instance);
|
|
63
|
+
renderer.render(vue.toRaw(scene), vue.toRaw(camera.instance));
|
|
54
64
|
}
|
|
55
65
|
};
|
|
56
66
|
|
|
@@ -94,8 +104,13 @@
|
|
|
94
104
|
props: {
|
|
95
105
|
// These should match the Lunchbox.WrapperProps interface
|
|
96
106
|
background: String,
|
|
107
|
+
cameraArgs: Array,
|
|
108
|
+
cameraLook: Array,
|
|
109
|
+
cameraLookAt: Array,
|
|
97
110
|
cameraPosition: Array,
|
|
98
111
|
dpr: Number,
|
|
112
|
+
ortho: Boolean,
|
|
113
|
+
orthographic: Boolean,
|
|
99
114
|
rendererProperties: Object,
|
|
100
115
|
shadow: [Boolean, Object],
|
|
101
116
|
transparent: Boolean,
|
|
@@ -106,6 +121,7 @@
|
|
|
106
121
|
const dpr = vue.ref(props.dpr ?? -1);
|
|
107
122
|
const container = vue.ref();
|
|
108
123
|
let renderer;
|
|
124
|
+
let camera;
|
|
109
125
|
let scene;
|
|
110
126
|
// MOUNT
|
|
111
127
|
// ====================
|
|
@@ -113,40 +129,106 @@
|
|
|
113
129
|
// canvas needs to exist
|
|
114
130
|
if (!canvas.value)
|
|
115
131
|
throw new Error('missing canvas');
|
|
116
|
-
// ensure camera
|
|
117
|
-
const camera = ensureCamera().instance;
|
|
118
|
-
// move camera if needed
|
|
119
|
-
if (camera && props.cameraPosition) {
|
|
120
|
-
camera.position.set(...props.cameraPosition);
|
|
121
|
-
}
|
|
122
132
|
// RENDERER
|
|
123
133
|
// ====================
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
// is there already a renderer?
|
|
135
|
+
// TODO: allow other renderer types
|
|
136
|
+
renderer = tryGetNodeWithInstanceType([
|
|
137
|
+
'WebGLRenderer',
|
|
138
|
+
]);
|
|
139
|
+
// if renderer is missing, initialize with options
|
|
140
|
+
if (!renderer) {
|
|
141
|
+
// build renderer args
|
|
142
|
+
const rendererArgs = {
|
|
143
|
+
antialias: true,
|
|
144
|
+
canvas: canvas.value.domElement,
|
|
145
|
+
};
|
|
146
|
+
if (props.transparent) {
|
|
147
|
+
rendererArgs.alpha = true;
|
|
148
|
+
}
|
|
149
|
+
// create new renderer
|
|
150
|
+
ensureRenderer.value = createNode({
|
|
151
|
+
type: 'WebGLRenderer',
|
|
152
|
+
uuid: fallbackRendererUuid,
|
|
153
|
+
props: {
|
|
154
|
+
args: [rendererArgs],
|
|
155
|
+
},
|
|
141
156
|
});
|
|
157
|
+
// we've initialized the renderer, so anything depending on it can execute now
|
|
158
|
+
rendererReady.value = true;
|
|
159
|
+
const rendererAsWebGlRenderer = ensureRenderer;
|
|
160
|
+
// update render sugar
|
|
161
|
+
const sugar = {
|
|
162
|
+
shadow: props.shadow,
|
|
163
|
+
};
|
|
164
|
+
if (rendererAsWebGlRenderer.value.instance && sugar?.shadow) {
|
|
165
|
+
rendererAsWebGlRenderer.value.instance.shadowMap.enabled =
|
|
166
|
+
true;
|
|
167
|
+
if (typeof sugar.shadow === 'object') {
|
|
168
|
+
rendererAsWebGlRenderer.value.instance.shadowMap.type =
|
|
169
|
+
sugar.shadow.type;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// set renderer props if needed
|
|
173
|
+
if (props.rendererProperties) {
|
|
174
|
+
Object.keys(props.rendererProperties).forEach((key) => {
|
|
175
|
+
lodash.set(rendererAsWebGlRenderer.value, key, props.rendererProperties[key]);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// update using created renderer
|
|
179
|
+
renderer = rendererAsWebGlRenderer.value;
|
|
142
180
|
}
|
|
143
|
-
|
|
181
|
+
else {
|
|
144
182
|
useFallbackRenderer.value = false;
|
|
183
|
+
// the user has initialized the renderer, so anything depending
|
|
184
|
+
// on the renderer can execute
|
|
185
|
+
rendererReady.value = true;
|
|
145
186
|
return;
|
|
146
187
|
}
|
|
188
|
+
// CAMERA
|
|
189
|
+
// ====================
|
|
190
|
+
// is there already a camera?
|
|
191
|
+
camera = tryGetNodeWithInstanceType([
|
|
192
|
+
'PerspectiveCamera',
|
|
193
|
+
'OrthographicCamera',
|
|
194
|
+
]);
|
|
195
|
+
// if not, let's create one
|
|
196
|
+
if (!camera) {
|
|
197
|
+
// create ortho camera
|
|
198
|
+
if (props.ortho || props.orthographic) {
|
|
199
|
+
ensuredCamera.value = createNode({
|
|
200
|
+
props: { args: props.cameraArgs ?? [] },
|
|
201
|
+
type: 'OrthographicCamera',
|
|
202
|
+
uuid: fallbackCameraUuid,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
ensuredCamera.value = createNode({
|
|
207
|
+
props: {
|
|
208
|
+
args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
|
|
209
|
+
},
|
|
210
|
+
type: 'PerspectiveCamera',
|
|
211
|
+
uuid: fallbackCameraUuid,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
cameraReady.value = true;
|
|
215
|
+
camera = ensuredCamera.value;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
cameraReady.value = true;
|
|
219
|
+
}
|
|
220
|
+
// move camera if needed
|
|
221
|
+
if (camera && props.cameraPosition) {
|
|
222
|
+
camera.instance?.position.set(...props.cameraPosition);
|
|
223
|
+
}
|
|
224
|
+
// angle camera if needed
|
|
225
|
+
if (camera && (props.cameraLookAt || props.cameraLook)) {
|
|
226
|
+
const source = (props.cameraLookAt || props.cameraLook);
|
|
227
|
+
camera.instance?.lookAt(...source);
|
|
228
|
+
}
|
|
147
229
|
// SCENE
|
|
148
230
|
// ====================
|
|
149
|
-
scene =
|
|
231
|
+
scene = ensuredScene.value;
|
|
150
232
|
// set background color
|
|
151
233
|
if (scene && scene.instance && props.background) {
|
|
152
234
|
scene.instance.background = new THREE.Color(props.background);
|
|
@@ -156,7 +238,7 @@
|
|
|
156
238
|
if (dpr.value === -1) {
|
|
157
239
|
dpr.value = window.devicePixelRatio;
|
|
158
240
|
}
|
|
159
|
-
if (renderer
|
|
241
|
+
if (renderer?.instance) {
|
|
160
242
|
renderer.instance.setPixelRatio(dpr.value);
|
|
161
243
|
globals.dpr.value = dpr.value;
|
|
162
244
|
// prep canvas (sizing, observe, unmount, etc)
|
|
@@ -167,9 +249,10 @@
|
|
|
167
249
|
}
|
|
168
250
|
// KICK UPDATE
|
|
169
251
|
// ====================
|
|
252
|
+
// console.log(scene)
|
|
170
253
|
update({
|
|
171
254
|
app: vue.getCurrentInstance().appContext.app,
|
|
172
|
-
camera,
|
|
255
|
+
camera: camera.instance,
|
|
173
256
|
renderer: renderer.instance,
|
|
174
257
|
scene: scene.instance,
|
|
175
258
|
});
|
|
@@ -199,21 +282,6 @@
|
|
|
199
282
|
},
|
|
200
283
|
};
|
|
201
284
|
|
|
202
|
-
const catalogue = {};
|
|
203
|
-
|
|
204
|
-
const lunchboxDomComponentNames = [
|
|
205
|
-
'canvas',
|
|
206
|
-
'div',
|
|
207
|
-
'LunchboxWrapper',
|
|
208
|
-
];
|
|
209
|
-
// component creation utility
|
|
210
|
-
const createComponent$1 = (tag) => vue.defineComponent({
|
|
211
|
-
inheritAttrs: false,
|
|
212
|
-
name: tag,
|
|
213
|
-
setup(props, context) {
|
|
214
|
-
return () => vue.h(tag, context.attrs, context.slots?.default?.() || []);
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
285
|
// list of all components to register out of the box
|
|
218
286
|
const autoGeneratedComponents = [
|
|
219
287
|
// ThreeJS basics
|
|
@@ -326,21 +394,11 @@
|
|
|
326
394
|
'arrayCamera',
|
|
327
395
|
// renderers
|
|
328
396
|
'webGLRenderer',
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
});
|
|
333
|
-
const components = {
|
|
334
|
-
...autoGeneratedComponents,
|
|
335
|
-
'Lunchbox': LunchboxWrapper,
|
|
336
|
-
// Gltf,
|
|
337
|
-
};
|
|
338
|
-
// console.log(components, Gltf)
|
|
339
|
-
/*
|
|
340
|
-
// List copied from r3f
|
|
341
|
-
// https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
|
|
397
|
+
/*
|
|
398
|
+
// List copied from r3f:
|
|
399
|
+
// https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
|
|
342
400
|
|
|
343
|
-
|
|
401
|
+
// NOT IMPLEMENTED (can be added via Extend - docs.lunchboxjs.com/components/extend/):
|
|
344
402
|
audioListener: AudioListenerProps
|
|
345
403
|
positionalAudio: PositionalAudioProps
|
|
346
404
|
|
|
@@ -388,6 +446,27 @@
|
|
|
388
446
|
fogExp2: FogExp2Props
|
|
389
447
|
shape: ShapeProps
|
|
390
448
|
*/
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
const catalogue = {};
|
|
452
|
+
|
|
453
|
+
const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper'];
|
|
454
|
+
// component creation utility
|
|
455
|
+
const createComponent$1 = (tag) => vue.defineComponent({
|
|
456
|
+
inheritAttrs: false,
|
|
457
|
+
name: tag,
|
|
458
|
+
setup(props, context) {
|
|
459
|
+
return () => vue.h(tag, context.attrs, context.slots?.default?.() || []);
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
|
|
463
|
+
acc[curr.name] = curr;
|
|
464
|
+
return acc;
|
|
465
|
+
});
|
|
466
|
+
const components = {
|
|
467
|
+
...autoGeneratedComponents,
|
|
468
|
+
Lunchbox: LunchboxWrapper,
|
|
469
|
+
};
|
|
391
470
|
|
|
392
471
|
function find(target) {
|
|
393
472
|
target = vue.isRef(target) ? target.value : target;
|
|
@@ -444,7 +523,7 @@
|
|
|
444
523
|
/** Create a new Lunchbox comment node. */
|
|
445
524
|
function createCommentNode(options = {}) {
|
|
446
525
|
const defaults = {
|
|
447
|
-
text: options.text ?? ''
|
|
526
|
+
text: options.text ?? '',
|
|
448
527
|
};
|
|
449
528
|
return new MiniDom.RendererCommentNode({
|
|
450
529
|
...defaults,
|
|
@@ -468,7 +547,7 @@
|
|
|
468
547
|
/** Create a new Lunchbox text node. */
|
|
469
548
|
function createTextNode(options = {}) {
|
|
470
549
|
const defaults = {
|
|
471
|
-
text: options.text ?? ''
|
|
550
|
+
text: options.text ?? '',
|
|
472
551
|
};
|
|
473
552
|
return new MiniDom.RendererTextNode({
|
|
474
553
|
...options,
|
|
@@ -481,7 +560,7 @@
|
|
|
481
560
|
const defaults = {
|
|
482
561
|
attached: options.attached ?? [],
|
|
483
562
|
attachedArray: options.attachedArray ?? {},
|
|
484
|
-
instance: options.instance ?? null
|
|
563
|
+
instance: options.instance ?? null,
|
|
485
564
|
};
|
|
486
565
|
const node = new MiniDom.RendererStandardNode({
|
|
487
566
|
...options,
|
|
@@ -489,14 +568,25 @@
|
|
|
489
568
|
metaType: 'standardMeta',
|
|
490
569
|
});
|
|
491
570
|
if (node.type && !isLunchboxRootNode(node) && !node.instance) {
|
|
571
|
+
// if (node.type.includes('Camera')) {
|
|
572
|
+
// console.log(node.type, {
|
|
573
|
+
// ...node.props,
|
|
574
|
+
// ...props,
|
|
575
|
+
// })
|
|
576
|
+
// console.trace()
|
|
577
|
+
// }
|
|
492
578
|
node.instance = instantiateThreeObject({
|
|
493
579
|
...node,
|
|
494
580
|
props: {
|
|
495
581
|
...node.props,
|
|
496
582
|
...props,
|
|
497
|
-
}
|
|
583
|
+
},
|
|
498
584
|
});
|
|
499
585
|
}
|
|
586
|
+
if (node.type === 'scene') {
|
|
587
|
+
// manually set scene override
|
|
588
|
+
ensuredScene.value = node;
|
|
589
|
+
}
|
|
500
590
|
return node;
|
|
501
591
|
}
|
|
502
592
|
|
|
@@ -527,7 +617,9 @@
|
|
|
527
617
|
node.eventListeners[key].push(value);
|
|
528
618
|
// if we need it, let's get/create the main raycaster
|
|
529
619
|
if (interactionsRequiringRaycaster.includes(key)) {
|
|
530
|
-
|
|
620
|
+
// we're not using `v` here, we're just making sure the raycaster has been created
|
|
621
|
+
// TODO: is this necessary?
|
|
622
|
+
ensuredRaycaster.value;
|
|
531
623
|
if (node.instance && !interactables.includes(node)) {
|
|
532
624
|
addInteractable(node);
|
|
533
625
|
node.eventListenerRemoveFunctions[key].push(() => removeInteractable(node));
|
|
@@ -585,7 +677,7 @@
|
|
|
585
677
|
return;
|
|
586
678
|
// add mouse events once renderer is ready
|
|
587
679
|
let stopWatcher = null;
|
|
588
|
-
stopWatcher = vue.watch(() =>
|
|
680
|
+
stopWatcher = vue.watch(() => ensureRenderer.value, (renderer) => {
|
|
589
681
|
// make sure renderer exists
|
|
590
682
|
if (!renderer?.instance)
|
|
591
683
|
return;
|
|
@@ -626,8 +718,8 @@
|
|
|
626
718
|
let currentIntersections = [];
|
|
627
719
|
const autoRaycasterBeforeRender = () => {
|
|
628
720
|
// setup
|
|
629
|
-
const raycaster =
|
|
630
|
-
const camera =
|
|
721
|
+
const raycaster = ensuredRaycaster.value?.instance;
|
|
722
|
+
const camera = ensuredCamera.value?.instance;
|
|
631
723
|
if (!raycaster || !camera)
|
|
632
724
|
return;
|
|
633
725
|
raycaster.setFromCamera(globals.mousePos.value, camera);
|
|
@@ -711,119 +803,147 @@
|
|
|
711
803
|
}
|
|
712
804
|
return exports.lunchboxTree;
|
|
713
805
|
}
|
|
714
|
-
//
|
|
806
|
+
// This is used in `buildEnsured` below and `LunchboxWrapper`
|
|
807
|
+
/** Search the overrides record and the node tree for a node in the given types */
|
|
808
|
+
function tryGetNodeWithInstanceType(pascalCaseTypes) {
|
|
809
|
+
if (!Array.isArray(pascalCaseTypes)) {
|
|
810
|
+
pascalCaseTypes = [pascalCaseTypes];
|
|
811
|
+
}
|
|
812
|
+
// default to override if we have one
|
|
813
|
+
for (let singleType of pascalCaseTypes) {
|
|
814
|
+
if (overrides[singleType])
|
|
815
|
+
return overrides[singleType];
|
|
816
|
+
}
|
|
817
|
+
// look for auto-created node
|
|
818
|
+
for (let singleType of pascalCaseTypes) {
|
|
819
|
+
const found = autoCreated[singleType] ||
|
|
820
|
+
allNodes.find((node) => node.type?.toLowerCase() ===
|
|
821
|
+
singleType.toLowerCase());
|
|
822
|
+
// if we have one, save and return
|
|
823
|
+
if (found) {
|
|
824
|
+
const createdAsNode = found;
|
|
825
|
+
autoCreated[singleType] = createdAsNode;
|
|
826
|
+
return createdAsNode;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
// GENERIC ENSURE FUNCTION
|
|
715
832
|
// ====================
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
833
|
+
// Problem:
|
|
834
|
+
// I want to make sure an object of type Xyz exists in my Lunchbox app.
|
|
835
|
+
// If it doesn't exist, I want to create it and add it to the root node.
|
|
836
|
+
//
|
|
837
|
+
// Solution:
|
|
838
|
+
// export const ensuredXyz = buildEnsured<Xyz>('Xyz', 'FALLBACK_XYZ')
|
|
839
|
+
//
|
|
840
|
+
// Now in other components, you can do both:
|
|
841
|
+
// import { ensuredXyz }
|
|
842
|
+
// ensuredXyz.value (...)
|
|
843
|
+
// and:
|
|
844
|
+
// ensuredXyz.value = ...
|
|
845
|
+
const autoCreated = vue.reactive({});
|
|
846
|
+
const overrides = vue.reactive({});
|
|
847
|
+
/**
|
|
848
|
+
* Build a computed ensured value with a getter and setter.
|
|
849
|
+
* @param pascalCaseTypes List of types this can be. Will autocreate first type if array provided.
|
|
850
|
+
* @param fallbackUuid Fallback UUID to use.
|
|
851
|
+
* @param props Props to pass to autocreated element
|
|
852
|
+
* @returns Computed getter/setter for ensured object.
|
|
853
|
+
*/
|
|
854
|
+
function buildEnsured(pascalCaseTypes, fallbackUuid, props = {}, callback = null) {
|
|
855
|
+
// make sure we've got an array
|
|
856
|
+
if (!Array.isArray(pascalCaseTypes)) {
|
|
857
|
+
pascalCaseTypes = [pascalCaseTypes];
|
|
729
858
|
}
|
|
730
|
-
//
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
859
|
+
// add type for autoCreated and overrides
|
|
860
|
+
for (let singleType of pascalCaseTypes) {
|
|
861
|
+
if (!autoCreated[singleType]) {
|
|
862
|
+
autoCreated[singleType] = null;
|
|
863
|
+
}
|
|
864
|
+
if (!overrides[singleType]) {
|
|
865
|
+
overrides[singleType] = null;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return vue.computed({
|
|
869
|
+
get() {
|
|
870
|
+
// try to get existing type
|
|
871
|
+
const existing = tryGetNodeWithInstanceType(pascalCaseTypes);
|
|
872
|
+
if (existing)
|
|
873
|
+
return existing;
|
|
874
|
+
// otherwise, create a new node
|
|
875
|
+
const root = ensureRootNode();
|
|
876
|
+
const node = createNode({
|
|
877
|
+
type: pascalCaseTypes[0],
|
|
878
|
+
uuid: fallbackUuid,
|
|
879
|
+
props,
|
|
880
|
+
});
|
|
881
|
+
root.addChild(node);
|
|
882
|
+
autoCreated[pascalCaseTypes[0]] = node;
|
|
883
|
+
if (callback) {
|
|
884
|
+
callback(node);
|
|
885
|
+
}
|
|
886
|
+
return node;
|
|
887
|
+
},
|
|
888
|
+
set(val) {
|
|
889
|
+
const t = val.type ?? '';
|
|
890
|
+
const pascalType = t[0].toUpperCase() + t.slice(1);
|
|
891
|
+
overrides[pascalType] = val;
|
|
737
892
|
},
|
|
738
893
|
});
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
894
|
+
}
|
|
895
|
+
// ENSURE CAMERA
|
|
896
|
+
// ====================
|
|
897
|
+
const fallbackCameraUuid = 'FALLBACK_CAMERA';
|
|
898
|
+
const defaultCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, { args: [45, 0.5625, 1, 1000] });
|
|
899
|
+
/** Special value to be changed ONLY in `LunchboxWrapper`.
|
|
900
|
+
* Functions waiting for a Camera need to wait for this to be true. */
|
|
901
|
+
const cameraReady = vue.ref(false);
|
|
902
|
+
const ensuredCamera = vue.computed({
|
|
903
|
+
get() {
|
|
904
|
+
return (cameraReady.value ? defaultCamera.value : null);
|
|
905
|
+
},
|
|
906
|
+
set(val) {
|
|
907
|
+
const t = val.type ?? '';
|
|
908
|
+
const pascalType = t[0].toUpperCase() + t.slice(1);
|
|
909
|
+
overrides[pascalType] = val;
|
|
910
|
+
},
|
|
911
|
+
});
|
|
912
|
+
// export const ensuredCamera = buildEnsured<THREE.Camera>(
|
|
913
|
+
// ['PerspectiveCamera', 'OrthographicCamera'],
|
|
914
|
+
// fallbackCameraUuid,
|
|
915
|
+
// {
|
|
916
|
+
// args: [45, 0.5625, 1, 1000],
|
|
917
|
+
// }
|
|
918
|
+
// )
|
|
745
919
|
// ENSURE RENDERER
|
|
746
920
|
// ====================
|
|
747
921
|
const fallbackRendererUuid = 'FALLBACK_RENDERER';
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
uuid: fallbackRendererUuid,
|
|
766
|
-
}, { args: fallbackArgs });
|
|
767
|
-
// shadow sugar
|
|
768
|
-
if (sugar?.shadow) {
|
|
769
|
-
rendererNode.instance.shadowMap.enabled = true;
|
|
770
|
-
if (typeof sugar.shadow === 'object') {
|
|
771
|
-
rendererNode.instance.shadowMap.type = sugar.shadow.type;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
root.addChild(rendererNode);
|
|
775
|
-
createdRenderer.value = rendererNode;
|
|
776
|
-
// return created node
|
|
777
|
-
return rendererNode;
|
|
778
|
-
};
|
|
922
|
+
const v = buildEnsured(
|
|
923
|
+
// TODO: ensure support for css/svg renderers
|
|
924
|
+
['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
|
|
925
|
+
fallbackRendererUuid, {});
|
|
926
|
+
/** Special value to be changed ONLY in `LunchboxWrapper`.
|
|
927
|
+
* Functions waiting for a Renderer need to wait for this to be true. */
|
|
928
|
+
const rendererReady = vue.ref(false);
|
|
929
|
+
const ensureRenderer = vue.computed({
|
|
930
|
+
get() {
|
|
931
|
+
return (rendererReady.value ? v.value : null);
|
|
932
|
+
},
|
|
933
|
+
set(val) {
|
|
934
|
+
const t = val.type ?? '';
|
|
935
|
+
const pascalType = t[0].toUpperCase() + t.slice(1);
|
|
936
|
+
overrides[pascalType] = val;
|
|
937
|
+
},
|
|
938
|
+
});
|
|
779
939
|
// ENSURE SCENE
|
|
780
940
|
// ====================
|
|
781
941
|
const fallbackSceneUuid = 'FALLBACK_SCENE';
|
|
782
|
-
const
|
|
783
|
-
const ensureScene = () => {
|
|
784
|
-
// look for scenes
|
|
785
|
-
const foundScene = allNodes.find((node) => node.type?.toLowerCase() === 'scene');
|
|
786
|
-
// if we have one, return
|
|
787
|
-
if (foundScene) {
|
|
788
|
-
const sceneAsLunchboxNode = foundScene;
|
|
789
|
-
createdScene.value = sceneAsLunchboxNode;
|
|
790
|
-
return sceneAsLunchboxNode;
|
|
791
|
-
}
|
|
792
|
-
// otherwise, create a new scene
|
|
793
|
-
const root = ensureRootNode();
|
|
794
|
-
const sceneNode = createNode({
|
|
795
|
-
type: 'Scene',
|
|
796
|
-
uuid: fallbackSceneUuid,
|
|
797
|
-
});
|
|
798
|
-
root.addChild(sceneNode);
|
|
799
|
-
createdScene.value = sceneNode;
|
|
800
|
-
return sceneNode;
|
|
801
|
-
};
|
|
942
|
+
const ensuredScene = buildEnsured('Scene', fallbackSceneUuid);
|
|
802
943
|
// ENSURE AUTO-RAYCASTER
|
|
803
944
|
const autoRaycasterUuid = 'AUTO_RAYCASTER';
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
// look for autoraycaster
|
|
807
|
-
const found = allNodes.find((node) => node.uuid === autoRaycasterUuid);
|
|
808
|
-
// if we have one, return
|
|
809
|
-
if (found) {
|
|
810
|
-
const foundAsNode = found;
|
|
811
|
-
createdRaycaster.value = foundAsNode;
|
|
812
|
-
return foundAsNode;
|
|
813
|
-
}
|
|
814
|
-
// otherwise, create raycaster
|
|
815
|
-
const root = ensureRootNode();
|
|
816
|
-
const raycasterNode = createNode({
|
|
817
|
-
type: 'Raycaster',
|
|
818
|
-
uuid: autoRaycasterUuid,
|
|
819
|
-
});
|
|
820
|
-
root.addChild(raycasterNode);
|
|
821
|
-
createdRaycaster.value = raycasterNode;
|
|
822
|
-
// finish auto-raycaster setup
|
|
823
|
-
setupAutoRaycaster(raycasterNode);
|
|
824
|
-
// done with raycaster
|
|
825
|
-
return raycasterNode;
|
|
826
|
-
};
|
|
945
|
+
// `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
|
|
946
|
+
const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
|
|
827
947
|
|
|
828
948
|
const createComponent = (tag) => vue.defineComponent({
|
|
829
949
|
inheritAttrs: false,
|
|
@@ -871,9 +991,11 @@
|
|
|
871
991
|
// replace $attached values with their instances
|
|
872
992
|
// we need to guarantee everything comes back as an array so we can spread $attachedArrays,
|
|
873
993
|
// so we'll use processPropAsArray
|
|
874
|
-
const argsWrappedInArrays = args.map((arg) => {
|
|
994
|
+
const argsWrappedInArrays = args.map((arg) => {
|
|
995
|
+
return processPropAsArray({ node, prop: arg });
|
|
996
|
+
});
|
|
875
997
|
let processedArgs = [];
|
|
876
|
-
argsWrappedInArrays.forEach(arr => {
|
|
998
|
+
argsWrappedInArrays.forEach((arr) => {
|
|
877
999
|
processedArgs = processedArgs.concat(arr);
|
|
878
1000
|
});
|
|
879
1001
|
const instance = new targetClass(...processedArgs);
|
|
@@ -975,7 +1097,7 @@
|
|
|
975
1097
|
get nextSibling() {
|
|
976
1098
|
if (!this.parentNode)
|
|
977
1099
|
return null;
|
|
978
|
-
const idx = this.parentNode.children.findIndex(n => n.uuid === this.uuid);
|
|
1100
|
+
const idx = this.parentNode.children.findIndex((n) => n.uuid === this.uuid);
|
|
979
1101
|
// return next sibling if we're present and not the last child of the parent
|
|
980
1102
|
if (idx !== -1 && idx < this.parentNode.children.length - 1) {
|
|
981
1103
|
return this.parentNode.children[idx + 1];
|
|
@@ -985,7 +1107,7 @@
|
|
|
985
1107
|
insertBefore(child, anchor) {
|
|
986
1108
|
child.removeAsChildFromAnyParents();
|
|
987
1109
|
child.parentNode = this;
|
|
988
|
-
const anchorIdx = this.children.findIndex(n => n.uuid === anchor?.uuid);
|
|
1110
|
+
const anchorIdx = this.children.findIndex((n) => n.uuid === anchor?.uuid);
|
|
989
1111
|
if (anchorIdx !== -1) {
|
|
990
1112
|
this.children.splice(anchorIdx, 0, child);
|
|
991
1113
|
}
|
|
@@ -994,7 +1116,7 @@
|
|
|
994
1116
|
}
|
|
995
1117
|
}
|
|
996
1118
|
removeChild(child) {
|
|
997
|
-
const idx = this.children.findIndex(n => n?.uuid === child?.uuid);
|
|
1119
|
+
const idx = this.children.findIndex((n) => n?.uuid === child?.uuid);
|
|
998
1120
|
if (idx !== -1) {
|
|
999
1121
|
this.children.splice(idx, 1);
|
|
1000
1122
|
}
|
|
@@ -1006,7 +1128,7 @@
|
|
|
1006
1128
|
if (child) {
|
|
1007
1129
|
// remove child from any other parents
|
|
1008
1130
|
child.removeAsChildFromAnyParents();
|
|
1009
|
-
// add to this node
|
|
1131
|
+
// add to this node
|
|
1010
1132
|
child.parentNode = this;
|
|
1011
1133
|
this.insertBefore(child, null);
|
|
1012
1134
|
}
|
|
@@ -1034,11 +1156,15 @@
|
|
|
1034
1156
|
// TODO: depth-first vs breadth-first
|
|
1035
1157
|
walk(callback) {
|
|
1036
1158
|
const queue = [this, ...this.children];
|
|
1159
|
+
const traversed = [];
|
|
1037
1160
|
let canContinue = true;
|
|
1038
1161
|
while (queue.length && canContinue) {
|
|
1039
1162
|
const current = queue.shift();
|
|
1040
1163
|
if (current) {
|
|
1041
|
-
|
|
1164
|
+
if (traversed.includes(current))
|
|
1165
|
+
continue;
|
|
1166
|
+
traversed.push(current);
|
|
1167
|
+
queue.push(...current.children.filter((child) => !traversed.includes(child)));
|
|
1042
1168
|
canContinue = callback(current);
|
|
1043
1169
|
}
|
|
1044
1170
|
else {
|
|
@@ -1050,7 +1176,7 @@
|
|
|
1050
1176
|
// ====================
|
|
1051
1177
|
minidomType;
|
|
1052
1178
|
removeAsChildFromAnyParents() {
|
|
1053
|
-
allNodes.forEach(node => node.removeChild(this));
|
|
1179
|
+
allNodes.forEach((node) => node.removeChild(this));
|
|
1054
1180
|
}
|
|
1055
1181
|
}
|
|
1056
1182
|
MiniDom.BaseNode = BaseNode;
|
|
@@ -1074,8 +1200,8 @@
|
|
|
1074
1200
|
drop() {
|
|
1075
1201
|
super.drop();
|
|
1076
1202
|
// handle remove functions
|
|
1077
|
-
Object.keys(this.eventListenerRemoveFunctions).forEach(key => {
|
|
1078
|
-
this.eventListenerRemoveFunctions[key].forEach(func => func());
|
|
1203
|
+
Object.keys(this.eventListenerRemoveFunctions).forEach((key) => {
|
|
1204
|
+
this.eventListenerRemoveFunctions[key].forEach((func) => func());
|
|
1079
1205
|
});
|
|
1080
1206
|
}
|
|
1081
1207
|
}
|
|
@@ -1086,7 +1212,8 @@
|
|
|
1086
1212
|
class RendererRootNode extends MiniDom.RendererBaseNode {
|
|
1087
1213
|
constructor(options = {}, parent) {
|
|
1088
1214
|
super(options, parent);
|
|
1089
|
-
this.domElement =
|
|
1215
|
+
this.domElement =
|
|
1216
|
+
options.domElement ?? document.createElement('div');
|
|
1090
1217
|
}
|
|
1091
1218
|
domElement;
|
|
1092
1219
|
isLunchboxRootNode = true;
|
|
@@ -1103,7 +1230,8 @@
|
|
|
1103
1230
|
class RendererDomNode extends MiniDom.RendererBaseNode {
|
|
1104
1231
|
constructor(options = {}, parent) {
|
|
1105
1232
|
super(options, parent);
|
|
1106
|
-
this.domElement =
|
|
1233
|
+
this.domElement =
|
|
1234
|
+
options.domElement ?? document.createElement('div');
|
|
1107
1235
|
}
|
|
1108
1236
|
domElement;
|
|
1109
1237
|
}
|
|
@@ -1136,7 +1264,14 @@
|
|
|
1136
1264
|
const beforeRender = [];
|
|
1137
1265
|
const afterRender = [];
|
|
1138
1266
|
const update = (opts) => {
|
|
1139
|
-
|
|
1267
|
+
// request next frame
|
|
1268
|
+
frameID = requestAnimationFrame(() => update({
|
|
1269
|
+
app: opts.app,
|
|
1270
|
+
renderer: ensureRenderer.value?.instance,
|
|
1271
|
+
scene: ensuredScene.value.instance,
|
|
1272
|
+
camera: ensuredCamera.value?.instance,
|
|
1273
|
+
}));
|
|
1274
|
+
// prep options
|
|
1140
1275
|
const { app, renderer, scene, camera } = opts;
|
|
1141
1276
|
// BEFORE RENDER
|
|
1142
1277
|
beforeRender.forEach((cb) => {
|
|
@@ -1146,7 +1281,12 @@
|
|
|
1146
1281
|
});
|
|
1147
1282
|
// RENDER
|
|
1148
1283
|
if (renderer && scene && camera) {
|
|
1149
|
-
|
|
1284
|
+
if (app.customRender) {
|
|
1285
|
+
app.customRender(opts);
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
renderer.render(vue.toRaw(scene), vue.toRaw(camera));
|
|
1289
|
+
}
|
|
1150
1290
|
}
|
|
1151
1291
|
// AFTER RENDER
|
|
1152
1292
|
afterRender.forEach((cb) => {
|
|
@@ -1154,22 +1294,6 @@
|
|
|
1154
1294
|
cb(opts);
|
|
1155
1295
|
}
|
|
1156
1296
|
});
|
|
1157
|
-
/*
|
|
1158
|
-
frameID = requestAnimationFrame(() => update(renderer, scene, camera))
|
|
1159
|
-
|
|
1160
|
-
// Make sure we have all necessary components
|
|
1161
|
-
if (!renderer) {
|
|
1162
|
-
renderer = ensureRenderer().instance
|
|
1163
|
-
}
|
|
1164
|
-
if (!scene) {
|
|
1165
|
-
scene = ensureScene().instance
|
|
1166
|
-
}
|
|
1167
|
-
if (!camera) {
|
|
1168
|
-
camera = ensureCamera().instance
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
*/
|
|
1173
1297
|
};
|
|
1174
1298
|
const onBeforeRender = (cb, index = Infinity) => {
|
|
1175
1299
|
if (index === Infinity) {
|
|
@@ -1179,6 +1303,15 @@
|
|
|
1179
1303
|
beforeRender.splice(index, 0, cb);
|
|
1180
1304
|
}
|
|
1181
1305
|
};
|
|
1306
|
+
const offBeforeRender = (cb) => {
|
|
1307
|
+
if (isFinite(cb)) {
|
|
1308
|
+
beforeRender.splice(cb, 1);
|
|
1309
|
+
}
|
|
1310
|
+
else {
|
|
1311
|
+
const idx = beforeRender.findIndex((v) => v == cb);
|
|
1312
|
+
beforeRender.splice(idx, 1);
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1182
1315
|
const onAfterRender = (cb, index = Infinity) => {
|
|
1183
1316
|
if (index === Infinity) {
|
|
1184
1317
|
afterRender.push(cb);
|
|
@@ -1187,6 +1320,15 @@
|
|
|
1187
1320
|
afterRender.splice(index, 0, cb);
|
|
1188
1321
|
}
|
|
1189
1322
|
};
|
|
1323
|
+
const offAfterRender = (cb) => {
|
|
1324
|
+
if (isFinite(cb)) {
|
|
1325
|
+
afterRender.splice(cb, 1);
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
const idx = afterRender.findIndex((v) => v == cb);
|
|
1329
|
+
afterRender.splice(idx, 1);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1190
1332
|
const cancelUpdate = () => {
|
|
1191
1333
|
if (frameID)
|
|
1192
1334
|
cancelAnimationFrame(frameID);
|
|
@@ -1292,10 +1434,7 @@
|
|
|
1292
1434
|
'src',
|
|
1293
1435
|
];
|
|
1294
1436
|
|
|
1295
|
-
const autoAttach = [
|
|
1296
|
-
'geometry',
|
|
1297
|
-
'material',
|
|
1298
|
-
];
|
|
1437
|
+
const autoAttach = ['geometry', 'material'];
|
|
1299
1438
|
const createElement = (type, isSVG, isCustomizedBuiltin, vnodeProps) => {
|
|
1300
1439
|
const options = { type };
|
|
1301
1440
|
if (vnodeProps) {
|
|
@@ -1310,7 +1449,7 @@
|
|
|
1310
1449
|
// handle standard node
|
|
1311
1450
|
const node = createNode(options);
|
|
1312
1451
|
// autoattach
|
|
1313
|
-
autoAttach.forEach(key => {
|
|
1452
|
+
autoAttach.forEach((key) => {
|
|
1314
1453
|
if (type.toLowerCase().endsWith(key)) {
|
|
1315
1454
|
node.props.attach = key;
|
|
1316
1455
|
}
|
|
@@ -1350,17 +1489,19 @@
|
|
|
1350
1489
|
}
|
|
1351
1490
|
// add to scene if parent is the wrapper node
|
|
1352
1491
|
if (child.metaType === 'standardMeta' &&
|
|
1353
|
-
child.type !== '
|
|
1492
|
+
child.type !== 'scene' &&
|
|
1354
1493
|
isLunchboxRootNode(effectiveParent)) {
|
|
1355
1494
|
// ensure scene exists
|
|
1356
|
-
const sceneNode =
|
|
1495
|
+
const sceneNode = ensuredScene.value;
|
|
1357
1496
|
if (sceneNode.instance && child) {
|
|
1358
1497
|
sceneNode.addChild(child);
|
|
1359
1498
|
}
|
|
1360
1499
|
if (child.instance &&
|
|
1361
1500
|
child.instance.isObject3D &&
|
|
1362
1501
|
sceneNode.instance) {
|
|
1363
|
-
sceneNode
|
|
1502
|
+
if (sceneNode !== child) {
|
|
1503
|
+
sceneNode.instance.add(child.instance);
|
|
1504
|
+
}
|
|
1364
1505
|
}
|
|
1365
1506
|
}
|
|
1366
1507
|
// add to hierarchy otherwise
|
|
@@ -1443,19 +1584,29 @@
|
|
|
1443
1584
|
const remove = (node) => {
|
|
1444
1585
|
if (!node)
|
|
1445
1586
|
return;
|
|
1587
|
+
const overrideKeys = Object.keys(overrides);
|
|
1446
1588
|
// prep subtree
|
|
1447
1589
|
const subtree = [];
|
|
1448
|
-
node.walk(descendant => {
|
|
1590
|
+
node.walk((descendant) => {
|
|
1449
1591
|
subtree.push(descendant);
|
|
1450
1592
|
return true;
|
|
1451
1593
|
});
|
|
1452
1594
|
// clean up subtree
|
|
1453
1595
|
subtree.forEach((n) => {
|
|
1596
|
+
const overrideKey = overrideKeys.find((key) => overrides[key]?.uuid === n.uuid);
|
|
1597
|
+
// if this node is an override, remove it from the overrides list
|
|
1598
|
+
if (overrideKey) {
|
|
1599
|
+
overrides[overrideKey] = null;
|
|
1600
|
+
}
|
|
1454
1601
|
if (isLunchboxStandardNode(n)) {
|
|
1455
1602
|
// try to remove three object
|
|
1456
1603
|
n.instance?.removeFromParent?.();
|
|
1457
1604
|
// try to dispose three object
|
|
1458
|
-
const dispose =
|
|
1605
|
+
const dispose =
|
|
1606
|
+
// calling `dispose` on a scene triggers an error,
|
|
1607
|
+
// so let's ignore if this node is a scene
|
|
1608
|
+
n.type !== 'scene' &&
|
|
1609
|
+
n.instance?.dispose;
|
|
1459
1610
|
if (dispose)
|
|
1460
1611
|
dispose.bind(n.instance)();
|
|
1461
1612
|
n.instance = null;
|
|
@@ -1463,7 +1614,7 @@
|
|
|
1463
1614
|
// drop tree node
|
|
1464
1615
|
n.drop();
|
|
1465
1616
|
// remove Lunchbox node from main list
|
|
1466
|
-
const idx = allNodes.findIndex(v => v.uuid === n.uuid);
|
|
1617
|
+
const idx = allNodes.findIndex((v) => v.uuid === n.uuid);
|
|
1467
1618
|
if (idx !== -1) {
|
|
1468
1619
|
allNodes.splice(idx, 1);
|
|
1469
1620
|
}
|
|
@@ -1530,15 +1681,34 @@
|
|
|
1530
1681
|
dpr: vue.ref(1),
|
|
1531
1682
|
inputActive,
|
|
1532
1683
|
mousePos,
|
|
1533
|
-
camera: createdCamera,
|
|
1534
|
-
renderer: createdRenderer,
|
|
1535
|
-
scene: createdScene,
|
|
1536
1684
|
};
|
|
1537
|
-
const camera = vue.computed(() =>
|
|
1538
|
-
const renderer = vue.computed(() =>
|
|
1539
|
-
const scene = vue.computed(() =>
|
|
1685
|
+
const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
|
|
1686
|
+
const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
|
|
1687
|
+
const scene = vue.computed(() => ensuredScene.value.instance);
|
|
1688
|
+
// CUSTOM RENDER SUPPORT
|
|
1689
|
+
// ====================
|
|
1690
|
+
let app = null;
|
|
1691
|
+
let queuedCustomRenderFunction = null;
|
|
1692
|
+
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
1693
|
+
* Changing this requires the user to manually render their scene.
|
|
1694
|
+
*/
|
|
1695
|
+
const setCustomRender = (render) => {
|
|
1696
|
+
if (app)
|
|
1697
|
+
app.setCustomRender(render);
|
|
1698
|
+
else
|
|
1699
|
+
queuedCustomRenderFunction = render;
|
|
1700
|
+
};
|
|
1701
|
+
/** Clear the active app's custom render function. */
|
|
1702
|
+
const clearCustomRender = () => {
|
|
1703
|
+
if (app)
|
|
1704
|
+
app.clearCustomRender();
|
|
1705
|
+
else
|
|
1706
|
+
queuedCustomRenderFunction = null;
|
|
1707
|
+
};
|
|
1708
|
+
// CREATE APP
|
|
1709
|
+
// ====================
|
|
1540
1710
|
const createApp = (root) => {
|
|
1541
|
-
|
|
1711
|
+
app = vue.createRenderer(nodeOps).createApp(root);
|
|
1542
1712
|
// register all components
|
|
1543
1713
|
Object.keys(components).forEach((key) => {
|
|
1544
1714
|
app.component(key, components[key]);
|
|
@@ -1561,27 +1731,38 @@
|
|
|
1561
1731
|
};
|
|
1562
1732
|
// embed .extend function
|
|
1563
1733
|
app.extend = (targets) => {
|
|
1564
|
-
extend({ app, ...targets });
|
|
1734
|
+
extend({ app: app, ...targets });
|
|
1565
1735
|
return app;
|
|
1566
1736
|
};
|
|
1567
|
-
//
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
//
|
|
1737
|
+
// prep for custom render support
|
|
1738
|
+
app.setCustomRender = (newRender) => {
|
|
1739
|
+
app.customRender = newRender;
|
|
1740
|
+
};
|
|
1741
|
+
// add queued custom render if we have one
|
|
1742
|
+
if (queuedCustomRenderFunction) {
|
|
1743
|
+
app.setCustomRender(queuedCustomRenderFunction);
|
|
1744
|
+
queuedCustomRenderFunction = null;
|
|
1745
|
+
}
|
|
1746
|
+
// add custom render removal
|
|
1747
|
+
app.clearCustomRender = () => {
|
|
1748
|
+
app.customRender = null;
|
|
1749
|
+
};
|
|
1572
1750
|
// done
|
|
1573
1751
|
return app;
|
|
1574
1752
|
};
|
|
1575
1753
|
|
|
1576
1754
|
exports.camera = camera;
|
|
1755
|
+
exports.clearCustomRender = clearCustomRender;
|
|
1577
1756
|
exports.createApp = createApp;
|
|
1578
1757
|
exports.find = find;
|
|
1579
1758
|
exports.globals = globals;
|
|
1759
|
+
exports.offAfterRender = offAfterRender;
|
|
1760
|
+
exports.offBeforeRender = offBeforeRender;
|
|
1580
1761
|
exports.onAfterRender = onAfterRender;
|
|
1581
1762
|
exports.onBeforeRender = onBeforeRender;
|
|
1582
1763
|
exports.renderer = renderer;
|
|
1583
1764
|
exports.scene = scene;
|
|
1584
|
-
exports.
|
|
1765
|
+
exports.setCustomRender = setCustomRender;
|
|
1585
1766
|
|
|
1586
1767
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1587
1768
|
|