lunchboxjs 0.1.4002 → 0.1.4006-dev002

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.
@@ -9,27 +9,36 @@ const allNodes = [];
9
9
  const resizeCanvas = (width, height) => {
10
10
  const renderer = ensureRenderer.value?.instance;
11
11
  const scene = ensuredScene.value.instance;
12
+ const camera = ensuredCamera.value;
12
13
  // ignore if no element
13
- if (!renderer?.domElement || !scene)
14
+ if (!renderer?.domElement || !scene || !camera)
14
15
  return;
15
16
  width = width ?? window.innerWidth;
16
17
  height = height ?? window.innerHeight;
17
18
  // update camera
18
19
  const aspect = width / height;
19
- const camera = ensuredCamera.value;
20
20
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
21
21
  const perspectiveCamera = camera.instance;
22
22
  perspectiveCamera.aspect = aspect;
23
23
  perspectiveCamera.updateProjectionMatrix();
24
24
  }
25
+ else if (camera.type?.toLowerCase() === 'orthographiccamera') {
26
+ // console.log('TODO: ortho camera update')
27
+ const orthoCamera = camera.instance;
28
+ const heightInTermsOfWidth = height / width;
29
+ orthoCamera.top = heightInTermsOfWidth * 10;
30
+ orthoCamera.bottom = -heightInTermsOfWidth * 10;
31
+ orthoCamera.right = 10;
32
+ orthoCamera.left = -10;
33
+ orthoCamera.updateProjectionMatrix();
34
+ }
25
35
  else {
26
- console.log('TODO: ortho camera update');
36
+ console.log('TODO: non-ortho or perspective camera');
27
37
  }
28
38
  // update canvas
29
39
  renderer.setSize(width, height);
30
40
  // render immediately so there's no flicker
31
41
  if (scene && camera.instance) {
32
- // const cameraInstance = scene.traverse(v => )
33
42
  renderer.render(toRaw(scene), toRaw(camera.instance));
34
43
  }
35
44
  };
@@ -74,8 +83,13 @@ const LunchboxWrapper = {
74
83
  props: {
75
84
  // These should match the Lunchbox.WrapperProps interface
76
85
  background: String,
86
+ cameraArgs: Array,
87
+ cameraLook: Array,
88
+ cameraLookAt: Array,
77
89
  cameraPosition: Array,
78
90
  dpr: Number,
91
+ ortho: Boolean,
92
+ orthographic: Boolean,
79
93
  rendererProperties: Object,
80
94
  shadow: [Boolean, Object],
81
95
  transparent: Boolean,
@@ -86,6 +100,7 @@ const LunchboxWrapper = {
86
100
  const dpr = ref(props.dpr ?? -1);
87
101
  const container = ref();
88
102
  let renderer;
103
+ let camera;
89
104
  let scene;
90
105
  // MOUNT
91
106
  // ====================
@@ -93,12 +108,6 @@ const LunchboxWrapper = {
93
108
  // canvas needs to exist
94
109
  if (!canvas.value)
95
110
  throw new Error('missing canvas');
96
- // ensure camera
97
- const camera = ensuredCamera.value.instance;
98
- // move camera if needed
99
- if (camera && props.cameraPosition) {
100
- camera.position.set(...props.cameraPosition);
101
- }
102
111
  // RENDERER
103
112
  // ====================
104
113
  // is there already a renderer?
@@ -155,6 +164,47 @@ const LunchboxWrapper = {
155
164
  rendererReady.value = true;
156
165
  return;
157
166
  }
167
+ // CAMERA
168
+ // ====================
169
+ // is there already a camera?
170
+ camera = tryGetNodeWithInstanceType([
171
+ 'PerspectiveCamera',
172
+ 'OrthographicCamera',
173
+ ]);
174
+ // if not, let's create one
175
+ if (!camera) {
176
+ // create ortho camera
177
+ if (props.ortho || props.orthographic) {
178
+ ensuredCamera.value = createNode({
179
+ props: { args: props.cameraArgs ?? [] },
180
+ type: 'OrthographicCamera',
181
+ uuid: fallbackCameraUuid,
182
+ });
183
+ }
184
+ else {
185
+ ensuredCamera.value = createNode({
186
+ props: {
187
+ args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
188
+ },
189
+ type: 'PerspectiveCamera',
190
+ uuid: fallbackCameraUuid,
191
+ });
192
+ }
193
+ cameraReady.value = true;
194
+ camera = ensuredCamera.value;
195
+ }
196
+ else {
197
+ cameraReady.value = true;
198
+ }
199
+ // move camera if needed
200
+ if (camera && props.cameraPosition) {
201
+ camera.instance?.position.set(...props.cameraPosition);
202
+ }
203
+ // angle camera if needed
204
+ if (camera && (props.cameraLookAt || props.cameraLook)) {
205
+ const source = (props.cameraLookAt || props.cameraLook);
206
+ camera.instance?.lookAt(...source);
207
+ }
158
208
  // SCENE
159
209
  // ====================
160
210
  scene = ensuredScene.value;
@@ -181,7 +231,7 @@ const LunchboxWrapper = {
181
231
  // console.log(scene)
182
232
  update({
183
233
  app: getCurrentInstance().appContext.app,
184
- camera,
234
+ camera: camera.instance,
185
235
  renderer: renderer.instance,
186
236
  scene: scene.instance,
187
237
  });
@@ -211,21 +261,6 @@ const LunchboxWrapper = {
211
261
  },
212
262
  };
213
263
 
214
- const catalogue = {};
215
-
216
- const lunchboxDomComponentNames = [
217
- 'canvas',
218
- 'div',
219
- 'LunchboxWrapper',
220
- ];
221
- // component creation utility
222
- const createComponent$1 = (tag) => defineComponent({
223
- inheritAttrs: false,
224
- name: tag,
225
- setup(props, context) {
226
- return () => h(tag, context.attrs, context.slots?.default?.() || []);
227
- },
228
- });
229
264
  // list of all components to register out of the box
230
265
  const autoGeneratedComponents = [
231
266
  // ThreeJS basics
@@ -338,21 +373,11 @@ const autoGeneratedComponents = [
338
373
  'arrayCamera',
339
374
  // renderers
340
375
  'webGLRenderer',
341
- ].map(createComponent$1).reduce((acc, curr) => {
342
- acc[curr.name] = curr;
343
- return acc;
344
- });
345
- const components = {
346
- ...autoGeneratedComponents,
347
- 'Lunchbox': LunchboxWrapper,
348
- // Gltf,
349
- };
350
- // console.log(components, Gltf)
351
- /*
352
- // List copied from r3f
353
- // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
376
+ /*
377
+ // List copied from r3f:
378
+ // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
354
379
 
355
- // NOT IMPLEMENTED:
380
+ // NOT IMPLEMENTED (can be added via Extend - docs.lunchboxjs.com/components/extend/):
356
381
  audioListener: AudioListenerProps
357
382
  positionalAudio: PositionalAudioProps
358
383
 
@@ -400,6 +425,27 @@ const components = {
400
425
  fogExp2: FogExp2Props
401
426
  shape: ShapeProps
402
427
  */
428
+ ];
429
+
430
+ const catalogue = {};
431
+
432
+ const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper'];
433
+ // component creation utility
434
+ const createComponent$1 = (tag) => defineComponent({
435
+ inheritAttrs: false,
436
+ name: tag,
437
+ setup(props, context) {
438
+ return () => h(tag, context.attrs, context.slots?.default?.() || []);
439
+ },
440
+ });
441
+ autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
442
+ acc[curr.name] = curr;
443
+ return acc;
444
+ });
445
+ const components = {
446
+ ...autoGeneratedComponents,
447
+ Lunchbox: LunchboxWrapper,
448
+ };
403
449
 
404
450
  function find(target) {
405
451
  target = isRef(target) ? target.value : target;
@@ -561,6 +607,7 @@ function addEventListener({ node, key, value, }) {
561
607
  // register click, pointerdown, pointerup
562
608
  if (key === 'onClick' || key === 'onPointerDown' || key === 'onPointerUp') {
563
609
  const stop = watch(() => inputActive.value, (isDown) => {
610
+ console.log(isDown, currentIntersections);
564
611
  const idx = currentIntersections
565
612
  .map((v) => v.element)
566
613
  .findIndex((v) => v.instance &&
@@ -828,9 +875,27 @@ function buildEnsured(pascalCaseTypes, fallbackUuid, props = {}, callback = null
828
875
  // ENSURE CAMERA
829
876
  // ====================
830
877
  const fallbackCameraUuid = 'FALLBACK_CAMERA';
831
- const ensuredCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, {
832
- args: [45, 0.5625, 1, 1000],
878
+ const defaultCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, { args: [45, 0.5625, 1, 1000] });
879
+ /** Special value to be changed ONLY in `LunchboxWrapper`.
880
+ * Functions waiting for a Camera need to wait for this to be true. */
881
+ const cameraReady = ref(false);
882
+ const ensuredCamera = computed({
883
+ get() {
884
+ return (cameraReady.value ? defaultCamera.value : null);
885
+ },
886
+ set(val) {
887
+ const t = val.type ?? '';
888
+ const pascalType = t[0].toUpperCase() + t.slice(1);
889
+ overrides[pascalType] = val;
890
+ },
833
891
  });
892
+ // export const ensuredCamera = buildEnsured<THREE.Camera>(
893
+ // ['PerspectiveCamera', 'OrthographicCamera'],
894
+ // fallbackCameraUuid,
895
+ // {
896
+ // args: [45, 0.5625, 1, 1000],
897
+ // }
898
+ // )
834
899
  // ENSURE RENDERER
835
900
  // ====================
836
901
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
@@ -1179,12 +1244,14 @@ let frameID;
1179
1244
  const beforeRender = [];
1180
1245
  const afterRender = [];
1181
1246
  const update = (opts) => {
1247
+ // request next frame
1182
1248
  frameID = requestAnimationFrame(() => update({
1183
1249
  app: opts.app,
1184
1250
  renderer: ensureRenderer.value?.instance,
1185
1251
  scene: ensuredScene.value.instance,
1186
- camera: ensuredCamera.value.instance,
1252
+ camera: ensuredCamera.value?.instance,
1187
1253
  }));
1254
+ // prep options
1188
1255
  const { app, renderer, scene, camera } = opts;
1189
1256
  // BEFORE RENDER
1190
1257
  beforeRender.forEach((cb) => {
@@ -1193,9 +1260,13 @@ const update = (opts) => {
1193
1260
  }
1194
1261
  });
1195
1262
  // RENDER
1196
- // console.log(camera?.position.z)
1197
1263
  if (renderer && scene && camera) {
1198
- renderer.render(toRaw(scene), toRaw(camera));
1264
+ if (app.customRender) {
1265
+ app.customRender(opts);
1266
+ }
1267
+ else {
1268
+ renderer.render(toRaw(scene), toRaw(camera));
1269
+ }
1199
1270
  }
1200
1271
  // AFTER RENDER
1201
1272
  afterRender.forEach((cb) => {
@@ -1212,6 +1283,15 @@ const onBeforeRender = (cb, index = Infinity) => {
1212
1283
  beforeRender.splice(index, 0, cb);
1213
1284
  }
1214
1285
  };
1286
+ const offBeforeRender = (cb) => {
1287
+ if (isFinite(cb)) {
1288
+ beforeRender.splice(cb, 1);
1289
+ }
1290
+ else {
1291
+ const idx = beforeRender.findIndex((v) => v == cb);
1292
+ beforeRender.splice(idx, 1);
1293
+ }
1294
+ };
1215
1295
  const onAfterRender = (cb, index = Infinity) => {
1216
1296
  if (index === Infinity) {
1217
1297
  afterRender.push(cb);
@@ -1220,6 +1300,15 @@ const onAfterRender = (cb, index = Infinity) => {
1220
1300
  afterRender.splice(index, 0, cb);
1221
1301
  }
1222
1302
  };
1303
+ const offAfterRender = (cb) => {
1304
+ if (isFinite(cb)) {
1305
+ afterRender.splice(cb, 1);
1306
+ }
1307
+ else {
1308
+ const idx = afterRender.findIndex((v) => v == cb);
1309
+ afterRender.splice(idx, 1);
1310
+ }
1311
+ };
1223
1312
  const cancelUpdate = () => {
1224
1313
  if (frameID)
1225
1314
  cancelAnimationFrame(frameID);
@@ -1573,11 +1662,33 @@ const globals = {
1573
1662
  inputActive,
1574
1663
  mousePos,
1575
1664
  };
1576
- const camera = computed(() => ensuredCamera.value.instance);
1665
+ const camera = computed(() => ensuredCamera.value?.instance ?? null);
1577
1666
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1578
1667
  const scene = computed(() => ensuredScene.value.instance);
1668
+ // CUSTOM RENDER SUPPORT
1669
+ // ====================
1670
+ let app = null;
1671
+ let queuedCustomRenderFunction = null;
1672
+ /** Set a custom render function, overriding the Lunchbox app's default render function.
1673
+ * Changing this requires the user to manually render their scene.
1674
+ */
1675
+ const setCustomRender = (render) => {
1676
+ if (app)
1677
+ app.setCustomRender(render);
1678
+ else
1679
+ queuedCustomRenderFunction = render;
1680
+ };
1681
+ /** Clear the active app's custom render function. */
1682
+ const clearCustomRender = () => {
1683
+ if (app)
1684
+ app.clearCustomRender();
1685
+ else
1686
+ queuedCustomRenderFunction = null;
1687
+ };
1688
+ // CREATE APP
1689
+ // ====================
1579
1690
  const createApp = (root) => {
1580
- const app = createRenderer(nodeOps).createApp(root);
1691
+ app = createRenderer(nodeOps).createApp(root);
1581
1692
  // register all components
1582
1693
  Object.keys(components).forEach((key) => {
1583
1694
  app.component(key, components[key]);
@@ -1600,11 +1711,24 @@ const createApp = (root) => {
1600
1711
  };
1601
1712
  // embed .extend function
1602
1713
  app.extend = (targets) => {
1603
- extend({ app, ...targets });
1714
+ extend({ app: app, ...targets });
1604
1715
  return app;
1605
1716
  };
1717
+ // prep for custom render support
1718
+ app.setCustomRender = (newRender) => {
1719
+ app.customRender = newRender;
1720
+ };
1721
+ // add queued custom render if we have one
1722
+ if (queuedCustomRenderFunction) {
1723
+ app.setCustomRender(queuedCustomRenderFunction);
1724
+ queuedCustomRenderFunction = null;
1725
+ }
1726
+ // add custom render removal
1727
+ app.clearCustomRender = () => {
1728
+ app.customRender = null;
1729
+ };
1606
1730
  // done
1607
1731
  return app;
1608
1732
  };
1609
1733
 
1610
- export { camera, createApp, find, globals, lunchboxRootNode as lunchboxTree, onAfterRender, onBeforeRender, renderer, scene, update };
1734
+ export { autoGeneratedComponents, camera, clearCustomRender, createApp, find, globals, lunchboxRootNode as lunchboxTree, offAfterRender, offBeforeRender, onAfterRender, onBeforeRender, renderer, scene, setCustomRender };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4002",
3
+ "version": "0.1.4006-dev002",
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",
@@ -25,6 +25,8 @@
25
25
  "@types/three": "0.133.0",
26
26
  "@types/uuid": "8.3.1",
27
27
  "@vitejs/plugin-vue": "^1.9.3",
28
+ "chroma-js": "2.1.2",
29
+ "nice-color-palettes": "3.0.0",
28
30
  "rollup-plugin-delete": "2.0.0",
29
31
  "rollup-plugin-terser": "7.0.2",
30
32
  "typescript": "^4.4.3",
@@ -8,11 +8,13 @@ import {
8
8
  WritableComputedRef,
9
9
  } from 'vue'
10
10
  import {
11
+ cameraReady,
11
12
  cancelUpdate,
12
13
  createNode,
13
14
  ensuredCamera,
14
15
  ensureRenderer,
15
16
  ensuredScene,
17
+ fallbackCameraUuid,
16
18
  fallbackRendererUuid,
17
19
  MiniDom,
18
20
  rendererReady,
@@ -21,7 +23,7 @@ import {
21
23
  } from '../../core'
22
24
  import { set } from 'lodash'
23
25
  import { globals, Lunch } from '../..'
24
- import { Color } from 'three'
26
+ import { Color, Vector2 } from 'three'
25
27
  import { prepCanvas } from './prepCanvas'
26
28
 
27
29
  /** fixed & fill styling for container */
@@ -42,8 +44,13 @@ export const LunchboxWrapper: ComponentOptions = {
42
44
  props: {
43
45
  // These should match the Lunchbox.WrapperProps interface
44
46
  background: String,
47
+ cameraArgs: Array,
48
+ cameraLook: Array,
49
+ cameraLookAt: Array,
45
50
  cameraPosition: Array,
46
51
  dpr: Number,
52
+ ortho: Boolean,
53
+ orthographic: Boolean,
47
54
  rendererProperties: Object,
48
55
  shadow: [Boolean, Object],
49
56
  transparent: Boolean,
@@ -54,6 +61,7 @@ export const LunchboxWrapper: ComponentOptions = {
54
61
  const dpr = ref(props.dpr ?? -1)
55
62
  const container = ref<MiniDom.RendererDomNode>()
56
63
  let renderer: Lunch.Node<THREE.WebGLRenderer> | null
64
+ let camera: Lunch.Node<THREE.Camera> | null
57
65
  let scene: MiniDom.RendererStandardNode<THREE.Scene>
58
66
 
59
67
  // MOUNT
@@ -62,13 +70,6 @@ export const LunchboxWrapper: ComponentOptions = {
62
70
  // canvas needs to exist
63
71
  if (!canvas.value) throw new Error('missing canvas')
64
72
 
65
- // ensure camera
66
- const camera = ensuredCamera.value.instance
67
- // move camera if needed
68
- if (camera && props.cameraPosition) {
69
- camera.position.set(...props.cameraPosition)
70
- }
71
-
72
73
  // RENDERER
73
74
  // ====================
74
75
  // is there already a renderer?
@@ -139,6 +140,48 @@ export const LunchboxWrapper: ComponentOptions = {
139
140
  return
140
141
  }
141
142
 
143
+ // CAMERA
144
+ // ====================
145
+ // is there already a camera?
146
+ camera = tryGetNodeWithInstanceType([
147
+ 'PerspectiveCamera',
148
+ 'OrthographicCamera',
149
+ ])
150
+ // if not, let's create one
151
+ if (!camera) {
152
+ // create ortho camera
153
+ if (props.ortho || props.orthographic) {
154
+ ensuredCamera.value = createNode<THREE.OrthographicCamera>({
155
+ props: { args: props.cameraArgs ?? [] },
156
+ type: 'OrthographicCamera',
157
+ uuid: fallbackCameraUuid,
158
+ })
159
+ } else {
160
+ ensuredCamera.value = createNode<THREE.PerspectiveCamera>({
161
+ props: {
162
+ args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
163
+ },
164
+ type: 'PerspectiveCamera',
165
+ uuid: fallbackCameraUuid,
166
+ })
167
+ }
168
+
169
+ cameraReady.value = true
170
+
171
+ camera = ensuredCamera.value
172
+ } else {
173
+ cameraReady.value = true
174
+ }
175
+ // move camera if needed
176
+ if (camera && props.cameraPosition) {
177
+ camera.instance?.position.set(...props.cameraPosition)
178
+ }
179
+ // angle camera if needed
180
+ if (camera && (props.cameraLookAt || props.cameraLook)) {
181
+ const source = (props.cameraLookAt || props.cameraLook)!
182
+ camera.instance?.lookAt(...source)
183
+ }
184
+
142
185
  // SCENE
143
186
  // ====================
144
187
  scene = ensuredScene.value
@@ -171,7 +214,7 @@ export const LunchboxWrapper: ComponentOptions = {
171
214
  // console.log(scene)
172
215
  update({
173
216
  app: getCurrentInstance()!.appContext.app as Lunch.App,
174
- camera,
217
+ camera: camera.instance,
175
218
  renderer: renderer.instance,
176
219
  scene: scene.instance,
177
220
  })
@@ -4,29 +4,37 @@ import { toRaw } from 'vue'
4
4
  export const resizeCanvas = (width?: number, height?: number) => {
5
5
  const renderer = ensureRenderer.value?.instance
6
6
  const scene = ensuredScene.value.instance
7
+ const camera = ensuredCamera.value
7
8
 
8
9
  // ignore if no element
9
- if (!renderer?.domElement || !scene) return
10
+ if (!renderer?.domElement || !scene || !camera) return
10
11
 
11
12
  width = width ?? window.innerWidth
12
13
  height = height ?? window.innerHeight
13
14
 
14
15
  // update camera
15
16
  const aspect = width / height
16
- const camera = ensuredCamera.value
17
17
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
18
18
  const perspectiveCamera = camera.instance as THREE.PerspectiveCamera
19
19
  perspectiveCamera.aspect = aspect
20
20
  perspectiveCamera.updateProjectionMatrix()
21
+ } else if (camera.type?.toLowerCase() === 'orthographiccamera') {
22
+ // console.log('TODO: ortho camera update')
23
+ const orthoCamera = camera.instance as THREE.OrthographicCamera
24
+ const heightInTermsOfWidth = height / width
25
+ orthoCamera.top = heightInTermsOfWidth * 10
26
+ orthoCamera.bottom = -heightInTermsOfWidth * 10
27
+ orthoCamera.right = 10
28
+ orthoCamera.left = -10
29
+ orthoCamera.updateProjectionMatrix()
21
30
  } else {
22
- console.log('TODO: ortho camera update')
31
+ console.log('TODO: non-ortho or perspective camera')
23
32
  }
24
33
 
25
34
  // update canvas
26
35
  renderer.setSize(width, height)
27
36
  // render immediately so there's no flicker
28
37
  if (scene && camera.instance) {
29
- // const cameraInstance = scene.traverse(v => )
30
38
  renderer.render(toRaw(scene), toRaw(camera.instance))
31
39
  }
32
40
  }