lunchboxjs 0.1.4016 → 0.2.1001-beta.0

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.
@@ -12,7 +12,14 @@
12
12
 
13
13
  <script lang="ts" setup>
14
14
  import { computed, ref, watch } from 'vue'
15
- import { onBeforeRender, globals, Lunch, camera, renderer } from '../src'
15
+ import {
16
+ onBeforeRender,
17
+ globals,
18
+ Lunch,
19
+ // camera,
20
+ useRenderer,
21
+ useCamera,
22
+ } from '../src'
16
23
 
17
24
  // props
18
25
  const props = defineProps<{
@@ -20,14 +27,15 @@ const props = defineProps<{
20
27
  }>()
21
28
 
22
29
  // computed
23
- // const camera = globals.camera
24
- // const renderer = globals.renderer
25
30
  const ready = computed(() => {
26
- return camera.value !== null && renderer.value?.domElement
31
+ return camera.value !== null && renderer.value.domElement
27
32
  })
33
+ const camera = useCamera()
34
+ const renderer = useRenderer()
28
35
  const orbitArgs = computed(() => [camera.value, renderer.value?.domElement])
29
36
  // watch(() => orbitArgs.value, console.log, { immediate: true })
30
37
  // console.log(renderer)
38
+ watch(camera, console.log)
31
39
 
32
40
  // update
33
41
  const controls = ref<Lunch.LunchboxComponent>()
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4016",
3
+ "version": "0.2.1001-beta.0",
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",
7
7
  "build:tsc": "tsc --project ./utils/tsconfig.lib.json",
8
8
  "build:rollup": "rollup -c ./utils/lib-rollup.ts",
9
9
  "build:dts": "cp utils/lib-dts.d.ts dist/lunchboxjs.es.d.ts && cp utils/lib-dts.d.ts dist/lunchboxjs.umd.d.ts",
10
- "build:lib": "npm run build:tsc && npm run build:rollup && npm run build:dts",
10
+ "build:lib": "rm -rf js && npm run build:tsc && npm run build:rollup && npm run build:dts",
11
11
  "prepare": "npm run build:lib",
12
12
  "docs:dev": "vitepress dev docs",
13
13
  "docs:build": "vitepress build docs",
@@ -19,12 +19,17 @@
19
19
  "vue": "^3.2.16"
20
20
  },
21
21
  "devDependencies": {
22
+ "@babel/plugin-transform-runtime": "7.18.10",
23
+ "@rollup/plugin-babel": "5.3.1",
22
24
  "@rollup/plugin-node-resolve": "13.0.5",
23
25
  "@rollup/plugin-typescript": "8.3.0",
24
26
  "@types/lodash": "4.14.175",
27
+ "@types/node": "18.6.3",
25
28
  "@types/three": "0.141.0",
26
29
  "@types/uuid": "8.3.1",
27
- "@vitejs/plugin-vue": "^1.9.3",
30
+ "@vitejs/plugin-vue": "3.0.1",
31
+ "@vitejs/plugin-vue-jsx": "2.0.0",
32
+ "@vue/babel-plugin-jsx": "1.1.1",
28
33
  "chroma-js": "2.1.2",
29
34
  "kolorist": "1.5.1",
30
35
  "lodash": "4.17.21",
@@ -32,12 +37,14 @@
32
37
  "prompt": "1.3.0",
33
38
  "prompts": "2.4.2",
34
39
  "rollup-plugin-delete": "2.0.0",
40
+ "rollup-plugin-jsx": "1.0.3",
35
41
  "rollup-plugin-terser": "7.0.2",
36
42
  "three": "0.141.0",
37
43
  "typescript": "^4.4.3",
38
- "vite": "^2.6.4",
44
+ "vite": "3.0.4",
39
45
  "vite-plugin-glsl": "0.0.5",
40
46
  "vitepress": "0.20.1",
47
+ "vue-jsx-factory": "0.3.0",
41
48
  "vue-tsc": "^0.3.0"
42
49
  },
43
50
  "peerDependencies": {
@@ -0,0 +1,8 @@
1
+ import { defineComponent } from 'vue'
2
+
3
+ export const LunchboxScene = defineComponent({
4
+ name: 'LunchboxScene',
5
+ setup(props, { slots }) {
6
+ return () => <scene>{slots.default?.()}</scene>
7
+ },
8
+ })
@@ -0,0 +1,299 @@
1
+ import {
2
+ defineComponent,
3
+ onBeforeUnmount,
4
+ onMounted,
5
+ PropType,
6
+ reactive,
7
+ ref,
8
+ WatchSource,
9
+ } from 'vue'
10
+ import { cancelUpdate, cancelUpdateSource, MiniDom, update } from '../../core'
11
+ import { Lunch, useApp, useGlobals } from '../..'
12
+ import * as THREE from 'three'
13
+ import { prepCanvas } from './prepCanvas'
14
+ import { useUpdateGlobals, useStartCallbacks } from '../..'
15
+ import { LunchboxScene } from './LunchboxScene'
16
+
17
+ /** fixed & fill styling for container */
18
+ const fillStyle = (position: string) => {
19
+ return {
20
+ position,
21
+ top: 0,
22
+ right: 0,
23
+ bottom: 0,
24
+ left: 0,
25
+ width: '100%',
26
+ height: '100%',
27
+ display: 'block',
28
+ }
29
+ }
30
+
31
+ export const LunchboxWrapper = defineComponent({
32
+ name: 'Lunchbox',
33
+ props: {
34
+ // These should match the Lunchbox.WrapperProps interface
35
+ background: String,
36
+ cameraArgs: Array,
37
+ cameraLook: Array as unknown as PropType<Lunch.Vector3AsArray>,
38
+ cameraLookAt: Array as unknown as PropType<Lunch.Vector3AsArray>,
39
+ cameraPosition: Array as unknown as PropType<Lunch.Vector3AsArray>,
40
+ dpr: Number,
41
+ ortho: Boolean,
42
+ orthographic: Boolean,
43
+ r3f: Boolean,
44
+ rendererArguments: Object,
45
+ rendererProperties: Object,
46
+ sizePolicy: String as PropType<Lunch.SizePolicy>,
47
+ shadow: [Boolean, Object],
48
+ transparent: Boolean,
49
+ zoom: Number,
50
+ updateSource: Object as PropType<WatchSource>,
51
+ },
52
+ setup(props, context) {
53
+ const canvas = ref<MiniDom.RendererDomNode>()
54
+ let dpr = props.dpr ?? -1
55
+ const container = ref<MiniDom.RendererDomNode>()
56
+ const renderer = ref<Lunch.LunchboxComponent<THREE.Renderer>>()
57
+ const camera = ref<Lunch.LunchboxComponent<THREE.Camera>>()
58
+ const scene = ref<Lunch.LunchboxComponent<THREE.Scene>>()
59
+ const globals = useGlobals()
60
+ const updateGlobals = useUpdateGlobals()
61
+ const app = useApp()
62
+ const consolidatedCameraProperties: Record<string, any> = reactive({})
63
+ const startCallbacks = useStartCallbacks()
64
+
65
+ // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
66
+ if (props.r3f && (THREE as any)?.ColorManagement) {
67
+ ;(THREE as any).ColorManagement.legacyMode = false
68
+ }
69
+
70
+ // MOUNT
71
+ // ====================
72
+ onMounted(async () => {
73
+ // canvas needs to exist (or user needs to handle it on their own)
74
+ if (!canvas.value && !context.slots?.renderer?.()?.length)
75
+ throw new Error('missing canvas')
76
+
77
+ // no camera provided, so let's create one
78
+ if (!context.slots?.camera?.()?.length) {
79
+ if (props.cameraPosition) {
80
+ consolidatedCameraProperties.position = props.cameraPosition
81
+ }
82
+ if (props.cameraLook || props.cameraLookAt) {
83
+ consolidatedCameraProperties.lookAt =
84
+ props.cameraLook || props.cameraLookAt
85
+ }
86
+ if (props.zoom !== undefined) {
87
+ consolidatedCameraProperties.zoom = props.zoom
88
+ }
89
+ }
90
+
91
+ // SCENE
92
+ // ====================
93
+ // set background color
94
+ if (scene.value?.$el?.instance && props.background) {
95
+ scene.value.$el.instance.background = new THREE.Color(
96
+ props.background
97
+ )
98
+ }
99
+
100
+ // MISC PROPERTIES
101
+ // ====================
102
+ if (dpr === -1) {
103
+ dpr = window.devicePixelRatio
104
+ }
105
+ updateGlobals?.({ dpr })
106
+
107
+ console.log(1)
108
+ while (
109
+ !renderer.value?.$el?.instance &&
110
+ // TODO: remove `as any`
111
+ !(renderer.value as any)?.component?.ctx.$el?.instance
112
+ ) {
113
+ console.log(2)
114
+ await new Promise((r) => requestAnimationFrame(r))
115
+ }
116
+
117
+ console.log(3)
118
+ while (
119
+ !scene.value?.$el?.instance &&
120
+ // TODO: remove `as any`
121
+ !(scene.value as any)?.component?.ctx.$el?.instance
122
+ ) {
123
+ console.log(4)
124
+ await new Promise((r) => requestAnimationFrame(r))
125
+ }
126
+ console.log(5)
127
+
128
+ const normalizedRenderer = (renderer.value?.$el?.instance ??
129
+ (renderer.value as any)?.component?.ctx.$el
130
+ ?.instance) as THREE.WebGLRenderer
131
+
132
+ normalizedRenderer.setPixelRatio(globals.dpr)
133
+
134
+ const normalizedScene = (scene.value?.$el?.instance ??
135
+ (scene.value as any)?.component?.ctx.$el
136
+ ?.instance) as THREE.Scene
137
+
138
+ const normalizedCamera = (camera.value?.$el?.instance ??
139
+ (camera.value as any)?.component?.ctx.$el
140
+ ?.instance) as THREE.Camera
141
+
142
+ // TODO: update DPR on monitor switch
143
+ // prep canvas (sizing, observe, unmount, etc)
144
+ // (only run if no custom renderer)
145
+ if (!context.slots?.renderer?.()?.length) {
146
+ // TODO: use dispose
147
+ const { dispose } = prepCanvas(
148
+ container,
149
+ normalizedCamera,
150
+ normalizedRenderer,
151
+ normalizedScene,
152
+ props.sizePolicy
153
+ )
154
+
155
+ if (props.r3f) {
156
+ normalizedRenderer.outputEncoding = THREE.sRGBEncoding
157
+ normalizedRenderer.toneMapping = THREE.ACESFilmicToneMapping
158
+ }
159
+
160
+ // update render sugar
161
+ const sugar = {
162
+ shadow: props.shadow,
163
+ }
164
+ if (sugar?.shadow) {
165
+ normalizedRenderer.shadowMap.enabled = true
166
+ if (typeof sugar.shadow === 'object') {
167
+ normalizedRenderer.shadowMap.type = sugar.shadow.type
168
+ }
169
+ }
170
+ }
171
+
172
+ // START
173
+ // ====================
174
+ if (!app) {
175
+ throw new Error('error creating app')
176
+ }
177
+
178
+ // save renderer, scene, camera
179
+ app.config.globalProperties.lunchbox.camera = normalizedCamera
180
+ app.config.globalProperties.lunchbox.renderer = normalizedRenderer
181
+ app.config.globalProperties.lunchbox.scene = normalizedScene
182
+
183
+ for (let startCallback of startCallbacks ?? []) {
184
+ startCallback({
185
+ app,
186
+ camera: normalizedCamera,
187
+ renderer: normalizedRenderer,
188
+ scene: normalizedScene,
189
+ })
190
+ }
191
+
192
+ // KICK UPDATE
193
+ // ====================
194
+ update({
195
+ app,
196
+ camera: normalizedCamera,
197
+ renderer: normalizedRenderer,
198
+ scene: normalizedScene,
199
+ updateSource: props.updateSource,
200
+ })
201
+ })
202
+
203
+ // UNMOUNT
204
+ // ====================
205
+ onBeforeUnmount(() => {
206
+ cancelUpdate()
207
+ cancelUpdateSource()
208
+ })
209
+
210
+ // RENDER FUNCTION
211
+ // ====================
212
+ const containerFillStyle =
213
+ props.sizePolicy === 'container' ? 'static' : 'absolute'
214
+ const canvasFillStyle =
215
+ props.sizePolicy === 'container' ? 'static' : 'fixed'
216
+
217
+ return () => (
218
+ <>
219
+ {/* use renderer slot if provided... */}
220
+ {context.slots?.renderer?.()?.length ? (
221
+ // TODO: remove `as any` cast
222
+ (renderer.value = context.slots?.renderer?.()[0] as any)
223
+ ) : (
224
+ // ...otherwise, add canvas...
225
+ <>
226
+ <div
227
+ class="lunchbox-container"
228
+ style={fillStyle(containerFillStyle) as any}
229
+ ref={container}
230
+ data-lunchbox="true"
231
+ >
232
+ <canvas
233
+ ref={canvas}
234
+ class="lunchbox-canvas"
235
+ style={fillStyle(canvasFillStyle) as any}
236
+ data-lunchbox="true"
237
+ ></canvas>
238
+ </div>
239
+ {/* ...and create default renderer once canvas is present */}
240
+ {canvas.value?.domElement && (
241
+ <webGLRenderer
242
+ {...(props.rendererProperties ?? {})}
243
+ ref={renderer}
244
+ args={[
245
+ {
246
+ alpha: props.transparent,
247
+ antialias: true,
248
+ canvas: canvas.value?.domElement,
249
+ powerPreference: !!props.r3f
250
+ ? 'high-performance'
251
+ : 'default',
252
+ ...(props.rendererArguments ?? {}),
253
+ },
254
+ ]}
255
+ />
256
+ )}
257
+ </>
258
+ )}
259
+
260
+ {/* use scene slot if provided... */}
261
+ {context.slots?.scene?.()?.length ? (
262
+ // TODO: remove `as any` cast
263
+ (scene.value = context.slots?.scene?.()[0] as any)
264
+ ) : (
265
+ // ...otherwise, add default scene
266
+ // TODO: why does this need to be a separate component? <scene> throws an error
267
+ <LunchboxScene ref={scene}>
268
+ {context.slots?.default?.()}
269
+ </LunchboxScene>
270
+ )}
271
+
272
+ {/* use camera slot if provided... */}
273
+ {context.slots?.camera?.()?.length ? (
274
+ // TODO: remove `any` cast
275
+ (camera.value = context.slots?.camera?.()[0] as any)
276
+ ) : props.ortho || props.orthographic ? (
277
+ <orthographicCamera
278
+ ref={camera}
279
+ args={props.cameraArgs ?? []}
280
+ {...consolidatedCameraProperties}
281
+ />
282
+ ) : (
283
+ <perspectiveCamera
284
+ ref={camera}
285
+ args={
286
+ props.cameraArgs ?? [
287
+ props.r3f ? 75 : 45,
288
+ 0.5625,
289
+ 1,
290
+ 1000,
291
+ ]
292
+ }
293
+ {...consolidatedCameraProperties}
294
+ />
295
+ )}
296
+ </>
297
+ )
298
+ },
299
+ })
@@ -1,22 +1,43 @@
1
1
  import { MiniDom } from '../../core'
2
+ import { Lunch } from '../../types'
2
3
  import { Ref } from 'vue'
3
4
  import { resizeCanvas } from './resizeCanvas'
4
5
 
6
+ const getInnerDimensions = (node: Element) => {
7
+ const computedStyle = getComputedStyle(node)
8
+ const width =
9
+ node.clientWidth -
10
+ parseFloat(computedStyle.paddingLeft) -
11
+ parseFloat(computedStyle.paddingRight)
12
+ const height =
13
+ node.clientHeight -
14
+ parseFloat(computedStyle.paddingTop) -
15
+ parseFloat(computedStyle.paddingBottom)
16
+ return { width, height }
17
+ }
18
+
5
19
  export const prepCanvas = (
6
20
  container: Ref<MiniDom.RendererDomNode | undefined>,
7
- canvasElement: HTMLCanvasElement,
8
- onBeforeUnmount: Function
21
+ camera: THREE.Camera,
22
+ renderer: THREE.Renderer,
23
+ scene: THREE.Scene,
24
+ sizePolicy?: Lunch.SizePolicy
9
25
  ) => {
10
26
  const containerElement = container.value?.domElement
11
27
  if (!containerElement) throw new Error('missing container')
12
28
 
13
- // save...
14
- // ...and size element
15
- resizeCanvas()
29
+ // save and size element
30
+ const resizeCanvasByPolicy = () => {
31
+ if (sizePolicy === 'container') {
32
+ const dims = getInnerDimensions(containerElement)
33
+ resizeCanvas(camera, renderer, scene, dims.width, dims.height)
34
+ } else resizeCanvas(camera, renderer, scene)
35
+ }
36
+ resizeCanvasByPolicy()
16
37
 
17
38
  // attach listeners
18
- const observer = new ResizeObserver(([canvas]) => {
19
- resizeCanvas()
39
+ let observer = new ResizeObserver(() => {
40
+ resizeCanvasByPolicy()
20
41
  })
21
42
  // window.addEventListener('resize', resizeCanvas)
22
43
  if (containerElement) {
@@ -24,9 +45,11 @@ export const prepCanvas = (
24
45
  }
25
46
 
26
47
  // cleanup
27
- onBeforeUnmount(() => {
28
- if (canvasElement) {
29
- observer.unobserve(canvasElement)
30
- }
31
- })
48
+ return {
49
+ dispose() {
50
+ if (containerElement) {
51
+ observer.unobserve(containerElement)
52
+ }
53
+ },
54
+ }
32
55
  }
@@ -1,11 +1,12 @@
1
- import { ensuredCamera, ensureRenderer, ensuredScene } from '../../core'
2
1
  import { toRaw } from 'vue'
3
2
 
4
- export const resizeCanvas = (width?: number, height?: number) => {
5
- const renderer = ensureRenderer.value?.instance
6
- const scene = ensuredScene.value.instance
7
- const camera = ensuredCamera.value
8
-
3
+ export const resizeCanvas = (
4
+ camera: THREE.Camera,
5
+ renderer: THREE.Renderer,
6
+ scene: THREE.Scene,
7
+ width?: number,
8
+ height?: number
9
+ ) => {
9
10
  // ignore if no element
10
11
  if (!renderer?.domElement || !scene || !camera) return
11
12
 
@@ -15,12 +16,12 @@ export const resizeCanvas = (width?: number, height?: number) => {
15
16
  // update camera
16
17
  const aspect = width / height
17
18
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
18
- const perspectiveCamera = camera.instance as THREE.PerspectiveCamera
19
+ const perspectiveCamera = camera as THREE.PerspectiveCamera
19
20
  perspectiveCamera.aspect = aspect
20
21
  perspectiveCamera.updateProjectionMatrix()
21
22
  } else if (camera.type?.toLowerCase() === 'orthographiccamera') {
22
- // console.log('TODO: ortho camera update')
23
- const orthoCamera = camera.instance as THREE.OrthographicCamera
23
+ // TODO: ortho camera update
24
+ const orthoCamera = camera as THREE.OrthographicCamera
24
25
  const heightInTermsOfWidth = height / width
25
26
  orthoCamera.top = heightInTermsOfWidth * 10
26
27
  orthoCamera.bottom = -heightInTermsOfWidth * 10
@@ -28,13 +29,13 @@ export const resizeCanvas = (width?: number, height?: number) => {
28
29
  orthoCamera.left = -10
29
30
  orthoCamera.updateProjectionMatrix()
30
31
  } else {
31
- console.log('TODO: non-ortho or perspective camera')
32
+ // TODO: non-ortho or perspective camera
32
33
  }
33
34
 
34
35
  // update canvas
35
36
  renderer.setSize(width, height)
36
37
  // render immediately so there's no flicker
37
- if (scene && camera.instance) {
38
- renderer.render(toRaw(scene), toRaw(camera.instance))
38
+ if (scene && camera) {
39
+ renderer.render(toRaw(scene), toRaw(camera))
39
40
  }
40
41
  }
@@ -5,8 +5,6 @@ import { autoGeneratedComponents } from './autoGeneratedComponents'
5
5
  import { catalogue } from './catalogue'
6
6
  export { catalogue }
7
7
 
8
- export const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper']
9
-
10
8
  // component creation utility
11
9
  const createComponent = (tag: string) =>
12
10
  defineComponent({
@@ -1,7 +1,6 @@
1
1
  import { isLunchboxRootNode } from '../utils'
2
- import { instantiateThreeObject, MiniDom, ensuredScene } from '.'
2
+ import { instantiateThreeObject, MiniDom } from '.'
3
3
  import { Lunch } from '..'
4
- import { ensuredCamera } from './ensure'
5
4
 
6
5
  /** Create a new Lunchbox comment node. */
7
6
  export function createCommentNode(options: Partial<Lunch.CommentMeta> = {}) {
@@ -59,13 +58,6 @@ export function createNode<T extends object = THREE.Object3D>(
59
58
  })
60
59
 
61
60
  if (node.type && !isLunchboxRootNode(node) && !node.instance) {
62
- // if (node.type.includes('Camera')) {
63
- // console.log(node.type, {
64
- // ...node.props,
65
- // ...props,
66
- // })
67
- // console.trace()
68
- // }
69
61
  node.instance = instantiateThreeObject({
70
62
  ...node,
71
63
  props: {
@@ -75,13 +67,5 @@ export function createNode<T extends object = THREE.Object3D>(
75
67
  })
76
68
  }
77
69
 
78
- // TODO: these manual overrides are a bit brittle - replace?
79
- if (node.type?.toLowerCase() === 'scene') {
80
- // manually set scene override
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>
84
- }
85
-
86
70
  return node
87
71
  }