lunchboxjs 0.1.4013 → 0.1.4016

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.
@@ -1,6 +1,5 @@
1
1
  import { toRaw, ref, onMounted, getCurrentInstance, onBeforeUnmount, h, defineComponent, isRef, isVNode, watch, reactive, computed, createRenderer } from 'vue';
2
2
  import * as THREE from 'three';
3
- import { Color } from 'three';
4
3
  import { set, get, isNumber } from 'lodash';
5
4
 
6
5
  // this needs to be in a separate file to ensure it's created immediately
@@ -66,6 +65,8 @@ const prepCanvas = (container, canvasElement, onBeforeUnmount) => {
66
65
  });
67
66
  };
68
67
 
68
+ // TODO:
69
+ // Continue r3f prop - what else (besides camera fov) makes r3f look good?
69
70
  /** fixed & fill styling for container */
70
71
  const fillStyle = (position) => {
71
72
  return {
@@ -90,11 +91,13 @@ const LunchboxWrapper = {
90
91
  dpr: Number,
91
92
  ortho: Boolean,
92
93
  orthographic: Boolean,
94
+ r3f: Boolean,
93
95
  rendererArguments: Object,
94
96
  rendererProperties: Object,
95
97
  shadow: [Boolean, Object],
96
98
  transparent: Boolean,
97
99
  zoom: Number,
100
+ updateSource: Object,
98
101
  },
99
102
  setup(props, context) {
100
103
  const canvas = ref();
@@ -104,6 +107,10 @@ const LunchboxWrapper = {
104
107
  let renderer;
105
108
  let camera;
106
109
  let scene;
110
+ // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
111
+ if (props.r3f && THREE?.ColorManagement) {
112
+ THREE.ColorManagement.legacyMode = false;
113
+ }
107
114
  // MOUNT
108
115
  // ====================
109
116
  onMounted(() => {
@@ -124,6 +131,9 @@ const LunchboxWrapper = {
124
131
  alpha: props.transparent,
125
132
  antialias: true,
126
133
  canvas: canvas.value.domElement,
134
+ powerPreference: !!props.r3f
135
+ ? 'high-performance'
136
+ : 'default',
127
137
  ...(props.rendererArguments ?? {}),
128
138
  };
129
139
  // create new renderer
@@ -137,6 +147,15 @@ const LunchboxWrapper = {
137
147
  // we've initialized the renderer, so anything depending on it can execute now
138
148
  rendererReady.value = true;
139
149
  const rendererAsWebGlRenderer = ensureRenderer;
150
+ // apply r3f settings if desired
151
+ if (props.r3f) {
152
+ if (rendererAsWebGlRenderer.value.instance) {
153
+ rendererAsWebGlRenderer.value.instance.outputEncoding =
154
+ THREE.sRGBEncoding;
155
+ rendererAsWebGlRenderer.value.instance.toneMapping =
156
+ THREE.ACESFilmicToneMapping;
157
+ }
158
+ }
140
159
  // update render sugar
141
160
  const sugar = {
142
161
  shadow: props.shadow,
@@ -184,7 +203,12 @@ const LunchboxWrapper = {
184
203
  else {
185
204
  ensuredCamera.value = createNode({
186
205
  props: {
187
- args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
206
+ args: props.cameraArgs ?? [
207
+ props.r3f ? 75 : 45,
208
+ 0.5625,
209
+ 1,
210
+ 1000,
211
+ ],
188
212
  },
189
213
  type: 'PerspectiveCamera',
190
214
  uuid: fallbackCameraUuid,
@@ -217,7 +241,7 @@ const LunchboxWrapper = {
217
241
  scene = ensuredScene.value;
218
242
  // set background color
219
243
  if (scene && scene.instance && props.background) {
220
- scene.instance.background = new Color(props.background);
244
+ scene.instance.background = new THREE.Color(props.background);
221
245
  }
222
246
  // MISC PROPERTIES
223
247
  // ====================
@@ -253,12 +277,14 @@ const LunchboxWrapper = {
253
277
  camera: camera.instance,
254
278
  renderer: renderer.instance,
255
279
  scene: scene.instance,
280
+ updateSource: props.updateSource,
256
281
  });
257
282
  });
258
283
  // UNMOUNT
259
284
  // ====================
260
285
  onBeforeUnmount(() => {
261
286
  cancelUpdate();
287
+ cancelUpdateSource();
262
288
  });
263
289
  // RENDER FUNCTION
264
290
  // ====================
@@ -523,76 +549,6 @@ const isLunchboxRootNode = (node) => {
523
549
  return node.isLunchboxRootNode;
524
550
  };
525
551
 
526
- /** Create a new Lunchbox comment node. */
527
- function createCommentNode(options = {}) {
528
- const defaults = {
529
- text: options.text ?? '',
530
- };
531
- return new MiniDom.RendererCommentNode({
532
- ...defaults,
533
- ...options,
534
- metaType: 'commentMeta',
535
- });
536
- }
537
- /** Create a new DOM node. */
538
- function createDomNode(options = {}) {
539
- const domElement = document.createElement(options.type ?? '');
540
- const defaults = {
541
- domElement,
542
- };
543
- const node = new MiniDom.RendererDomNode({
544
- ...defaults,
545
- ...options,
546
- metaType: 'domMeta',
547
- });
548
- return node;
549
- }
550
- /** Create a new Lunchbox text node. */
551
- function createTextNode(options = {}) {
552
- const defaults = {
553
- text: options.text ?? '',
554
- };
555
- return new MiniDom.RendererTextNode({
556
- ...options,
557
- ...defaults,
558
- metaType: 'textMeta',
559
- });
560
- }
561
- /** Create a new Lunchbox standard node. */
562
- function createNode(options = {}, props = {}) {
563
- const defaults = {
564
- attached: options.attached ?? [],
565
- attachedArray: options.attachedArray ?? {},
566
- instance: options.instance ?? null,
567
- };
568
- const node = new MiniDom.RendererStandardNode({
569
- ...options,
570
- ...defaults,
571
- metaType: 'standardMeta',
572
- });
573
- if (node.type && !isLunchboxRootNode(node) && !node.instance) {
574
- // if (node.type.includes('Camera')) {
575
- // console.log(node.type, {
576
- // ...node.props,
577
- // ...props,
578
- // })
579
- // console.trace()
580
- // }
581
- node.instance = instantiateThreeObject({
582
- ...node,
583
- props: {
584
- ...node.props,
585
- ...props,
586
- },
587
- });
588
- }
589
- if (node.type === 'scene') {
590
- // manually set scene override
591
- ensuredScene.value = node;
592
- }
593
- return node;
594
- }
595
-
596
552
  const interactables = [];
597
553
  const addInteractable = (target) => {
598
554
  interactables.push(target);
@@ -674,6 +630,7 @@ let mouseDownListener;
674
630
  let mouseUpListener;
675
631
  const mousePos = ref({ x: Infinity, y: Infinity });
676
632
  let autoRaycasterEventsInitialized = false;
633
+ let frameID$1;
677
634
  const setupAutoRaycaster = (node) => {
678
635
  const instance = node.instance;
679
636
  if (!instance)
@@ -706,8 +663,14 @@ const setupAutoRaycaster = (node) => {
706
663
  renderer.instance.domElement.addEventListener('mousedown', mouseDownListener);
707
664
  renderer.instance.domElement.addEventListener('mouseup', mouseUpListener);
708
665
  // TODO: add touch events
709
- // add to update loop
710
- onBeforeRender(autoRaycasterBeforeRender);
666
+ // process mouse events asynchronously, whenever the mouse state changes
667
+ watch(() => [inputActive.value, mousePos.value.x, mousePos.value.y], () => {
668
+ if (frameID$1)
669
+ cancelAnimationFrame(frameID$1);
670
+ frameID$1 = requestAnimationFrame(() => {
671
+ autoRaycasterBeforeRender();
672
+ });
673
+ });
711
674
  // mark complete
712
675
  autoRaycasterEventsInitialized = true;
713
676
  // cancel setup watcher
@@ -918,17 +881,10 @@ const ensuredCamera = computed({
918
881
  overrides[pascalType] = val;
919
882
  },
920
883
  });
921
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
922
- // ['PerspectiveCamera', 'OrthographicCamera'],
923
- // fallbackCameraUuid,
924
- // {
925
- // args: [45, 0.5625, 1, 1000],
926
- // }
927
- // )
928
884
  // ENSURE RENDERER
929
885
  // ====================
930
886
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
931
- const v = buildEnsured(
887
+ const ensuredRenderer = buildEnsured(
932
888
  // TODO: ensure support for css/svg renderers
933
889
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
934
890
  fallbackRendererUuid, {});
@@ -937,7 +893,7 @@ fallbackRendererUuid, {});
937
893
  const rendererReady = ref(false);
938
894
  const ensureRenderer = computed({
939
895
  get() {
940
- return (rendererReady.value ? v.value : null);
896
+ return (rendererReady.value ? ensuredRenderer.value : null);
941
897
  },
942
898
  set(val) {
943
899
  const t = val.type ?? '';
@@ -954,6 +910,80 @@ const autoRaycasterUuid = 'AUTO_RAYCASTER';
954
910
  // `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
955
911
  const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
956
912
 
913
+ /** Create a new Lunchbox comment node. */
914
+ function createCommentNode(options = {}) {
915
+ const defaults = {
916
+ text: options.text ?? '',
917
+ };
918
+ return new MiniDom.RendererCommentNode({
919
+ ...defaults,
920
+ ...options,
921
+ metaType: 'commentMeta',
922
+ });
923
+ }
924
+ /** Create a new DOM node. */
925
+ function createDomNode(options = {}) {
926
+ const domElement = document.createElement(options.type ?? '');
927
+ const defaults = {
928
+ domElement,
929
+ };
930
+ const node = new MiniDom.RendererDomNode({
931
+ ...defaults,
932
+ ...options,
933
+ metaType: 'domMeta',
934
+ });
935
+ return node;
936
+ }
937
+ /** Create a new Lunchbox text node. */
938
+ function createTextNode(options = {}) {
939
+ const defaults = {
940
+ text: options.text ?? '',
941
+ };
942
+ return new MiniDom.RendererTextNode({
943
+ ...options,
944
+ ...defaults,
945
+ metaType: 'textMeta',
946
+ });
947
+ }
948
+ /** Create a new Lunchbox standard node. */
949
+ function createNode(options = {}, props = {}) {
950
+ const defaults = {
951
+ attached: options.attached ?? [],
952
+ attachedArray: options.attachedArray ?? {},
953
+ instance: options.instance ?? null,
954
+ };
955
+ const node = new MiniDom.RendererStandardNode({
956
+ ...options,
957
+ ...defaults,
958
+ metaType: 'standardMeta',
959
+ });
960
+ if (node.type && !isLunchboxRootNode(node) && !node.instance) {
961
+ // if (node.type.includes('Camera')) {
962
+ // console.log(node.type, {
963
+ // ...node.props,
964
+ // ...props,
965
+ // })
966
+ // console.trace()
967
+ // }
968
+ node.instance = instantiateThreeObject({
969
+ ...node,
970
+ props: {
971
+ ...node.props,
972
+ ...props,
973
+ },
974
+ });
975
+ }
976
+ // TODO: these manual overrides are a bit brittle - replace?
977
+ if (node.type?.toLowerCase() === 'scene') {
978
+ // manually set scene override
979
+ ensuredScene.value = node;
980
+ }
981
+ else if (node.type?.toLowerCase().endsWith('camera')) {
982
+ ensuredCamera.value = node;
983
+ }
984
+ return node;
985
+ }
986
+
957
987
  const createComponent = (tag) => defineComponent({
958
988
  inheritAttrs: false,
959
989
  name: tag,
@@ -969,7 +999,7 @@ const extend = ({ app, ...targets }) => {
969
999
  };
970
1000
 
971
1001
  /** Process props into either themselves or the $attached value */
972
- function processProp({ node, prop }) {
1002
+ function processProp({ node, prop, }) {
973
1003
  // return $attachedArray value if needed
974
1004
  if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
975
1005
  return node.attachedArray[prop.replace('$attachedArray.', '')];
@@ -981,10 +1011,12 @@ function processProp({ node, prop }) {
981
1011
  // otherwise, return plain value
982
1012
  return prop;
983
1013
  }
984
- function processPropAsArray({ node, prop }) {
1014
+ function processPropAsArray({ node, prop, }) {
985
1015
  const isAttachedArray = typeof prop === 'string' && prop.startsWith('$attachedArray');
986
1016
  const output = processProp({ node, prop });
987
- return Array.isArray(output) && isAttachedArray ? output : [output];
1017
+ return Array.isArray(output) && isAttachedArray
1018
+ ? output
1019
+ : [output];
988
1020
  }
989
1021
 
990
1022
  function instantiateThreeObject(node) {
@@ -1283,16 +1315,34 @@ const onStart = (cb, index = Infinity) => {
1283
1315
  };
1284
1316
 
1285
1317
  let frameID;
1318
+ let watchStopHandle;
1286
1319
  const beforeRender = [];
1287
1320
  const afterRender = [];
1288
- const update = (opts) => {
1289
- // request next frame
1321
+ const requestUpdate = (opts) => {
1322
+ cancelUpdate();
1290
1323
  frameID = requestAnimationFrame(() => update({
1291
1324
  app: opts.app,
1292
1325
  renderer: ensureRenderer.value?.instance,
1293
1326
  scene: ensuredScene.value.instance,
1294
1327
  camera: ensuredCamera.value?.instance,
1328
+ updateSource: opts.updateSource,
1295
1329
  }));
1330
+ };
1331
+ const update = (opts) => {
1332
+ if (opts.updateSource) {
1333
+ if (!watchStopHandle) {
1334
+ // request next frame only when state changes
1335
+ watchStopHandle = watch(opts.updateSource, () => {
1336
+ requestUpdate(opts);
1337
+ }, {
1338
+ deep: true,
1339
+ });
1340
+ }
1341
+ }
1342
+ else {
1343
+ // request next frame on a continuous loop
1344
+ requestUpdate(opts);
1345
+ }
1296
1346
  // prep options
1297
1347
  const { app, renderer, scene, camera } = opts;
1298
1348
  // BEFORE RENDER
@@ -1355,6 +1405,10 @@ const cancelUpdate = () => {
1355
1405
  if (frameID)
1356
1406
  cancelAnimationFrame(frameID);
1357
1407
  };
1408
+ const cancelUpdateSource = () => {
1409
+ if (watchStopHandle)
1410
+ watchStopHandle();
1411
+ };
1358
1412
 
1359
1413
  /** Update a single prop on a given node. */
1360
1414
  function updateObjectProp({ node, key, value, }) {
@@ -1710,46 +1764,31 @@ const globals = {
1710
1764
  /** The current camera. Often easier to use `useCamera` instead of this. */
1711
1765
  const camera = computed(() => ensuredCamera.value?.instance ?? null);
1712
1766
  /** Run a function using the current camera when it's present. */
1713
- function useCamera(callback, once = true) {
1714
- let destroy;
1715
- destroy = watch(camera, (newVal) => {
1767
+ function useCamera(callback) {
1768
+ return watch(camera, (newVal) => {
1716
1769
  if (!newVal)
1717
1770
  return;
1718
- // TODO: better fix than `any`?
1719
1771
  callback(newVal);
1720
- if (once) {
1721
- destroy?.();
1722
- }
1723
1772
  }, { immediate: true });
1724
1773
  }
1725
1774
  /** The current renderer. Often easier to use `useRenderer` instead of this. */
1726
1775
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1727
1776
  /** Run a function using the current renderer when it's present. */
1728
- function useRenderer(callback, once = true) {
1729
- let destroy;
1730
- destroy = watch(renderer, (newVal) => {
1777
+ function useRenderer(callback) {
1778
+ return watch(renderer, (newVal) => {
1731
1779
  if (!newVal)
1732
1780
  return;
1733
- // TODO: better fix than `any`?
1734
1781
  callback(newVal);
1735
- if (once) {
1736
- destroy?.();
1737
- }
1738
1782
  }, { immediate: true });
1739
1783
  }
1740
1784
  /** The current scene. Often easier to use `useScene` instead of this. */
1741
1785
  const scene = computed(() => ensuredScene.value.instance);
1742
1786
  /** Run a function using the current scene when it's present. */
1743
- function useScene(callback, once = true) {
1744
- let destroy;
1745
- destroy = watch(scene, (newVal) => {
1787
+ function useScene(callback) {
1788
+ return watch(scene, (newVal) => {
1746
1789
  if (!newVal)
1747
1790
  return;
1748
- // TODO: better fix than `any`?
1749
1791
  callback(newVal);
1750
- if (once) {
1751
- destroy?.();
1752
- }
1753
1792
  }, { immediate: true });
1754
1793
  }
1755
1794
  // CUSTOM RENDER SUPPORT
@@ -1778,12 +1817,14 @@ const createApp = (root) => {
1778
1817
  app = createRenderer(nodeOps).createApp(root);
1779
1818
  // register all components
1780
1819
  Object.keys(components).forEach((key) => {
1781
- app.component(key, components[key]);
1820
+ app?.component(key, components[key]);
1782
1821
  });
1783
1822
  // update mount function to match Lunchbox.Node
1784
1823
  const { mount } = app;
1785
1824
  app.mount = (root, ...args) => {
1825
+ // find DOM element to use as app root
1786
1826
  const domElement = (typeof root === 'string' ? document.querySelector(root) : root);
1827
+ // create or find root node
1787
1828
  const rootNode = ensureRootNode({
1788
1829
  domElement,
1789
1830
  isLunchboxRootNode: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4013",
3
+ "version": "0.1.4016",
4
4
  "scripts": {
5
5
  "dev": "vite -c utils/vite.config.ts",
6
6
  "build": "vue-tsc --noEmit && vite build -c utils/vite.config.ts",
@@ -11,10 +11,10 @@
11
11
  "prepare": "npm run build:lib",
12
12
  "docs:dev": "vitepress dev docs",
13
13
  "docs:build": "vitepress build docs",
14
- "docs:serve": "vitepress serve docs"
14
+ "docs:serve": "vitepress serve docs",
15
+ "demo:create": "node utils/createExample"
15
16
  },
16
17
  "dependencies": {
17
- "lodash": "4.17.21",
18
18
  "uuid": "8.3.2",
19
19
  "vue": "^3.2.16"
20
20
  },
@@ -22,13 +22,18 @@
22
22
  "@rollup/plugin-node-resolve": "13.0.5",
23
23
  "@rollup/plugin-typescript": "8.3.0",
24
24
  "@types/lodash": "4.14.175",
25
- "@types/three": "0.133.0",
25
+ "@types/three": "0.141.0",
26
26
  "@types/uuid": "8.3.1",
27
27
  "@vitejs/plugin-vue": "^1.9.3",
28
28
  "chroma-js": "2.1.2",
29
+ "kolorist": "1.5.1",
30
+ "lodash": "4.17.21",
29
31
  "nice-color-palettes": "3.0.0",
32
+ "prompt": "1.3.0",
33
+ "prompts": "2.4.2",
30
34
  "rollup-plugin-delete": "2.0.0",
31
35
  "rollup-plugin-terser": "7.0.2",
36
+ "three": "0.141.0",
32
37
  "typescript": "^4.4.3",
33
38
  "vite": "^2.6.4",
34
39
  "vite-plugin-glsl": "0.0.5",
@@ -36,7 +41,7 @@
36
41
  "vue-tsc": "^0.3.0"
37
42
  },
38
43
  "peerDependencies": {
39
- "three": "0.137.5"
44
+ "three": "0.141.0"
40
45
  },
41
46
  "files": [
42
47
  "dist",
package/src/.DS_Store ADDED
Binary file
@@ -5,11 +5,13 @@ import {
5
5
  onBeforeUnmount,
6
6
  onMounted,
7
7
  ref,
8
+ WatchSource,
8
9
  WritableComputedRef,
9
10
  } from 'vue'
10
11
  import {
11
12
  cameraReady,
12
13
  cancelUpdate,
14
+ cancelUpdateSource,
13
15
  createNode,
14
16
  ensuredCamera,
15
17
  ensureRenderer,
@@ -24,9 +26,13 @@ import {
24
26
  } from '../../core'
25
27
  import { set } from 'lodash'
26
28
  import { globals, Lunch } from '../..'
27
- import { Color, Vector2 } from 'three'
29
+ // import { Color, Vector2, sRGBEncoding, ACESFilmicToneMapping } from 'three'
30
+ import * as THREE from 'three'
28
31
  import { prepCanvas } from './prepCanvas'
29
32
 
33
+ // TODO:
34
+ // Continue r3f prop - what else (besides camera fov) makes r3f look good?
35
+
30
36
  /** fixed & fill styling for container */
31
37
  const fillStyle = (position: string) => {
32
38
  return {
@@ -52,11 +58,13 @@ export const LunchboxWrapper: ComponentOptions = {
52
58
  dpr: Number,
53
59
  ortho: Boolean,
54
60
  orthographic: Boolean,
61
+ r3f: Boolean,
55
62
  rendererArguments: Object,
56
63
  rendererProperties: Object,
57
64
  shadow: [Boolean, Object],
58
65
  transparent: Boolean,
59
66
  zoom: Number,
67
+ updateSource: Object,
60
68
  },
61
69
  setup(props: Lunch.WrapperProps, context) {
62
70
  const canvas = ref<MiniDom.RendererDomNode>()
@@ -67,6 +75,11 @@ export const LunchboxWrapper: ComponentOptions = {
67
75
  let camera: Lunch.Node<THREE.Camera> | null
68
76
  let scene: MiniDom.RendererStandardNode<THREE.Scene>
69
77
 
78
+ // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
79
+ if (props.r3f && (THREE as any)?.ColorManagement) {
80
+ ;(THREE as any).ColorManagement.legacyMode = false
81
+ }
82
+
70
83
  // MOUNT
71
84
  // ====================
72
85
  onMounted(() => {
@@ -88,6 +101,9 @@ export const LunchboxWrapper: ComponentOptions = {
88
101
  alpha: props.transparent,
89
102
  antialias: true,
90
103
  canvas: canvas.value.domElement,
104
+ powerPreference: !!props.r3f
105
+ ? 'high-performance'
106
+ : 'default',
91
107
  ...(props.rendererArguments ?? {}),
92
108
  }
93
109
 
@@ -108,6 +124,16 @@ export const LunchboxWrapper: ComponentOptions = {
108
124
  Lunch.Node<THREE.WebGLRenderer>
109
125
  >
110
126
 
127
+ // apply r3f settings if desired
128
+ if (props.r3f) {
129
+ if (rendererAsWebGlRenderer.value.instance) {
130
+ rendererAsWebGlRenderer.value.instance.outputEncoding =
131
+ THREE.sRGBEncoding
132
+ rendererAsWebGlRenderer.value.instance.toneMapping =
133
+ THREE.ACESFilmicToneMapping
134
+ }
135
+ }
136
+
111
137
  // update render sugar
112
138
  const sugar = {
113
139
  shadow: props.shadow,
@@ -160,7 +186,12 @@ export const LunchboxWrapper: ComponentOptions = {
160
186
  } else {
161
187
  ensuredCamera.value = createNode<THREE.PerspectiveCamera>({
162
188
  props: {
163
- args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
189
+ args: props.cameraArgs ?? [
190
+ props.r3f ? 75 : 45,
191
+ 0.5625,
192
+ 1,
193
+ 1000,
194
+ ],
164
195
  },
165
196
  type: 'PerspectiveCamera',
166
197
  uuid: fallbackCameraUuid,
@@ -195,7 +226,7 @@ export const LunchboxWrapper: ComponentOptions = {
195
226
  scene = ensuredScene.value
196
227
  // set background color
197
228
  if (scene && scene.instance && props.background) {
198
- scene.instance.background = new Color(props.background)
229
+ scene.instance.background = new THREE.Color(props.background)
199
230
  }
200
231
 
201
232
  // MISC PROPERTIES
@@ -239,6 +270,7 @@ export const LunchboxWrapper: ComponentOptions = {
239
270
  camera: camera.instance,
240
271
  renderer: renderer.instance,
241
272
  scene: scene.instance,
273
+ updateSource: props.updateSource,
242
274
  })
243
275
  })
244
276
 
@@ -246,6 +278,7 @@ export const LunchboxWrapper: ComponentOptions = {
246
278
  // ====================
247
279
  onBeforeUnmount(() => {
248
280
  cancelUpdate()
281
+ cancelUpdateSource()
249
282
  })
250
283
 
251
284
  // RENDER FUNCTION
@@ -5,7 +5,7 @@ import { resizeCanvas } from './resizeCanvas'
5
5
  export const prepCanvas = (
6
6
  container: Ref<MiniDom.RendererDomNode | undefined>,
7
7
  canvasElement: HTMLCanvasElement,
8
- onBeforeUnmount: Function,
8
+ onBeforeUnmount: Function
9
9
  ) => {
10
10
  const containerElement = container.value?.domElement
11
11
  if (!containerElement) throw new Error('missing container')
@@ -29,4 +29,4 @@ export const prepCanvas = (
29
29
  observer.unobserve(canvasElement)
30
30
  }
31
31
  })
32
- }
32
+ }
@@ -1,3 +1,3 @@
1
1
  import { Lunch } from '..'
2
2
 
3
- export const catalogue: Lunch.Catalogue = {}
3
+ export const catalogue: Lunch.Catalogue = {}
Binary file
@@ -1,6 +1,7 @@
1
1
  import { isLunchboxRootNode } from '../utils'
2
2
  import { instantiateThreeObject, MiniDom, ensuredScene } from '.'
3
3
  import { Lunch } from '..'
4
+ import { ensuredCamera } from './ensure'
4
5
 
5
6
  /** Create a new Lunchbox comment node. */
6
7
  export function createCommentNode(options: Partial<Lunch.CommentMeta> = {}) {
@@ -74,9 +75,12 @@ export function createNode<T extends object = THREE.Object3D>(
74
75
  })
75
76
  }
76
77
 
77
- if (node.type === 'scene') {
78
+ // TODO: these manual overrides are a bit brittle - replace?
79
+ if (node.type?.toLowerCase() === 'scene') {
78
80
  // manually set scene override
79
81
  ensuredScene.value = node as Lunch.Node<THREE.Scene>
82
+ } else if (node.type?.toLowerCase().endsWith('camera')) {
83
+ ensuredCamera.value = node as Lunch.Node<THREE.Camera>
80
84
  }
81
85
 
82
86
  return node