lunchboxjs 0.2.1001-beta.0 → 0.2.1001-beta.301
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 +0 -47
- package/dist/lunchboxjs.js +438 -420
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +435 -408
- package/extras/OrbitControlsWrapper.vue +2 -12
- package/package.json +6 -2
- package/src/components/LunchboxEventHandlers.tsx +237 -0
- package/src/components/LunchboxWrapper/LunchboxWrapper.tsx +49 -7
- package/src/components/autoGeneratedComponents.ts +1 -1
- package/src/components/index.ts +2 -2
- package/src/core/createNode.ts +1 -1
- package/src/core/extend.ts +1 -1
- package/src/core/index.ts +0 -1
- package/src/core/instantiateThreeObject/index.ts +1 -1
- package/src/core/instantiateThreeObject/processProps.ts +1 -1
- package/src/core/interaction.ts +55 -0
- package/src/core/minidom.ts +1 -1
- package/src/core/update.ts +32 -44
- package/src/core/updateObjectProp.ts +21 -16
- package/src/index.ts +65 -34
- package/src/keys.ts +1 -0
- package/src/nodeOps/index.ts +70 -57
- package/src/types.ts +0 -8
- package/src/utils/index.ts +10 -0
- package/src/components/catalogue.ts +0 -3
- package/src/core/ensure.ts +0 -16
- package/src/core/interaction/index.ts +0 -102
- package/src/core/interaction/input.ts +0 -4
- package/src/core/interaction/interactables.ts +0 -14
- package/src/core/interaction/setupAutoRaycaster.ts +0 -232
package/dist/lunchboxjs.js
CHANGED
|
@@ -124,242 +124,11 @@
|
|
|
124
124
|
return node;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const globalsInjectionKey = Symbol();
|
|
128
|
-
const updateGlobalsInjectionKey = Symbol();
|
|
129
|
-
const setCustomRenderKey = Symbol();
|
|
130
|
-
const clearCustomRenderKey = Symbol();
|
|
131
|
-
const beforeRenderKey = Symbol();
|
|
132
|
-
const onBeforeRenderKey = Symbol();
|
|
133
|
-
const offBeforeRenderKey = Symbol();
|
|
134
|
-
const afterRenderKey = Symbol();
|
|
135
|
-
const onAfterRenderKey = Symbol();
|
|
136
|
-
const offAfterRenderKey = Symbol();
|
|
137
|
-
const frameIdKey = Symbol();
|
|
138
|
-
const watchStopHandleKey = Symbol();
|
|
139
|
-
const appRootNodeKey = Symbol();
|
|
140
|
-
const appKey = Symbol();
|
|
141
|
-
const appRenderersKey = Symbol();
|
|
142
|
-
const appSceneKey = Symbol();
|
|
143
|
-
const appCameraKey = Symbol();
|
|
144
|
-
const startCallbackKey = Symbol();
|
|
145
|
-
|
|
146
|
-
const ensuredCamera = () => vue.inject(appCameraKey); // ENSURE RENDERER
|
|
147
|
-
// ====================
|
|
148
|
-
|
|
149
|
-
const ensureRenderer = () => vue.inject(appRenderersKey); // ENSURE SCENE
|
|
150
|
-
// ====================
|
|
151
|
-
|
|
152
|
-
const ensuredScene = () => vue.inject(appSceneKey);
|
|
153
|
-
|
|
154
|
-
const interactables = [];
|
|
155
|
-
const addInteractable = target => {
|
|
156
|
-
interactables.push(target);
|
|
157
|
-
};
|
|
158
|
-
const removeInteractable = target => {
|
|
159
|
-
const idx = interactables.indexOf(target);
|
|
160
|
-
|
|
161
|
-
if (idx !== -1) {
|
|
162
|
-
interactables.splice(idx, 1);
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/** Mouse is down, touch is pressed, etc */
|
|
167
|
-
|
|
168
|
-
const inputActive = vue.ref(false);
|
|
169
|
-
|
|
170
|
-
// let mouseDownListener: (event: MouseEvent) => void
|
|
171
|
-
// let mouseUpListener: (event: MouseEvent) => void
|
|
172
|
-
|
|
173
|
-
const mousePos = vue.ref({
|
|
174
|
-
x: Infinity,
|
|
175
|
-
y: Infinity
|
|
176
|
-
}); // let autoRaycasterEventsInitialized = false
|
|
177
|
-
// let frameID: number
|
|
178
|
-
// export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
179
|
-
// const instance = node.instance
|
|
180
|
-
// if (!instance) return
|
|
181
|
-
// // TODO: inject doesn't work here. replace this raycaster with a component so we can
|
|
182
|
-
// // `inject` in `setup`?
|
|
183
|
-
// const appLevelGlobals = { dpr: window.devicePixelRatio } //useGlobals()
|
|
184
|
-
// // add mouse events once renderer is ready
|
|
185
|
-
// let stopWatcher: WatchStopHandle | null = null
|
|
186
|
-
// stopWatcher = watch(
|
|
187
|
-
// () => ensureRenderer.value,
|
|
188
|
-
// (renderer) => {
|
|
189
|
-
// // make sure renderer exists
|
|
190
|
-
// if (!renderer?.instance) return
|
|
191
|
-
// // cancel early if autoraycaster exists
|
|
192
|
-
// if (autoRaycasterEventsInitialized) {
|
|
193
|
-
// if (stopWatcher) stopWatcher()
|
|
194
|
-
// return
|
|
195
|
-
// }
|
|
196
|
-
// // create mouse events
|
|
197
|
-
// mouseMoveListener = (evt) => {
|
|
198
|
-
// const screenWidth =
|
|
199
|
-
// (renderer.instance!.domElement.width ?? 1) /
|
|
200
|
-
// appLevelGlobals.dpr
|
|
201
|
-
// const screenHeight =
|
|
202
|
-
// (renderer.instance!.domElement.height ?? 1) /
|
|
203
|
-
// appLevelGlobals.dpr
|
|
204
|
-
// mousePos.value.x = (evt.offsetX / screenWidth) * 2 - 1
|
|
205
|
-
// mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1
|
|
206
|
-
// }
|
|
207
|
-
// mouseDownListener = () => (inputActive.value = true)
|
|
208
|
-
// mouseUpListener = () => (inputActive.value = false)
|
|
209
|
-
// // add mouse events
|
|
210
|
-
// renderer.instance.domElement.addEventListener(
|
|
211
|
-
// 'mousemove',
|
|
212
|
-
// mouseMoveListener
|
|
213
|
-
// )
|
|
214
|
-
// renderer.instance.domElement.addEventListener(
|
|
215
|
-
// 'mousedown',
|
|
216
|
-
// mouseDownListener
|
|
217
|
-
// )
|
|
218
|
-
// renderer.instance.domElement.addEventListener(
|
|
219
|
-
// 'mouseup',
|
|
220
|
-
// mouseUpListener
|
|
221
|
-
// )
|
|
222
|
-
// // TODO: add touch events
|
|
223
|
-
// // process mouse events asynchronously, whenever the mouse state changes
|
|
224
|
-
// watch(
|
|
225
|
-
// () => [inputActive.value, mousePos.value.x, mousePos.value.y],
|
|
226
|
-
// () => {
|
|
227
|
-
// if (frameID) cancelAnimationFrame(frameID)
|
|
228
|
-
// frameID = requestAnimationFrame(() => {
|
|
229
|
-
// autoRaycasterBeforeRender()
|
|
230
|
-
// })
|
|
231
|
-
// }
|
|
232
|
-
// )
|
|
233
|
-
// // mark complete
|
|
234
|
-
// autoRaycasterEventsInitialized = true
|
|
235
|
-
// // cancel setup watcher
|
|
236
|
-
// if (stopWatcher) {
|
|
237
|
-
// stopWatcher()
|
|
238
|
-
// }
|
|
239
|
-
// },
|
|
240
|
-
// { immediate: true }
|
|
241
|
-
// )
|
|
242
|
-
// }
|
|
243
|
-
// AUTO-RAYCASTER CALLBACK
|
|
244
|
-
// ====================
|
|
245
|
-
|
|
246
|
-
let currentIntersections = []; // const autoRaycasterBeforeRender = () => {
|
|
247
|
-
// // setup
|
|
248
|
-
// const raycaster = ensuredRaycaster.value?.instance
|
|
249
|
-
// const camera = ensuredCamera.value?.instance
|
|
250
|
-
// if (!raycaster || !camera) return
|
|
251
|
-
// raycaster.setFromCamera(globals.mousePos.value, camera)
|
|
252
|
-
// const intersections = raycaster.intersectObjects(
|
|
253
|
-
// interactables.map((v) => v.instance as any as THREE.Object3D)
|
|
254
|
-
// )
|
|
255
|
-
// let enterValues: Array<Intersection<THREE.Object3D>> = [],
|
|
256
|
-
// sameValues: Array<Intersection<THREE.Object3D>> = [],
|
|
257
|
-
// leaveValues: Array<Intersection<THREE.Object3D>> = [],
|
|
258
|
-
// entering: Array<{
|
|
259
|
-
// element: Lunch.Node
|
|
260
|
-
// intersection: Intersection<THREE.Object3D>
|
|
261
|
-
// }> = [],
|
|
262
|
-
// staying: Array<{
|
|
263
|
-
// element: Lunch.Node
|
|
264
|
-
// intersection: Intersection<THREE.Object3D>
|
|
265
|
-
// }> = []
|
|
266
|
-
// // intersection arrays
|
|
267
|
-
// leaveValues = currentIntersections.map((v) => v.intersection)
|
|
268
|
-
// // element arrays
|
|
269
|
-
// intersections?.forEach((intersection) => {
|
|
270
|
-
// const currentIdx = currentIntersections.findIndex(
|
|
271
|
-
// (v) => v.intersection.object === intersection.object
|
|
272
|
-
// )
|
|
273
|
-
// if (currentIdx === -1) {
|
|
274
|
-
// // new intersection
|
|
275
|
-
// enterValues.push(intersection)
|
|
276
|
-
// const found = interactables.find(
|
|
277
|
-
// (v) => v.instance?.uuid === intersection.object.uuid
|
|
278
|
-
// )
|
|
279
|
-
// if (found) {
|
|
280
|
-
// entering.push({ element: found, intersection })
|
|
281
|
-
// }
|
|
282
|
-
// } else {
|
|
283
|
-
// // existing intersection
|
|
284
|
-
// sameValues.push(intersection)
|
|
285
|
-
// const found = interactables.find(
|
|
286
|
-
// (v) => v.instance?.uuid === intersection.object.uuid
|
|
287
|
-
// )
|
|
288
|
-
// if (found) {
|
|
289
|
-
// staying.push({ element: found, intersection })
|
|
290
|
-
// }
|
|
291
|
-
// }
|
|
292
|
-
// // this is a current intersection, so it won't be in our `leave` array
|
|
293
|
-
// const leaveIdx = leaveValues.findIndex(
|
|
294
|
-
// (v) => v.object.uuid === intersection.object.uuid
|
|
295
|
-
// )
|
|
296
|
-
// if (leaveIdx !== -1) {
|
|
297
|
-
// leaveValues.splice(leaveIdx, 1)
|
|
298
|
-
// }
|
|
299
|
-
// })
|
|
300
|
-
// const leaving: Array<{
|
|
301
|
-
// element: Lunch.Node
|
|
302
|
-
// intersection: Intersection<THREE.Object3D>
|
|
303
|
-
// }> = leaveValues.map((intersection) => {
|
|
304
|
-
// return {
|
|
305
|
-
// element: interactables.find(
|
|
306
|
-
// (interactable) =>
|
|
307
|
-
// interactable.instance?.uuid === intersection.object.uuid
|
|
308
|
-
// ) as any as Lunch.Node,
|
|
309
|
-
// intersection,
|
|
310
|
-
// }
|
|
311
|
-
// })
|
|
312
|
-
// // new interactions
|
|
313
|
-
// entering.forEach(({ element, intersection }) => {
|
|
314
|
-
// fireEventsFromIntersections({
|
|
315
|
-
// element,
|
|
316
|
-
// eventKeys: ['onPointerEnter'],
|
|
317
|
-
// intersection,
|
|
318
|
-
// })
|
|
319
|
-
// })
|
|
320
|
-
// // unchanged interactions
|
|
321
|
-
// staying.forEach(({ element, intersection }) => {
|
|
322
|
-
// const eventKeys: Array<Lunch.EventKey> = [
|
|
323
|
-
// 'onPointerOver',
|
|
324
|
-
// 'onPointerMove',
|
|
325
|
-
// ]
|
|
326
|
-
// fireEventsFromIntersections({ element, eventKeys, intersection })
|
|
327
|
-
// })
|
|
328
|
-
// // exited interactions
|
|
329
|
-
// leaving.forEach(({ element, intersection }) => {
|
|
330
|
-
// const eventKeys: Array<Lunch.EventKey> = [
|
|
331
|
-
// 'onPointerLeave',
|
|
332
|
-
// 'onPointerOut',
|
|
333
|
-
// ]
|
|
334
|
-
// fireEventsFromIntersections({ element, eventKeys, intersection })
|
|
335
|
-
// })
|
|
336
|
-
// currentIntersections = ([] as any).concat(entering, staying)
|
|
337
|
-
// }
|
|
338
|
-
// utility function for firing multiple callbacks and multiple events on a Lunchbox.Element
|
|
339
|
-
// const fireEventsFromIntersections = ({
|
|
340
|
-
// element,
|
|
341
|
-
// eventKeys,
|
|
342
|
-
// intersection,
|
|
343
|
-
// }: {
|
|
344
|
-
// element: Lunch.Node
|
|
345
|
-
// eventKeys: Array<Lunch.EventKey>
|
|
346
|
-
// intersection: Intersection<THREE.Object3D>
|
|
347
|
-
// }) => {
|
|
348
|
-
// if (!element) return
|
|
349
|
-
// eventKeys.forEach((eventKey) => {
|
|
350
|
-
// if (element.eventListeners[eventKey]) {
|
|
351
|
-
// element.eventListeners[eventKey].forEach((cb) => {
|
|
352
|
-
// cb({ intersection })
|
|
353
|
-
// })
|
|
354
|
-
// }
|
|
355
|
-
// })
|
|
356
|
-
// }
|
|
357
|
-
|
|
358
127
|
/** Add an event listener to the given node. Also creates the event teardown function and any necessary raycaster/interaction dictionary updates. */
|
|
359
|
-
|
|
360
128
|
function addEventListener({
|
|
361
129
|
node,
|
|
362
130
|
key,
|
|
131
|
+
interactables,
|
|
363
132
|
value
|
|
364
133
|
}) {
|
|
365
134
|
// create new records for this key if needed
|
|
@@ -375,37 +144,18 @@
|
|
|
375
144
|
node.eventListeners[key].push(value); // if we need it, let's get/create the main raycaster
|
|
376
145
|
|
|
377
146
|
if (interactionsRequiringRaycaster.includes(key)) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (key === 'onClick' || key === 'onPointerDown' || key === 'onPointerUp') {
|
|
389
|
-
const stop = vue.watch(() => inputActive.value, isDown => {
|
|
390
|
-
const idx = currentIntersections.map(v => v.element).findIndex(v => v.instance && v.instance.uuid === node.instance?.uuid);
|
|
391
|
-
|
|
392
|
-
if (idx !== -1) {
|
|
393
|
-
if (isDown && (key === 'onClick' || key === 'onPointerDown')) {
|
|
394
|
-
node.eventListeners[key].forEach(func => {
|
|
395
|
-
func({
|
|
396
|
-
intersection: currentIntersections[idx].intersection
|
|
397
|
-
});
|
|
398
|
-
});
|
|
399
|
-
} else if (!isDown && key === 'onPointerUp') {
|
|
400
|
-
node.eventListeners[key].forEach(func => {
|
|
401
|
-
func({
|
|
402
|
-
intersection: currentIntersections[idx].intersection
|
|
403
|
-
});
|
|
404
|
-
});
|
|
147
|
+
if (node.instance && !interactables.value.includes(node)) {
|
|
148
|
+
// add to interactables
|
|
149
|
+
interactables.value.push(node);
|
|
150
|
+
node.eventListenerRemoveFunctions[key].push(() => {
|
|
151
|
+
// remove from interactables
|
|
152
|
+
const idx = interactables.value.indexOf(node);
|
|
153
|
+
|
|
154
|
+
if (idx !== -1) {
|
|
155
|
+
interactables.value.splice(idx, 1);
|
|
405
156
|
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
node.eventListenerRemoveFunctions[key].push(stop);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
409
159
|
}
|
|
410
160
|
|
|
411
161
|
return node;
|
|
@@ -499,6 +249,194 @@
|
|
|
499
249
|
|
|
500
250
|
});
|
|
501
251
|
|
|
252
|
+
const LunchboxEventHandlers = vue.defineComponent({
|
|
253
|
+
name: 'LunchboxEventHandlers',
|
|
254
|
+
|
|
255
|
+
setup() {
|
|
256
|
+
const interactables = useLunchboxInteractables();
|
|
257
|
+
const globals = useGlobals();
|
|
258
|
+
const mousePos = vue.ref({
|
|
259
|
+
x: Infinity,
|
|
260
|
+
y: Infinity
|
|
261
|
+
});
|
|
262
|
+
const inputActive = vue.ref(false);
|
|
263
|
+
let currentIntersections = [];
|
|
264
|
+
const raycaster = new THREE__namespace.Raycaster(new THREE__namespace.Vector3(), new THREE__namespace.Vector3(0, 0, -1));
|
|
265
|
+
|
|
266
|
+
const fireEventsFromIntersections = ({
|
|
267
|
+
element,
|
|
268
|
+
eventKeys,
|
|
269
|
+
intersection
|
|
270
|
+
}) => {
|
|
271
|
+
if (!element) return;
|
|
272
|
+
eventKeys.forEach(eventKey => {
|
|
273
|
+
if (element.eventListeners[eventKey]) {
|
|
274
|
+
element.eventListeners[eventKey].forEach(cb => {
|
|
275
|
+
cb({
|
|
276
|
+
intersection
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}; // add mouse listener to renderer DOM element when the element is ready
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
onRendererReady(v => {
|
|
285
|
+
if (!v?.domElement) return; // we have a DOM element, so let's add mouse listeners
|
|
286
|
+
|
|
287
|
+
const {
|
|
288
|
+
domElement
|
|
289
|
+
} = v;
|
|
290
|
+
|
|
291
|
+
const mouseMoveListener = evt => {
|
|
292
|
+
const screenWidth = (domElement.width ?? 1) / globals.dpr;
|
|
293
|
+
const screenHeight = (domElement.height ?? 1) / globals.dpr;
|
|
294
|
+
mousePos.value.x = evt.offsetX / screenWidth * 2 - 1;
|
|
295
|
+
mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const mouseDownListener = () => inputActive.value = true;
|
|
299
|
+
|
|
300
|
+
const mouseUpListener = () => inputActive.value = false; // add mouse events
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
domElement.addEventListener('pointermove', mouseMoveListener);
|
|
304
|
+
domElement.addEventListener('pointerdown', mouseDownListener);
|
|
305
|
+
domElement.addEventListener('pointerup', mouseUpListener);
|
|
306
|
+
});
|
|
307
|
+
const camera = useCamera();
|
|
308
|
+
|
|
309
|
+
const update = () => {
|
|
310
|
+
const c = camera.value;
|
|
311
|
+
if (!c) return; // console.log(camera.value)
|
|
312
|
+
|
|
313
|
+
raycaster.setFromCamera(mousePos.value, c);
|
|
314
|
+
const intersections = raycaster.intersectObjects(interactables?.value.map(v => v.instance) ?? []);
|
|
315
|
+
let leaveValues = [],
|
|
316
|
+
entering = [],
|
|
317
|
+
staying = []; // intersection arrays
|
|
318
|
+
|
|
319
|
+
leaveValues = currentIntersections.map(v => v.intersection); // element arrays
|
|
320
|
+
|
|
321
|
+
intersections?.forEach(intersection => {
|
|
322
|
+
const currentIdx = currentIntersections.findIndex(v => v.intersection.object === intersection.object);
|
|
323
|
+
|
|
324
|
+
if (currentIdx === -1) {
|
|
325
|
+
const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
|
|
326
|
+
|
|
327
|
+
if (found) {
|
|
328
|
+
entering.push({
|
|
329
|
+
element: found,
|
|
330
|
+
intersection
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
|
|
335
|
+
|
|
336
|
+
if (found) {
|
|
337
|
+
staying.push({
|
|
338
|
+
element: found,
|
|
339
|
+
intersection
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
} // this is a current intersection, so it won't be in our `leave` array
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
const leaveIdx = leaveValues.findIndex(v => v.object.uuid === intersection.object.uuid);
|
|
346
|
+
|
|
347
|
+
if (leaveIdx !== -1) {
|
|
348
|
+
leaveValues.splice(leaveIdx, 1);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
const leaving = leaveValues.map(intersection => {
|
|
352
|
+
return {
|
|
353
|
+
element: interactables?.value.find(interactable => interactable.instance?.uuid === intersection.object.uuid),
|
|
354
|
+
intersection
|
|
355
|
+
};
|
|
356
|
+
}); // new interactions
|
|
357
|
+
|
|
358
|
+
entering.forEach(({
|
|
359
|
+
element,
|
|
360
|
+
intersection
|
|
361
|
+
}) => {
|
|
362
|
+
fireEventsFromIntersections({
|
|
363
|
+
element,
|
|
364
|
+
eventKeys: ['onPointerEnter'],
|
|
365
|
+
intersection
|
|
366
|
+
});
|
|
367
|
+
}); // unchanged interactions
|
|
368
|
+
|
|
369
|
+
staying.forEach(({
|
|
370
|
+
element,
|
|
371
|
+
intersection
|
|
372
|
+
}) => {
|
|
373
|
+
const eventKeys = ['onPointerOver', 'onPointerMove'];
|
|
374
|
+
fireEventsFromIntersections({
|
|
375
|
+
element,
|
|
376
|
+
eventKeys,
|
|
377
|
+
intersection
|
|
378
|
+
});
|
|
379
|
+
}); // exited interactions
|
|
380
|
+
|
|
381
|
+
leaving.forEach(({
|
|
382
|
+
element,
|
|
383
|
+
intersection
|
|
384
|
+
}) => {
|
|
385
|
+
const eventKeys = ['onPointerLeave', 'onPointerOut'];
|
|
386
|
+
fireEventsFromIntersections({
|
|
387
|
+
element,
|
|
388
|
+
eventKeys,
|
|
389
|
+
intersection
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
currentIntersections = [].concat(entering, staying);
|
|
393
|
+
}; // update function
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
onBeforeRender(update);
|
|
397
|
+
|
|
398
|
+
const teardown = () => offBeforeRender(update);
|
|
399
|
+
|
|
400
|
+
vue.onBeforeUnmount(teardown);
|
|
401
|
+
const clickEventKeys = ['onClick', 'onPointerDown', 'onPointerUp'];
|
|
402
|
+
vue.watch(inputActive, isDown => {
|
|
403
|
+
// run raycaster on click (necessary when `update` is not automatically called,
|
|
404
|
+
// for example in `updateSource` functions)
|
|
405
|
+
update(); // meshes with multiple intersections receive multiple callbacks by default -
|
|
406
|
+
// let's make it so they only receive one callback of each type per frame.
|
|
407
|
+
// (ie usually when you click on a mesh, you expect only one click event to fire, even
|
|
408
|
+
// if there are technically multiple intersections with that mesh)
|
|
409
|
+
|
|
410
|
+
const uuidsInteractedWithThisFrame = [];
|
|
411
|
+
currentIntersections.forEach(v => {
|
|
412
|
+
clickEventKeys.forEach(key => {
|
|
413
|
+
const id = v.element.uuid + key;
|
|
414
|
+
|
|
415
|
+
if (isDown && (key === 'onClick' || key === 'onPointerDown')) {
|
|
416
|
+
if (!uuidsInteractedWithThisFrame.includes(id)) {
|
|
417
|
+
v.element.eventListeners[key]?.forEach(cb => cb({
|
|
418
|
+
intersection: v.intersection
|
|
419
|
+
}));
|
|
420
|
+
uuidsInteractedWithThisFrame.push(id);
|
|
421
|
+
}
|
|
422
|
+
} else if (!isDown && key === 'onPointerUp') {
|
|
423
|
+
if (!uuidsInteractedWithThisFrame.includes(id)) {
|
|
424
|
+
v.element.eventListeners[key]?.forEach(cb => cb({
|
|
425
|
+
intersection: v.intersection
|
|
426
|
+
}));
|
|
427
|
+
uuidsInteractedWithThisFrame.push(id);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
}); // return arbitrary object to ensure instantiation
|
|
433
|
+
// TODO: why can't we return a <raycaster/> here?
|
|
434
|
+
|
|
435
|
+
return () => vue.createVNode(vue.resolveComponent("object3D"), null, null);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
});
|
|
439
|
+
|
|
502
440
|
/** fixed & fill styling for container */
|
|
503
441
|
|
|
504
442
|
const fillStyle = position => {
|
|
@@ -551,9 +489,10 @@
|
|
|
551
489
|
|
|
552
490
|
if (props.r3f && THREE__namespace?.ColorManagement) {
|
|
553
491
|
THREE__namespace.ColorManagement.legacyMode = false;
|
|
554
|
-
}
|
|
555
|
-
// ====================
|
|
492
|
+
}
|
|
556
493
|
|
|
494
|
+
const interactables = useLunchboxInteractables(); // MOUNT
|
|
495
|
+
// ====================
|
|
557
496
|
|
|
558
497
|
vue.onMounted(async () => {
|
|
559
498
|
// canvas needs to exist (or user needs to handle it on their own)
|
|
@@ -589,23 +528,17 @@
|
|
|
589
528
|
updateGlobals?.({
|
|
590
529
|
dpr
|
|
591
530
|
});
|
|
592
|
-
console.log(1);
|
|
593
531
|
|
|
594
532
|
while (!renderer.value?.$el?.instance && // TODO: remove `as any`
|
|
595
533
|
!renderer.value?.component?.ctx.$el?.instance) {
|
|
596
|
-
console.log(2);
|
|
597
534
|
await new Promise(r => requestAnimationFrame(r));
|
|
598
535
|
}
|
|
599
536
|
|
|
600
|
-
console.log(3);
|
|
601
|
-
|
|
602
537
|
while (!scene.value?.$el?.instance && // TODO: remove `as any`
|
|
603
538
|
!scene.value?.component?.ctx.$el?.instance) {
|
|
604
|
-
console.log(4);
|
|
605
539
|
await new Promise(r => requestAnimationFrame(r));
|
|
606
540
|
}
|
|
607
541
|
|
|
608
|
-
console.log(5);
|
|
609
542
|
const normalizedRenderer = renderer.value?.$el?.instance ?? renderer.value?.component?.ctx.$el?.instance;
|
|
610
543
|
normalizedRenderer.setPixelRatio(globals.dpr);
|
|
611
544
|
const normalizedScene = scene.value?.$el?.instance ?? scene.value?.component?.ctx.$el?.instance;
|
|
@@ -675,7 +608,37 @@
|
|
|
675
608
|
// ====================
|
|
676
609
|
|
|
677
610
|
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
678
|
-
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
611
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed'; // REACTIVE CUSTOM CAMERAS
|
|
612
|
+
// ====================
|
|
613
|
+
// find first camera with `type.name` property
|
|
614
|
+
// (which indicates a Lunch.Node)
|
|
615
|
+
|
|
616
|
+
const activeCamera = vue.computed(() => {
|
|
617
|
+
const output = context.slots?.camera?.().find(c => c.type?.name);
|
|
618
|
+
|
|
619
|
+
if (output) {
|
|
620
|
+
return output;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return output;
|
|
624
|
+
}); // TODO: make custom cameras reactive
|
|
625
|
+
|
|
626
|
+
vue.watch(activeCamera, async (newVal, oldVal) => {
|
|
627
|
+
// console.log('got camera', newVal)
|
|
628
|
+
if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
|
|
629
|
+
// TODO: remove cast
|
|
630
|
+
camera.value = newVal; // TODO: why isn't this updating app camera?
|
|
631
|
+
// const el = await waitFor(() => newVal.el)
|
|
632
|
+
// console.log(el)
|
|
633
|
+
// camera.value = el
|
|
634
|
+
// console.log(newVal.uuid)
|
|
635
|
+
// updateGlobals?.({ camera: el })
|
|
636
|
+
}
|
|
637
|
+
}, {
|
|
638
|
+
immediate: true
|
|
639
|
+
}); // RENDER FUNCTION
|
|
640
|
+
// ====================
|
|
641
|
+
|
|
679
642
|
return () => vue.createVNode(vue.Fragment, null, [context.slots?.renderer?.()?.length ? // TODO: remove `as any` cast
|
|
680
643
|
renderer.value = context.slots?.renderer?.()[0] : // ...otherwise, add canvas...
|
|
681
644
|
vue.createVNode(vue.Fragment, null, [vue.createVNode("div", {
|
|
@@ -705,13 +668,13 @@
|
|
|
705
668
|
}, {
|
|
706
669
|
default: () => [context.slots?.default?.()]
|
|
707
670
|
}), context.slots?.camera?.()?.length ? // TODO: remove `any` cast
|
|
708
|
-
camera.value
|
|
671
|
+
camera.value : props.ortho || props.orthographic ? vue.createVNode(vue.resolveComponent("orthographicCamera"), vue.mergeProps({
|
|
709
672
|
"ref": camera,
|
|
710
673
|
"args": props.cameraArgs ?? []
|
|
711
674
|
}, consolidatedCameraProperties), null) : vue.createVNode(vue.resolveComponent("perspectiveCamera"), vue.mergeProps({
|
|
712
675
|
"ref": camera,
|
|
713
676
|
"args": props.cameraArgs ?? [props.r3f ? 75 : 45, 0.5625, 1, 1000]
|
|
714
|
-
}, consolidatedCameraProperties), null)]);
|
|
677
|
+
}, consolidatedCameraProperties), null), interactables?.value.length && vue.createVNode(LunchboxEventHandlers, null, null)]);
|
|
715
678
|
}
|
|
716
679
|
|
|
717
680
|
});
|
|
@@ -726,7 +689,7 @@
|
|
|
726
689
|
'light', 'spotLightShadow', 'spotLight', 'pointLight', 'rectAreaLight', 'hemisphereLight', 'directionalLightShadow', 'directionalLight', 'ambientLight', 'lightShadow', 'ambientLightProbe', 'hemisphereLightProbe', 'lightProbe', // textures
|
|
727
690
|
'texture', 'videoTexture', 'dataTexture', 'dataTexture3D', 'compressedTexture', 'cubeTexture', 'canvasTexture', 'depthTexture', // Texture loaders
|
|
728
691
|
'textureLoader', // misc
|
|
729
|
-
'group', 'catmullRomCurve3', 'points', // helpers
|
|
692
|
+
'group', 'catmullRomCurve3', 'points', 'raycaster', // helpers
|
|
730
693
|
'cameraHelper', // cameras
|
|
731
694
|
'camera', 'perspectiveCamera', 'orthographicCamera', 'cubeCamera', 'arrayCamera', // renderers
|
|
732
695
|
'webGLRenderer'
|
|
@@ -761,7 +724,6 @@
|
|
|
761
724
|
arrowHelper: ArrowHelperProps
|
|
762
725
|
axesHelper: AxesHelperProps
|
|
763
726
|
// misc
|
|
764
|
-
raycaster: RaycasterProps
|
|
765
727
|
vector2: Vector2Props
|
|
766
728
|
vector3: Vector3Props
|
|
767
729
|
vector4: Vector4Props
|
|
@@ -778,7 +740,7 @@
|
|
|
778
740
|
*/
|
|
779
741
|
];
|
|
780
742
|
|
|
781
|
-
const catalogue = {};
|
|
743
|
+
const catalogue = {}; // component creation utility
|
|
782
744
|
|
|
783
745
|
const createComponent$1 = tag => vue.defineComponent({
|
|
784
746
|
inheritAttrs: false,
|
|
@@ -1174,9 +1136,25 @@
|
|
|
1174
1136
|
return item?.minidomType === 'RendererNode';
|
|
1175
1137
|
}
|
|
1176
1138
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1139
|
+
const globalsInjectionKey = Symbol();
|
|
1140
|
+
const updateGlobalsInjectionKey = Symbol();
|
|
1141
|
+
const setCustomRenderKey = Symbol();
|
|
1142
|
+
const clearCustomRenderKey = Symbol();
|
|
1143
|
+
const beforeRenderKey = Symbol();
|
|
1144
|
+
const onBeforeRenderKey = Symbol();
|
|
1145
|
+
const offBeforeRenderKey = Symbol();
|
|
1146
|
+
const afterRenderKey = Symbol();
|
|
1147
|
+
const onAfterRenderKey = Symbol();
|
|
1148
|
+
const offAfterRenderKey = Symbol();
|
|
1149
|
+
const frameIdKey = Symbol();
|
|
1150
|
+
const watchStopHandleKey = Symbol();
|
|
1151
|
+
const appRootNodeKey = Symbol();
|
|
1152
|
+
const appKey = Symbol();
|
|
1153
|
+
const appRenderersKey = Symbol();
|
|
1154
|
+
const appSceneKey = Symbol();
|
|
1155
|
+
const appCameraKey = Symbol();
|
|
1156
|
+
const lunchboxInteractables = Symbol();
|
|
1157
|
+
const startCallbackKey = Symbol();
|
|
1180
1158
|
|
|
1181
1159
|
const requestUpdate = opts => {
|
|
1182
1160
|
if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
|
|
@@ -1211,20 +1189,19 @@
|
|
|
1211
1189
|
const {
|
|
1212
1190
|
app,
|
|
1213
1191
|
renderer,
|
|
1214
|
-
scene
|
|
1215
|
-
camera
|
|
1192
|
+
scene
|
|
1216
1193
|
} = opts; // BEFORE RENDER
|
|
1217
1194
|
|
|
1218
1195
|
app.config.globalProperties.lunchbox.beforeRender.forEach(cb => {
|
|
1219
1196
|
cb?.(opts);
|
|
1220
1197
|
}); // RENDER
|
|
1221
1198
|
|
|
1222
|
-
if (renderer && scene && camera) {
|
|
1199
|
+
if (renderer && scene && opts.app.config.globalProperties.lunchbox.camera) {
|
|
1223
1200
|
if (app.customRender) {
|
|
1224
1201
|
app.customRender(opts);
|
|
1225
1202
|
} else {
|
|
1226
|
-
renderer.render(vue.toRaw(scene),
|
|
1227
|
-
|
|
1203
|
+
renderer.render(vue.toRaw(scene), opts.app.config.globalProperties.lunchbox.camera // toRaw(camera)
|
|
1204
|
+
);
|
|
1228
1205
|
}
|
|
1229
1206
|
} // AFTER RENDER
|
|
1230
1207
|
|
|
@@ -1234,97 +1211,92 @@
|
|
|
1234
1211
|
});
|
|
1235
1212
|
}; // before render
|
|
1236
1213
|
// ====================
|
|
1237
|
-
|
|
1214
|
+
|
|
1215
|
+
/** Obtain callback methods for `onBeforeRender` and `offBeforeRender`. Usually used internally by Lunchbox. */
|
|
1238
1216
|
|
|
1239
1217
|
const useBeforeRender = () => {
|
|
1240
1218
|
return {
|
|
1241
1219
|
onBeforeRender: vue.inject(onBeforeRenderKey),
|
|
1242
1220
|
offBeforeRender: vue.inject(offBeforeRenderKey)
|
|
1243
1221
|
};
|
|
1244
|
-
};
|
|
1222
|
+
};
|
|
1223
|
+
/** Run a function before every render.
|
|
1224
|
+
*
|
|
1225
|
+
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1226
|
+
* before a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1227
|
+
*/
|
|
1245
1228
|
|
|
1246
1229
|
const onBeforeRender = (cb, index = Infinity) => {
|
|
1247
1230
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1248
|
-
};
|
|
1231
|
+
};
|
|
1232
|
+
/** Remove a function from the `beforeRender` callback list. Useful for tearing down functions added
|
|
1233
|
+
* by `onBeforeRender`.
|
|
1234
|
+
*/
|
|
1249
1235
|
|
|
1250
1236
|
const offBeforeRender = cb => {
|
|
1251
1237
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1252
1238
|
}; // after render
|
|
1253
1239
|
// ====================
|
|
1254
|
-
|
|
1240
|
+
|
|
1241
|
+
/** Obtain callback methods for `onAfterRender` and `offAfterRender`. Usually used internally by Lunchbox. */
|
|
1255
1242
|
|
|
1256
1243
|
const useAfterRender = () => {
|
|
1257
1244
|
return {
|
|
1258
1245
|
onAfterRender: vue.inject(onBeforeRenderKey),
|
|
1259
1246
|
offAfterRender: vue.inject(offBeforeRenderKey)
|
|
1260
1247
|
};
|
|
1261
|
-
};
|
|
1248
|
+
};
|
|
1249
|
+
/** Run a function after every render.
|
|
1250
|
+
*
|
|
1251
|
+
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1252
|
+
* after a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1253
|
+
*/
|
|
1262
1254
|
|
|
1263
1255
|
const onAfterRender = (cb, index = Infinity) => {
|
|
1264
1256
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1265
|
-
};
|
|
1257
|
+
};
|
|
1258
|
+
/** Remove a function from the `afterRender` callback list. Useful for tearing down functions added
|
|
1259
|
+
* by `onAfterRender`.
|
|
1260
|
+
*/
|
|
1266
1261
|
|
|
1267
1262
|
const offAfterRender = cb => {
|
|
1268
1263
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
// afterRender.splice(index, 0, cb)
|
|
1274
|
-
// }
|
|
1275
|
-
// }
|
|
1276
|
-
// export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
|
|
1277
|
-
// if (isFinite(cb as number)) {
|
|
1278
|
-
// afterRender.splice(cb as number, 1)
|
|
1279
|
-
// } else {
|
|
1280
|
-
// const idx = afterRender.findIndex((v) => v == cb)
|
|
1281
|
-
// afterRender.splice(idx, 1)
|
|
1282
|
-
// }
|
|
1283
|
-
// }
|
|
1284
|
-
// TODO: document
|
|
1264
|
+
};
|
|
1265
|
+
/** Obtain a function used to cancel the current update frame. Use `cancelUpdate` if you wish
|
|
1266
|
+
* to immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1267
|
+
*/
|
|
1285
1268
|
|
|
1286
1269
|
const useCancelUpdate = () => {
|
|
1287
1270
|
const frameId = vue.inject(frameIdKey);
|
|
1288
1271
|
return () => {
|
|
1289
1272
|
if (frameId !== undefined) cancelAnimationFrame(frameId);
|
|
1290
1273
|
};
|
|
1291
|
-
};
|
|
1274
|
+
};
|
|
1275
|
+
/** Cancel the current update frame. Usually used internally by Lunchbox. */
|
|
1292
1276
|
|
|
1293
1277
|
const cancelUpdate = () => {
|
|
1294
1278
|
useCancelUpdate()?.();
|
|
1295
|
-
};
|
|
1279
|
+
};
|
|
1280
|
+
/** Obtain a function used to cancel an update source. Use `cancelUpdateSource` if you wish to
|
|
1281
|
+
* immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1282
|
+
*/
|
|
1296
1283
|
|
|
1297
1284
|
const useCancelUpdateSource = () => {
|
|
1298
1285
|
const cancel = vue.inject(watchStopHandleKey);
|
|
1299
1286
|
return () => cancel?.();
|
|
1300
|
-
};
|
|
1287
|
+
};
|
|
1288
|
+
/** Cancel an update source. Usually used internally by Lunchbox. */
|
|
1301
1289
|
|
|
1302
1290
|
const cancelUpdateSource = () => {
|
|
1303
1291
|
useCancelUpdateSource()?.();
|
|
1304
1292
|
};
|
|
1305
1293
|
|
|
1306
|
-
/** Update the given node so all of its props are current. */
|
|
1307
|
-
|
|
1308
|
-
function updateAllObjectProps({
|
|
1309
|
-
node
|
|
1310
|
-
}) {
|
|
1311
|
-
// set props
|
|
1312
|
-
const props = node.props || {};
|
|
1313
|
-
let output = node;
|
|
1314
|
-
Object.keys(props).forEach(key => {
|
|
1315
|
-
output = updateObjectProp({
|
|
1316
|
-
node,
|
|
1317
|
-
key,
|
|
1318
|
-
value: props[key]
|
|
1319
|
-
});
|
|
1320
|
-
});
|
|
1321
|
-
return output;
|
|
1322
|
-
}
|
|
1323
1294
|
/** Update a single prop on a given node. */
|
|
1324
1295
|
|
|
1325
1296
|
function updateObjectProp({
|
|
1326
1297
|
node,
|
|
1327
1298
|
key,
|
|
1299
|
+
interactables,
|
|
1328
1300
|
value
|
|
1329
1301
|
}) {
|
|
1330
1302
|
// handle and return early if prop is an event
|
|
@@ -1333,6 +1305,7 @@
|
|
|
1333
1305
|
return addEventListener({
|
|
1334
1306
|
node,
|
|
1335
1307
|
key,
|
|
1308
|
+
interactables,
|
|
1336
1309
|
value
|
|
1337
1310
|
});
|
|
1338
1311
|
} // update THREE property
|
|
@@ -1363,6 +1336,7 @@
|
|
|
1363
1336
|
const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
|
|
1364
1337
|
liveProperty = liveProperty = lodash.get(target, fullPath);
|
|
1365
1338
|
} // change property
|
|
1339
|
+
// first, save as array in case we need to spread it
|
|
1366
1340
|
|
|
1367
1341
|
|
|
1368
1342
|
if (liveProperty && lodash.isNumber(value) && liveProperty.setScalar) {
|
|
@@ -1373,14 +1347,24 @@
|
|
|
1373
1347
|
const nextValueAsArray = Array.isArray(value) ? value : [value];
|
|
1374
1348
|
target[finalKey].set(...nextValueAsArray);
|
|
1375
1349
|
} else if (typeof liveProperty === 'function') {
|
|
1376
|
-
//
|
|
1377
|
-
|
|
1350
|
+
// some function properties are set rather than called, so let's handle them
|
|
1351
|
+
if (finalKey.toLowerCase() === 'onbeforerender' || finalKey.toLowerCase() === 'onafterrender') {
|
|
1352
|
+
target[finalKey] = value;
|
|
1353
|
+
} else {
|
|
1354
|
+
if (!Array.isArray(value)) {
|
|
1355
|
+
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" />');
|
|
1356
|
+
} // if property is a function, let's try calling it
|
|
1357
|
+
|
|
1358
|
+
|
|
1359
|
+
liveProperty.bind(node.instance)(...value);
|
|
1360
|
+
} // pass the result to the parent
|
|
1378
1361
|
// const parent = node.parentNode
|
|
1379
1362
|
// if (parent) {
|
|
1380
1363
|
// const parentAsLunchboxNode = parent as Lunchbox.Node
|
|
1381
1364
|
// parentAsLunchboxNode.attached[finalKey] = result
|
|
1382
1365
|
// ; (parentAsLunchboxNode.instance as any)[finalKey] = result
|
|
1383
1366
|
// }
|
|
1367
|
+
|
|
1384
1368
|
} else if (lodash.get(target, finalKey, undefined) !== undefined) {
|
|
1385
1369
|
// blank strings evaluate to `true`
|
|
1386
1370
|
// <mesh castShadow receiveShadow /> will work the same as
|
|
@@ -1588,101 +1572,133 @@
|
|
|
1588
1572
|
Elements are `create`d from the outside in, then `insert`ed from the inside out.
|
|
1589
1573
|
*/
|
|
1590
1574
|
|
|
1591
|
-
const
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
createComment(text) {
|
|
1601
|
-
return createCommentNode({
|
|
1602
|
-
text
|
|
1603
|
-
});
|
|
1604
|
-
},
|
|
1605
|
-
|
|
1606
|
-
insert,
|
|
1575
|
+
const createNodeOps = () => {
|
|
1576
|
+
// APP-LEVEL GLOBALS
|
|
1577
|
+
// ====================
|
|
1578
|
+
// These need to exist at the app level in a place where the node ops can access them.
|
|
1579
|
+
// It'd be better to set these via `app.provide` at app creation, but the node ops need access
|
|
1580
|
+
// to these values before the app is instantiated, so this is the next-best place for them to exist.
|
|
1581
|
+
const interactables = vue.ref([]); // NODE OPS
|
|
1582
|
+
// ====================
|
|
1607
1583
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1584
|
+
const nodeOps = {
|
|
1585
|
+
createElement,
|
|
1610
1586
|
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const result = node.parentNode;
|
|
1617
|
-
if (!result) return null;
|
|
1618
|
-
return result;
|
|
1619
|
-
},
|
|
1587
|
+
createText(text) {
|
|
1588
|
+
return createTextNode({
|
|
1589
|
+
text
|
|
1590
|
+
});
|
|
1591
|
+
},
|
|
1620
1592
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1593
|
+
createComment(text) {
|
|
1594
|
+
return createCommentNode({
|
|
1595
|
+
text
|
|
1596
|
+
});
|
|
1597
|
+
},
|
|
1598
|
+
|
|
1599
|
+
insert,
|
|
1600
|
+
|
|
1601
|
+
nextSibling(node) {
|
|
1602
|
+
const result = node.nextSibling;
|
|
1603
|
+
if (!result) return null;
|
|
1604
|
+
return result;
|
|
1605
|
+
},
|
|
1606
|
+
|
|
1607
|
+
parentNode(node) {
|
|
1608
|
+
const result = node.parentNode;
|
|
1609
|
+
if (!result) return null;
|
|
1610
|
+
return result;
|
|
1611
|
+
},
|
|
1612
|
+
|
|
1613
|
+
patchProp(node, key, prevValue, nextValue) {
|
|
1614
|
+
if (isLunchboxDomComponent(node)) {
|
|
1615
|
+
// handle DOM node
|
|
1616
|
+
if (key === 'style') {
|
|
1617
|
+
// special handling for style
|
|
1618
|
+
Object.keys(nextValue).forEach(k => {
|
|
1619
|
+
node.domElement.style[k] = nextValue[k];
|
|
1620
|
+
});
|
|
1621
|
+
} else {
|
|
1622
|
+
node.domElement.setAttribute(key, nextValue);
|
|
1623
|
+
}
|
|
1632
1624
|
|
|
1633
|
-
|
|
1634
|
-
|
|
1625
|
+
return;
|
|
1626
|
+
} // ignore if root node, or Lunchbox internal prop
|
|
1635
1627
|
|
|
1636
1628
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1629
|
+
if (isLunchboxRootNode(node) || key.startsWith('$')) {
|
|
1630
|
+
return;
|
|
1631
|
+
} // otherwise, update prop
|
|
1640
1632
|
|
|
1641
1633
|
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1634
|
+
updateObjectProp({
|
|
1635
|
+
node: node,
|
|
1636
|
+
key,
|
|
1637
|
+
interactables,
|
|
1638
|
+
value: nextValue
|
|
1639
|
+
});
|
|
1640
|
+
},
|
|
1648
1641
|
|
|
1649
|
-
|
|
1642
|
+
remove,
|
|
1650
1643
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1644
|
+
setElementText() {// noop
|
|
1645
|
+
},
|
|
1653
1646
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1647
|
+
setText() {// noop
|
|
1648
|
+
}
|
|
1656
1649
|
|
|
1650
|
+
};
|
|
1651
|
+
return {
|
|
1652
|
+
nodeOps,
|
|
1653
|
+
interactables
|
|
1654
|
+
};
|
|
1657
1655
|
};
|
|
1658
1656
|
|
|
1659
|
-
/** The current camera
|
|
1660
|
-
// TODO: update docs
|
|
1657
|
+
/** The current camera as a computed value. */
|
|
1661
1658
|
|
|
1662
|
-
const
|
|
1659
|
+
const useCamera = () => vue.inject(appCameraKey);
|
|
1660
|
+
/** Run a function using the current camera when it's present. */
|
|
1663
1661
|
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1662
|
+
const onCameraReady = cb => {
|
|
1663
|
+
const stopWatch = vue.watch(useCamera(), newVal => {
|
|
1664
|
+
if (newVal) {
|
|
1665
|
+
cb(newVal);
|
|
1666
|
+
stopWatch();
|
|
1667
|
+
}
|
|
1668
|
+
}, {
|
|
1669
|
+
immediate: true
|
|
1670
|
+
});
|
|
1671
|
+
};
|
|
1672
|
+
/** The current renderer as a computed value. */
|
|
1666
1673
|
|
|
1667
|
-
const
|
|
1674
|
+
const useRenderer = () => vue.inject(appRenderersKey);
|
|
1668
1675
|
/** Run a function using the current renderer when it's present. */
|
|
1669
1676
|
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
|
|
1677
|
+
const onRendererReady = cb => {
|
|
1678
|
+
const stopWatch = vue.watch(useRenderer(), newVal => {
|
|
1679
|
+
if (newVal) {
|
|
1680
|
+
cb(newVal);
|
|
1681
|
+
stopWatch();
|
|
1682
|
+
}
|
|
1683
|
+
}, {
|
|
1684
|
+
immediate: true
|
|
1685
|
+
});
|
|
1686
|
+
};
|
|
1687
|
+
/** The current scene as a computed value. */
|
|
1673
1688
|
|
|
1674
|
-
const
|
|
1689
|
+
const useScene = () => vue.inject(appSceneKey);
|
|
1675
1690
|
/** Run a function using the current scene when it's present. */
|
|
1676
|
-
// TODO: update docs
|
|
1677
1691
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
if (
|
|
1681
|
-
|
|
1692
|
+
const onSceneReady = cb => {
|
|
1693
|
+
const stopWatch = vue.watch(useScene(), newVal => {
|
|
1694
|
+
if (newVal) {
|
|
1695
|
+
cb(newVal);
|
|
1696
|
+
stopWatch();
|
|
1697
|
+
}
|
|
1682
1698
|
}, {
|
|
1683
1699
|
immediate: true
|
|
1684
1700
|
});
|
|
1685
|
-
} // CUSTOM RENDER SUPPORT
|
|
1701
|
+
}; // CUSTOM RENDER SUPPORT
|
|
1686
1702
|
// ====================
|
|
1687
1703
|
|
|
1688
1704
|
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
@@ -1739,14 +1755,15 @@
|
|
|
1739
1755
|
|
|
1740
1756
|
const updateGlobals = newValue => {
|
|
1741
1757
|
useUpdateGlobals()?.(newValue);
|
|
1742
|
-
};
|
|
1743
|
-
|
|
1744
|
-
const useRootNode = () => vue.inject(appRootNodeKey); // TODO: document
|
|
1758
|
+
};
|
|
1759
|
+
/** Use the current Lunchbox app. Usually used internally by Lunchbox. */
|
|
1745
1760
|
|
|
1746
|
-
const useApp = () => vue.inject(appKey);
|
|
1761
|
+
const useApp = () => vue.inject(appKey);
|
|
1762
|
+
/** Obtain a list of the start callback functions. Usually used internally by Lunchbox. */
|
|
1747
1763
|
|
|
1748
|
-
const useStartCallbacks = () => vue.inject(startCallbackKey);
|
|
1749
|
-
|
|
1764
|
+
const useStartCallbacks = () => vue.inject(startCallbackKey);
|
|
1765
|
+
/** Run a given callback once when the Lunchbox app starts. Include an index to
|
|
1766
|
+
* splice the callback at that index in the callback queue. */
|
|
1750
1767
|
|
|
1751
1768
|
const onStart = (cb, index = Infinity) => {
|
|
1752
1769
|
const callbacks = useStartCallbacks();
|
|
@@ -1756,11 +1773,21 @@
|
|
|
1756
1773
|
} else {
|
|
1757
1774
|
callbacks?.splice(index, 0, cb);
|
|
1758
1775
|
}
|
|
1759
|
-
};
|
|
1776
|
+
};
|
|
1777
|
+
/** Obtain a list of interactable objects (registered via onClick, onHover, etc events). Usually used internally by Lunchbox. */
|
|
1778
|
+
|
|
1779
|
+
const useLunchboxInteractables = () => vue.inject(lunchboxInteractables); // CREATE APP
|
|
1760
1780
|
// ====================
|
|
1761
1781
|
|
|
1762
1782
|
const createApp = root => {
|
|
1763
|
-
const
|
|
1783
|
+
const {
|
|
1784
|
+
nodeOps,
|
|
1785
|
+
interactables
|
|
1786
|
+
} = createNodeOps();
|
|
1787
|
+
const app = vue.createRenderer(nodeOps).createApp(root); // provide Lunchbox interaction handlers flag (modified when user references events via
|
|
1788
|
+
// @click, etc)
|
|
1789
|
+
|
|
1790
|
+
app.provide(lunchboxInteractables, interactables); // register all components
|
|
1764
1791
|
// ====================
|
|
1765
1792
|
|
|
1766
1793
|
Object.keys(components).forEach(key => {
|
|
@@ -1911,7 +1938,6 @@
|
|
|
1911
1938
|
};
|
|
1912
1939
|
|
|
1913
1940
|
exports.addEventListener = addEventListener;
|
|
1914
|
-
exports.addInteractable = addInteractable;
|
|
1915
1941
|
exports.afterRenderKey = afterRenderKey;
|
|
1916
1942
|
exports.appCameraKey = appCameraKey;
|
|
1917
1943
|
exports.appKey = appKey;
|
|
@@ -1919,7 +1945,6 @@
|
|
|
1919
1945
|
exports.appRootNodeKey = appRootNodeKey;
|
|
1920
1946
|
exports.appSceneKey = appSceneKey;
|
|
1921
1947
|
exports.beforeRenderKey = beforeRenderKey;
|
|
1922
|
-
exports.camera = camera;
|
|
1923
1948
|
exports.cancelUpdate = cancelUpdate;
|
|
1924
1949
|
exports.cancelUpdateSource = cancelUpdateSource;
|
|
1925
1950
|
exports.clearCustomRender = clearCustomRender;
|
|
@@ -1929,19 +1954,13 @@
|
|
|
1929
1954
|
exports.createDomNode = createDomNode;
|
|
1930
1955
|
exports.createNode = createNode;
|
|
1931
1956
|
exports.createTextNode = createTextNode;
|
|
1932
|
-
exports.currentIntersections = currentIntersections;
|
|
1933
|
-
exports.ensureRenderer = ensureRenderer;
|
|
1934
|
-
exports.ensuredCamera = ensuredCamera;
|
|
1935
|
-
exports.ensuredScene = ensuredScene;
|
|
1936
1957
|
exports.extend = extend;
|
|
1937
1958
|
exports.find = find;
|
|
1938
1959
|
exports.frameIdKey = frameIdKey;
|
|
1939
1960
|
exports.globalsInjectionKey = globalsInjectionKey;
|
|
1940
|
-
exports.inputActive = inputActive;
|
|
1941
1961
|
exports.instantiateThreeObject = instantiateThreeObject;
|
|
1942
|
-
exports.interactables = interactables;
|
|
1943
1962
|
exports.isMinidomNode = isMinidomNode;
|
|
1944
|
-
exports.
|
|
1963
|
+
exports.lunchboxInteractables = lunchboxInteractables;
|
|
1945
1964
|
exports.nestedPropertiesToCheck = nestedPropertiesToCheck;
|
|
1946
1965
|
exports.offAfterRender = offAfterRender;
|
|
1947
1966
|
exports.offAfterRenderKey = offAfterRenderKey;
|
|
@@ -1951,15 +1970,14 @@
|
|
|
1951
1970
|
exports.onAfterRenderKey = onAfterRenderKey;
|
|
1952
1971
|
exports.onBeforeRender = onBeforeRender;
|
|
1953
1972
|
exports.onBeforeRenderKey = onBeforeRenderKey;
|
|
1973
|
+
exports.onCameraReady = onCameraReady;
|
|
1974
|
+
exports.onRendererReady = onRendererReady;
|
|
1975
|
+
exports.onSceneReady = onSceneReady;
|
|
1954
1976
|
exports.onStart = onStart;
|
|
1955
|
-
exports.removeInteractable = removeInteractable;
|
|
1956
|
-
exports.renderer = renderer;
|
|
1957
|
-
exports.scene = scene;
|
|
1958
1977
|
exports.setCustomRender = setCustomRender;
|
|
1959
1978
|
exports.setCustomRenderKey = setCustomRenderKey;
|
|
1960
1979
|
exports.startCallbackKey = startCallbackKey;
|
|
1961
1980
|
exports.update = update;
|
|
1962
|
-
exports.updateAllObjectProps = updateAllObjectProps;
|
|
1963
1981
|
exports.updateGlobals = updateGlobals;
|
|
1964
1982
|
exports.updateGlobalsInjectionKey = updateGlobalsInjectionKey;
|
|
1965
1983
|
exports.updateObjectProp = updateObjectProp;
|
|
@@ -1971,8 +1989,8 @@
|
|
|
1971
1989
|
exports.useCancelUpdateSource = useCancelUpdateSource;
|
|
1972
1990
|
exports.useCustomRender = useCustomRender;
|
|
1973
1991
|
exports.useGlobals = useGlobals;
|
|
1992
|
+
exports.useLunchboxInteractables = useLunchboxInteractables;
|
|
1974
1993
|
exports.useRenderer = useRenderer;
|
|
1975
|
-
exports.useRootNode = useRootNode;
|
|
1976
1994
|
exports.useScene = useScene;
|
|
1977
1995
|
exports.useStartCallbacks = useStartCallbacks;
|
|
1978
1996
|
exports.useUpdateGlobals = useUpdateGlobals;
|